• What’s New in Swift 5.3?
  • Anupam Chugh
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: chaingangway
  • Proofread by: chaingangway, Zorro

Swift 5.3 new features, do you know?

Support for cross-platform, multiple trailing closures, multi-mode catch clauses, and more

The Swift 5.3 release process began in late March and only recently entered the final development phase. One of the main goals of this release is to extend the language to support Windows and Linux platforms.

Apple is also very focused on improving the overall performance of languages to improve machine learning performance in SwiftUI and iOS. Let’s take a closer look at some of the major updates in the upcoming release.

Multiple trailing closures

A new trailing closure syntax was introduced in the SE-0279 proposal, allowing you to call multiple closures as arguments to functions in a more understandable way. This is a more powerful syntactic sugar that minimizes the use of excessive parentheses in function signatures.

It allows you to append several labeled closures to the original unlabeled closure. The following example demonstrates this usage:

//old
UIView.animate(withDuration: 0.5, animations: {
  self.view.alpha = 0
}, completion: { _ in
  self.view.removeFromSuperview()
})

//new multiple trailing closures
UIView.animate(withDuration: 0.5) {
            self.view.alpha = 0
        } completion: { _ in
            self.view.removeFromSuperview()
        }
Copy the code

The syntax changes above make the SwiftUI view easier to write.

Multi-mode catch clause

Currently, each catch clause in a do-catch statement can contain only one pattern. To solve this problem, it is best for developers to use swtich Case statements in the body of catch statements, but this increases nested and repetitive code.

Se-0276 is another nice improvement that allows pattern matching for catch words. The Catch clause will allow the user to specify a comma-separated list of patterns and bind variables to the Catch body, just like a switch statement. Here’s an example:

enum NetworkError: Error {
    case failure, timeout
}

//old
func networkCall(a){
  do{
    try someNetworkCall()
  }catch NetworkError.timeout{
    print("timeout")}catch NetworkError.failure{
    print("failure")}}//new
func networkCall(a){
  do{
    try someNetworkCall()
  }catch NetworkError.failure, NetworkError.timeout{
    print("handle for both")}}Copy the code

The multi-mode catch clause makes your code clean and concise.

Implement the comparison function of enumerated types

So far, comparing two enumerated types has not been an easy task. The Comparable protocol must be complied with first, and then the static Fun \< method is implemented to determine whether the original value of one enumeration type is less than the original value of another. (And vice versa for the > case).

Thankfully, in SE-0266, we can make enumeration types comply with the Comparable protocol without explicitly implementing it, just by ensuring that the enumeration is a standard type. If an enumeration type does not set an associated value, Enums compares it based on the semantic order of the declaration.

Here is an example of enumeration type sort:

enum Brightness: Comparable {
    case low(Int)
    case medium
    case high
}

([.high, .low(1), .medium, .low(0)] as [Brightness]).sorted()
// [Brightness.low(0), Brightness.low(1), Brightness.medium, Brightness.high]
Copy the code

Enumeration case matches protocol

The protocol matching model in Swift is very strict, stating that enumeration cases with the same name and parameters as the protocol do not match. We can only implement this manually, as follows:

protocol DecodingError {
  static var fileCorrupted: Self { get }
  static func keyNotFound(_ key: String) -> Self
}

enum JSONDecodingError: DecodingError {
  case _fileCorrupted
  case _keyNotFound(_ key: String)
  static var fileCorrupted: Self { return ._fileCorrupted }
  static func keyNotFound(_ key: String) -> Self { return ._keyNotFound(key) }
}
Copy the code

This restriction was removed in SE-0280 so that enumeration cases can be directly matched to the protocol while the names in the protocol are the same as parameters and enumeration cases.

protocol DecodingError {
  static var fileCorrupted: Self { get }
  static func keyNotFound(_ key: String) -> Self
}
enum JSONDecodingError: DecodingError {
  case fileCorrupted
  case keyNotFound(_ key: String)}Copy the code

Self does not need to be used explicitly everywhere

Proposal SE-0269 allows us to omit self when we don’t need to. Previously, when we captured values from outside the closure, we had to use self in the closure. Self will now exist implicitly when a circular reference is unlikely to occur.

The following example shows this update:

struct OldView: View {

