📜 ⬆️ ⬇️

What's new in Swift 5?

Hi, my name is Ilya. I am an iOS developer at Tinkoff.ru. In this article I will give a brief overview of the major changes in Swift 5. These changes are described in release notes . For those who have not yet reviewed, welcome under the cat!



The size of the application will decrease!


Applications written in Swift 5 and compiled for iOS 12.2, watchOS 5.2, tvOS 12.2 will not include dynamic libraries for the Swift standard library and Swift SDK. This means that the size of the application will decrease, though not by much. If you believe this tweet , the size of the empty project was reduced from 2.4 MB to 24 KB. Not a bad result for small applications, but for big ones there won't be much difference.

@dynamicCallable ( SE-0216 )


The @dynamicCallable attribute allows you to work with an object as a function. Such objects are called functional objects or functors (you can read more here ). Functional objects are in C ++, Python, JavaScript and in other languages, and in Swift they were added for compatibility with these languages. The fact is that Swift now interacts well with API C and Objective-C and language developers want to add interaction with dynamic languages ​​- Python, JavaScript, Ruby and others.

In order to make a type a functor, you need to add the @dynamicCallable attribute to its declaration. Consider an example of a Reducer structure, with which you can add numbers in an array:

@dynamicCallable struct Reducer { ... } 

After which you need to implement one or both of the following methods:

 func dynamicallyCall(withArguments: ExpressibleByArrayLiteral) func dynamicallyCall(withKeywordArguments: ExpressibleByDictionaryLiteral) 

The first function allows you to access an object by passing an array as arguments. The second function allows you to access the object by passing the same array as arguments, but using the names of the arguments.

For example, the implementation of the first function for the Reducer structure would look like this:

 func dynamicallyCall(withArguments arguments: [Int]) -> Int { return arguments.reduce(0, +) } 

Then apply this structure as follows:

 let reducer = Reducer() let sum = reducer(1, 2, 3) // sum = 6 

Consider the implementation of the second method on the example of the Comparator structure, with which you can compare two numbers:

 @dynamicCallable struct Comparator { func dynamicallyCall(withKeywordArguments arguments: KeValuePairs<String, Int>) -> ComparisonResult { guard let lhs = arguments["lhs"], let rhs = arguments["rhs"], lhs != rhs else { return .orderedSame } return lhs > rhs ? .orderedDescending : .orderedAscending } } 

You can use this structure as follows:

 let comparator = Comparator() let comparisionResult = comparator(lhs: 1, rhs: 2) // comparisionResult = .orderedAscending 

Attribute unknown in switch ( SE-0192 )


Many people know that when processing enumeration values, you need to describe all cases and try not to use default. This requirement, although it adds a pinch of security, also has a drawback, since if you change the values ​​in the enumeration, you need to add their processing. There is still a chance that the system framework will change some of the enumerations, and this is not processed in your application (So it was, for example, with LABiometryType ).

The attribute unknown is added to Swift 5, which will allow to separate 2 different scenarios when processing an enumeration:


Let's look at an example:

 enum HTTPMethod { case post, get, put } // Без @unknown switch httpMethod { case .post: print("Post") case .get: print("Get") default: print("Put") } // С @unknown switch httpMethod { case .post: print("Post") case .get: print("Get") @unknown default: print("Unknown HTTP method") } 

Getting rid of double Optional by calling a function with try? ( SE-0230 )


Surely, many were faced with the fact that when calling a throwable function that returns an Optional using try? , the result was a type wrapped in two Optionals . This is not very convenient, and so in Swift 5, try to call ? in this case, returns a type wrapped in only one Optional .

That's how it was before Swift 5:

 let result = try? optionalObject?.foo() // type(of: result) = SomeType?? 

This will be the case in Swift 5:

 let result = try? optionalObject?.foo() // type(of: result) = SomeType? 

Frequency check ( SE-0225 )


To check the multiplicity of one number to another, you can use the function isMultiple (of :) , instead of the remainder of the division (%):

 // Старый вариант let isEven = 4 % 2 == 0 // Новый вариант let isEvent = 4.isMultiple(of: 2) 

The change is minor, but it makes the code a bit clearer and makes it easier to search by code.

Counting the number of elements in the sequence with the condition ( SE-0220 )


In Swift 5, the Sequence type will add a count method (where: (Element) -> Bool) -> Int , which will allow counting the number of elements in a sequence that satisfy a given condition in one pass. Before that, you had to use filter in conjunction with count . This method will save the memory allocated when creating a new array in the filter method.

Example:

 let countOfZeroes = [0, 1, 2, 0, 4].count(where: { $0 == 0 }) // countOfZeroes = 2 

CompactMapValues ​​method in Dictionary ( SE-0218 )


This method combines compactMap from Array and mapValues from Dictionary . As a result of calling this method, a dictionary with transformed values ​​is created, in which there are no values ​​equal to nil .

Example:

 let dictionary = ["a": "1", "b": "2", "c": "Number"] let resultDictionary = dictionary.compactMapValues { Int($0) } // resultDictionary = ["a": 1, "b": 2] 

Raw strings ( SE-0200 )


Added the ability to write strings in which quotes and backslashes are used as ordinary characters and not as special characters. To do this, you must add the # symbol at the beginning and at the end of the line.

Example:

 let string1 = #"Строка со словом "в кавычках""# let string2 = #"Строка с \обратным слэшем"# 

If during the creation of a string, the insertion of a variable is used, then after the backslash you need to add a # sign:

 let string = #"Строка с переменной \#(variable)"# 

If the line contains the # sign, then at the beginning and at the end of the line you need to add two ## signs:

 let string = ##"Строка со знаком #"## 

Sequence protocol no longer contains associated type SubSequence ( SE-0234 )


The associative SubSequence type was moved from the Sequence protocol to the Collection. Now , all the methods in the Sequence that returned the SubSequence return a specific type. For example, the suffix method now returns an Array . Here is a complete list of the methods affected by this change:

 extension Sequence { public func dropFirst(_ k: Int = 1) -> DropFirstSequence<Self> public func dropLast(_ k: Int = 1) -> [Element] public func suffix(_ maxLength: Int) -> [Element] public func prefix(_ maxLength: Int) -> PrefixSequence<Self> public func drop(while predicate: (Element) throws -> Bool) rethrows -> DropWhileSequence<Self> public func prefix(while predicate: (Element) throws -> Bool) rethrows -> [Element] public func split( maxSplits: Int = Int.max, omittingEmptySubsequences: Bool = true, whereSeparator isSeparator: (Element) throws -> Bool ) rethrows -> [ArraySlice<Element>] } 

Now it will be easier to work with these methods.

Protocol Restrictions


Protocols now support constraints as classes that implement this protocol. In other words, you can now specify that the protocol can only be implemented by a specific class. For example:

 protocol Viewable: UIView {} protocol Viewable where Self: UIView {} 

The second record option is supported in Swift 4.2, but may cause a compilation error or in runtime. In Swift 5, this error will not occur.

Conclusion


This is not the entire list of changes in Swift 5, only major changes are collected here. In general, the changes presented are positive and make the language more understandable and flexible. The main thing is that "Convert to current Swift syntax" should be painless.

That's all, thanks for reading.

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