📜 ⬆️ ⬇️

Swift 5.1 - what's new?



Swift 5.0 became available with the release of Xcode 10.2, but work on the next version continues and there is already news about what can be expected in it.

The key feature of Swift 5.1 is module stability , which allows us to use third-party libraries without worrying about which version of Swift compiler they were created with. It looks like ABI stability , which we received in Swift 5.0, but there is a slight difference: ABI stability resolves differences in Swift versions at the execution stage, and module stability at the compilation stage.
')
In addition to this important innovation, we will get several important improvements in Swift, and in this article we will go over them with examples so that we can see them in action.

Universal Self


SE-0068 extends the use of Self , so that it refers to the type containing it within classes, structures, and enumerations. This is usually useful for dynamic types when it is necessary to determine the exact type of something at run time.

As an example, consider the following code:

class NetworkManager { class var maximumActiveRequests: Int { return 4 } func printDebugData() { print("Maximum network requests: \(NetworkManager.maximumActiveRequests).") } } 

Here we define the static property maximumActiveRequests inside the NetworkManager class and add the printDebugData () method to print this property. Everything is fine here, but only until we decide to inherit from NetworkManager :

 class ThrottledNetworkManager: NetworkManager { override class var maximumActiveRequests: Int { return 1 } } 

In this successor, we change the maximumActiveRequests property, so now it becomes equal to one, but if we call printDebugData () , it will output the value from the parent class:

 let manager = ThrottledNetworkManager() manager.printDebugData() 

Here we have to get 1 instead of 4, and here comes SE-0068 to the rescue: we can use Self (with a capital 'S') to refer to the current type. So now we can rewrite the printDebugData () method of the parent class like this:

 class ImprovedNetworkManager { class var maximumActiveRequests: Int { return 4 } func printDebugData() { print("Maximum network requests: \(Self.maximumActiveRequests).") } } 

That is, Self works in the same way as it worked on protocols in earlier versions of Swift.

Warnings in case of ambiguity of the variant none


Optionals in Swift are implemented as an enumeration with two options: some and none . This can be confusing if we create our own enumeration, which has the option none , and wrap it in optional . For example:

 enum BorderStyle { case none case solid(thickness: Int) } 

When using non-optional, everything is clean:

 let border1: BorderStyle = .none print(border1) 

This will output “none”. But if we use optional for this enumeration, then we will run into the problem:

 let border2: BorderStyle? = .none print(border2) 

Nil will be printed here, since Swift assumes that .none means that optional is empty , although in fact it is optional with the value BorderStyle.none.
In Swift 5.1, in the event of such ambiguity, a warning will be displayed:
“Assuming you mean 'Optional.none'; Did you mean 'BorderStyle.none' instead? ”
Thus, the developer will be informed that his code may not all go smoothly.

Matching optional and non-optional enums


Swift is smart enough to understand the switch / case construct with optional / non-optional text and integer combinations, but not in the case of enumerations.

Now in Swift 5.1, we can use switch / case to search for matching optional-and non-optional enums:

 enum BuildStatus { case starting case inProgress case complete } let status: BuildStatus? = .inProgress switch status { case .inProgress: print("Build is starting…") case .complete: print("Build is complete!") default: print("Some other build status") } 

Swift is able to match an optional listing with non-optional variants, and “Build is starting…” will be displayed here.

Comparing ordered collections


SE-0240 provided the ability to calculate differences between ordered collections, as well as apply the resulting comparison result to collections. This may be of interest to developers who have complex collections in the tableview, and they need to add or remove many elements using animation.

The basic principle is simple - Swift 5.1 provides a new difference method (from :) , which determines the differences between two ordered collections — which elements to add and which to remove. This applies to any ordered collections that contain elements that comply with the Equatable protocol.

To demonstrate this, we will create two arrays of values, calculate the differences of one from the other, and then go through the list of differences and apply them to make the two collections the same.

Note: since Swift is now distributed as part of the Apple operating system, new language tools should be used with the #available check to ensure that the code runs on an OS that supports the required functionality. For functionality launched on unknown, unannounced OSes that may be released in the future, the special version number “9999” is used, which means: “We still do not know the correct version number”.

 var scores1 = [100, 91, 95, 98, 100] let scores2 = [100, 98, 95, 91, 100] if #available(iOS 9999, *) { let diff = scores2.difference(from: scores1) for change in diff { switch change { case .remove(let offset, _, _): scores1.remove(at: offset) case .insert(let offset, let element, _): scores1.insert(element, at: offset) } } print(scores1) } 

For more advanced animation, we can use the third parameter in the resulting list of differences: associatedWith . Thus, instead of .insert (let offset, let element, _) we can write .insert (let offset, let element, let associatedwith ). This allows us to simultaneously track pairs of changes: moving an item in a collection two positions down is deleting an item and then adding it, and the associatedWith “binds” these two changes together and allows you to consider it a move.

Instead of applying the differences manually, one by one, you can apply them in one fell swoop using the new method of applying () :

 if #available(iOS 9999, *) { let diff = scores2.difference(from: scores1) let result = scores1.applying(diff) ?? [] } 

Creating uninitialized arrays


SE-0245 introduced a new initializer for arrays, which does not fill it with default values. It was available before as a private API, which meant that Xcode did not suggest this in code completion, but you could use it if you needed it and you understood the risk that this functionality might not be thereafter.

To use an initializer, set the size of the array, then pass a closure that fills the array with values. A closure will receive an unsafe pointer to the mutable buffer, as well as a second parameter in which you designate how many values ​​you actually use.

For example, we can create an array of 10 random integers like this:

 let randomNumbers = Array<Int>(_unsafeUninitializedCapacity: 10) { buffer, initializedCount in for x in 0..<10 { buffer[x] = Int.random(in: 0...10) } initializedCount = 10 } 

There are a few rules here:

  1. you do not need to use the entire volume you requested, but you cannot exceed it. That is, if you specify the size of the array 10, then you can set the initializedCount in the range from 0 to 10, but not 11.
  2. if you did not initialize the used elements in the array, for example, you set initializedCount to 5, but did not provide real values ​​to elements 0 through 4, then they will most likely get random values. As you can imagine, this is a bad option.
  3. If you do not set initializedCount , then it will be equal to 0 and all data that you have assigned will be lost.

Yes, we could easily rewrite the code using map () :

 let randomNumbers2 = (0...9).map { _ in Int.random(in: 0...10) } 

This is obviously more readable, but not so efficient: we create a range, then a new empty array, assign it a size, and “run over” the entire range, applying a closure to each element.

Conclusion


Swift 5.1 is still under development, and although the final branch for Swift itself has passed, changes from some other related projects are still visible.

So, the most important change is module stability , and it is known that the development team is working hard on this. They do not name the exact release date, although they said that Swift 5.1 has a significantly shorter development period compared to Swift 5.0, which required an extraordinary concentration of forces and attention. You can assume the output to WWDC19, but it is obvious that this is not the case when you need to hurry to a certain date.

Another note that deserves attention. Two changes in this list (“Warnings in the case of the ambiguity of the variant none” and “Matching optional and non-optional enumerations”) were not the result of the Swift evolution, but were recognized as bugs and corrected.

Source: https://habr.com/ru/post/446522/


All Articles