    var body: some View {
        Button(action: {
            self.sayHello()
        }) {
            Text("Press")}}func sayHello(a){}}struct NewView: View {

    var body: some View {
        Button {
            sayHello()
        } label: {
            Text("Press")}}func sayHello(a){}}Copy the code

Developers using SwiftUI will be happy to accept this. Because the view is stored in the structure of the value type, no circular reference occurs.

Type-based program entry

Se-0281 allows us to use the new @main property, which defines the entry to our app. Marking a structure or class with an attribute ensures that it is where the program goes, so you don’t have to manually call Appdelegate.main ().

@main
class AppDelegate: UIResponder.UIApplicationDelegate {
static func main(a) {
        print("App will launch & exit right away.")}}Copy the code

We can assume that the domain-specific attributes @UIApplicationMain and @NSApplicationMain from older versions will be deprecated in future releases, and @main will be recommended instead.

Where clause changes in the context of a generic declaration

So far, where clauses cannot be declared inline within a generic context. For example, if you add a WHERE restriction to a method, the compiler will throw an error. To solve this problem, we must create a separate extension to handle the specific WHERE clause.

In SE-0267, we can implement methods with WHERE clauses as long as we reference generic parameters. Here is a short code snippet:

struct Base<T> {
    var value : T 
}

extension Base{

  func checkEquals(newValue: T) where T : Equatable{... }func doCompare(newValue: T) where T : Comparable{...}

}
Copy the code

By allowing where clauses on member declarations, we can easily create shorter, more concise generic interfaces without creating separate extensions.

A new operation on a noncontinuous element of the collection

In the current version, accessing a contiguous range of elements in a collection is simple. For arrays, you simply access them with [startIndex… endIndex].

A new type called RangeSet was introduced in SE-0270, which gets a collection of discontinuous indexes,

var numbers = Array(1.15)

// Find the indices of all the even numbers
let indicesOfEvens = numbers.subranges(where: {$0.isMultiple(of: 2)})// Find the indices of all multiples of 3
let multiplesofThree = numbers.subranges(where: {$0.isMultiple(of: 3)})let combinedIndexes = multiplesofThree.intersection(indicesOfEvens)
let sum = numbers[combinedIndexes].reduce(0, +)
//Sum of 6 + 12 = 18
Copy the code

With RangeSet, we can do a lot of calculations and operations on collections. For example, by using the moveSubranges function, we can move a series of indexes through an array. So we can easily move all the even numbers in the array to the beginning of the array:

let rangeOfEvens = numbers.moveSubranges(indicesOfEvens, to: numbers.startIndex)
// numbers == [2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15]
Copy the code

Perfect didSet semantics

Previously, the getter method of the didSet attribute observer was always called to retrieve oldValue regardless of whether the attribute reference had a new assignment.

This mechanism may have little effect on variables, but for large arrays, storage space is allocated and unused values are loaded, a process that can affect program performance.

In SE-0268, oldValue is loaded only when needed, which will improve the efficiency of the didSet attribute observer. Also, if only didSet was implemented and no willSet was implemented, the update would work as usual.

Here is an example of an improved didSet:

class A {
    var firstArray = [1.2.3] {
        didSet { print("didSet called")}}var secondArray = [1.2.3.4] {
        didSet { print(oldValue) }
    }
}

let a = A(a)// This will not call the getter to fetch the oldValue of firstArray
a.firstArray = [1.2]
// This will call the getter to fetch the oldValue of secondArray
a.secondArray = [1]
Copy the code

New type Float16

The SE-0277 proposes Float16, a semi-precision floating-point type. With the emergence of machine learning on mobile devices in recent years, Apple has shown its ambition in this area. Float16 is typically used for GPU calculations on mobile devices and as a compression format for weights in ML programs.

letF16: Float16 = 7.29Copy the code

conclusion

We have summarized the important new features of the Swift 5.3 language, and the Swift package Manager has made many improvements. Let’s take a quick look at them:

  • Se-0271 allows you to add resources (images, data files, and so on) to a Swift package.
  • SE-0278 can add localized resources.
  • Se-0272 allows you to integrate closed source dependencies (such as Firebase) in binary format in the package manager.
  • Se-0273 enables you to conditionally specify dependency managers for different target platforms.

You can download the Linux distribution of Swift from this blog. Or you can go directly to the Swift website for a quick snapshot of Swift 5.3.

End of this article. Thanks for reading.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.