📜 ⬆️ ⬇️

What is waiting for us in Swift 4?

Table of contents


  1. One-way ranges
  2. Strings
  3. Private ads are visible in extensions of the same file.
  4. Smart keys of the way
  5. Archiving and serialization
  6. Dictionary and Set Improvements
  7. MutableCollection.swapAt method
  8. reduce with inout support
  9. Generics in subscripts
  10. Bridge for NSNumber
  11. Instances of classes and subtypes

How to start it all at home?


  1. Download the latest Swift 4 snapshot from the site.
  2. Run installer
  3. Go to Xcode> Toolchains> Manage Toolchains and select snepshot


One-way ranges


SE-0172 adds a new RangeExpression protocol and a set of prefix / postfix operators to define one-way ranges, in which either the lower or upper limit is not defined.


Infinite sequences


You can use a one-way sequence to create an infinite sequence. This is a more flexible replacement for enumerated() , if you do not want the numbering to start from scratch:


 let letters = ["a","b","c","d"] let numberedLetters = zip(1..., letters) Array(numberedLetters) 

Subscripts in collections


When a one-way sequence is used in a subscript of a collection, then startIndex or endIndex independently “fill” the missing upper or lower limit in the collection, respectively.


 let numbers = [1,2,3,4,5,6,7,8,9,10] numbers[5...] //  numbers[5..<numbers.endIndex] 

Pattern Comparison


Pattern comparisons are when a one-way sequence is used in the pattern comparison construct, for example, in case or switch . Note that the compiler cannot yet determine that the switch is superfluous here .


 let value = 5 switch value { case 1...: print("greater than zero") case 0: print("zero") case ..<0: print("less than zero") default: fatalError("unreachable") } 

Strings


Multiline String Literals


SE-0168 introduces simple syntax for multi-line string literals ( """ ). In multi-line literals, you do not need to escape single quotes, which means that formats like JSON and HTML can be inserted into them without any escaping. The closing literal defines How many spaces will be removed from the beginning of each line.


 let multilineString = """ This is a multi-line string. You don't have to escape "quotes" in here. The position of the closing delimiter controls whitespace stripping. """ print(multilineString) 

To see the output of print , you can display the console by clicking ( View> Debug Area> Activate Console ).


Row now again collection


SE-0163 is the first part of the revised string model for Swift 4. The biggest change is that now the string is a collection (as it was in Swift 1.x), that is, the String.CharacterView functionality String.CharacterView been minimized to its parent type. (Other views, UnicodeScalarView , UTF8View , and UTF16View , are still present.)


Please note that SE-0163 is not fully implemented yet, and there will be more stringent changes in the future.


 let greeting = "Hello, !" //      .characters greeting.count for char in greeting { print(char) } 

Substring - a new type for slice strings


String slice instances are now a Substring type. Both String and Substring implement the StringProtocol protocol. Almost all APIs for strings live in the StringProtocol , so the String and StringProtocol mostly behave the same.


 let comma = greeting.index(of: ",")! let substring = greeting[..<comma] type(of: substring) // API  String    Substring print(substring.uppercased()) 

Unicode 9


Swift 4 will support Unicode 9, fixed problems with proper clustering of graphs for modern emoji. Everything below is now a single character:


 "".count // person + skin tone "‍‍‍".count // family with four members "\u{200D}\u{200D}\u{200D}".count // family + skin tones "‍".count // person + skin tone + profession 

Habraparser ate all Emoji, with them look here


Property Character.unicodeScalars


You can now access Character points directly without turning them into a string ( SE-0178 ).


 let c: Character = "" Array(c.unicodeScalars) 

Private ads are visible in extensions of the same file.


SE-0169 modifies the access control rules so that private announcements are now visible in extensions of the parent type in the same file. This allows you to split your type definition into several extensions and still use private access for most “private” things, reducing the need to use the fileprivate access fileprivate .


 struct SortedArray<Element: Comparable> { private var storage: [Element] = [] init(unsorted: [Element]) { storage = unsorted.sorted() } } extension SortedArray { mutating func insert(_ element: Element) { // storage   storage.append(element) storage.sort() } } let array = SortedArray(unsorted: [3,1,2]) // storage __   (   fileprivate) //array.storage // error: 'storage' is inaccessible due to 'private' protection level 

Smart keys of the way


Probably one of the main features of Swift 4 is the new key path model described in SE-0161 . Unlike the string keys of the path in Cocoa, in Swift the keys of the path are strongly typed.


 struct Person { var name: String } struct Book { var title: String var authors: [Person] var primaryAuthor: Person { return authors.first! } } let abelson = Person(name: "Harold Abelson") let sussman = Person(name: "Gerald Jay Sussman") let sicp = Book(title: "Structure and Interpretation of Computer Programs", authors: [abelson, sussman]) 

Path keys can be specified starting from the root type and down to any combination of properties and names.


Writing the path key begins with backslash: \Book.title . Any type in Swift accepts [keyPath: …] - subscript to get or set the value for the desired path key.


 sicp[keyPath: \Book.title] //        sicp[keyPath: \Book.primaryAuthor.name] 

Path keys are a KeyPath object that can be stored and manipulated. For example, you can add additional segments to the key path to go further.


 let authorKeyPath = \Book.primaryAuthor type(of: authorKeyPath) let nameKeyPath = authorKeyPath.appending(path: \.name) //          sicp[keyPath: nameKeyPath] 

Subscripts in path keys


Subscript notation can also be used in path keys. Quite a convenient way to work with collections, arrays or dictionaries. This functionality is not yet implemented in the current snapshot.


 //sicp[keyPath: \Book.authors[0].name] // INTERNAL ERROR: feature not implemented: non-property key path component 

Archiving and serialization


SE-0166: Swift Archival & Serialization defines how types in Swift (classes, structures, and enums) will serialize and archive themselves. Types can make themselves (expanded) archived by implementing the Codable protocol.


In most cases, implementing the Codable protocol is all that is required. The compiler can generate the rest of the implementation itself only if all members of the type implement Codable . You can also override the standard behavior, if you need to change how it is, the type serializes itself. There are many nuances in this topic - be sure to read the proposal for further details.


 //     (   )   Codable struct Card: Codable { enum Suit: String, Codable { case clubs, spades, hearts, diamonds } enum Rank: Int, Codable { case ace = 1, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king } var suit: Suit var rank: Rank } let hand = [Card(suit: .clubs, rank: .ace), Card(suit: .hearts, rank: .queen)] 

Coding


When you have a value that implements Codable , you need to pass it on to the encoder in order to archive it.


You can write your own encoders and decoders that use the infrastructure from Codable , but Swift will be supplied built-in for JSON ( JSONEncoder and JSONDecoder ) and for the property list ( PropertyListEncoder and PropertyListDecoder ). They are defined in SE-0167 . NSKeyedArchiver will also support all Codable types.


 import Foundation var encoder = JSONEncoder() //   JSONEncoder    encoder.dataEncodingStrategy encoder.dateEncodingStrategy encoder.nonConformingFloatEncodingStrategy encoder.outputFormatting encoder.userInfo let jsonData = try encoder.encode(hand) String(data: jsonData, encoding: .utf8) 

Decoding


 let decoder = JSONDecoder() let decoded = try decoder.decode([Card].self, from: jsonData) 

Dictionary and Set Improvements


SE-0165 adds several improvements for Dictionary and Set .


Sequence initializer


Creating a dictionary from a sequence of key-value pairs.


 let names = ["Cagney", "Lacey", "Bensen"] let dict = Dictionary(uniqueKeysWithValues: zip(1..., names)) dict[2] 

Merge initializer and merge method


Now you can determine how duplicate keys will be processed when a dictionary is created from a sequence or a sequence is merged into the current dictionary.


 let duplicates = [("a", 1), ("b", 2), ("a", 3), ("b", 4)] let letters = Dictionary(duplicates, uniquingKeysWith: { (first, _) in first }) letters let defaults = ["foo": false, "bar": false, "baz": false] var options = ["foo": true, "bar": false] //      : error: generic parameter 'S' could not be inferred //       https://bugs.swift.org/browse/SR-922 //options.merge(defaults) { (old, _) in old } 

Subscript with default


You can define a default value for non-existing keys as a subscript argument, making the return type not optional.


 dict[4, default: "(unknown)"] //       

This is especially important when you need to mutate a value via subscript:


 let source = "how now brown cow" var frequencies: [Character: Int] = [:] for c in source { frequencies[c, default: 0] += 1 } frequencies 

Dictionary-specific map and filter


filter returns a Dictionary and not an Array . Similarly, the new mapValues method transforms values mapValues preserving its structure.


 let filtered = dict.filter { $0.key % 2 == 0 } type(of: filtered) let mapped = dict.mapValues { value in value.uppercased() } mapped 

Set.filter also returns a Set and not an Array .


 let set: Set = [1,2,3,4,5] let filteredSet = set.filter { $0 % 2 == 0 } type(of: filteredSet) 

Sequence grouping


Grouping a sequence of values ​​into bouquets. break the words in the list by their first letter.


 let contacts = ["Julia", "Susan", "John", "Alice", "Alex"] let grouped = Dictionary(grouping: contacts, by: { $0.first! }) grouped 

MutableCollection.swapAt method


SE-0173 introduces a new method for exchanging two items in a collection. Unlike the existing swap(_:_:) , the swapAt(_:_:) method swapAt(_:_:) accepts indices of the elements that need to be exchanged, and not the elements themselves (through inout arguments).


The reason for adding this method is that exchanging with two inout arguments is incompatible.
with the new rules of access to the memory SE-0176 . The existing swap(_:_:) function swap(_:_:) will no longer work for the exchange of two elements of the same collection.


 var numbers = [1,2,3,4,5] numbers.swapAt(0,1) // Will be illegal in Swift 4 (not implemented yet) swap(&numbers[3], &numbers[4]) numbers 

reduce with inout support


SE-0171 adds a variant of the reduce method in which the result is passed as inout to the combine function. This can be a significant acceleration for algorithms that use reduce to incrementally build sequences, by eliminating copying and intermediate results.


SE-0171 not yet implemented


 //     extension Sequence where Iterator.Element: Equatable { func uniq() -> [Iterator.Element] { return reduce(into: []) { (result: inout [Iterator.Element], element) in if result.last != element { result.append(element) } } } } [1,1,1,2,3,3,4].uniq() 

Generics in subscripts


As represented in SE-0148 , subscript can now accept and return arguments in the form of generics.


A canonical example is the type that represents JSON data: you can define a subscript with generic so that the context of the calling code can determine the expected return type.


 struct JSON { fileprivate var storage: [String:Any] init(dictionary: [String:Any]) { self.storage = dictionary } subscript<T>(key: String) -> T? { return storage[key] as? T } } let json = JSON(dictionary: [ "name": "Berlin", "country": "de", "population": 3_500_500 ]) //     as? Int let population: Int? = json["population"] 

Another example is a subscript in a Collection that takes a sequence of indices and returns an array of values ​​of these indices.


 extension Collection { subscript<Indices: Sequence>(indices indices: Indices) -> [Iterator.Element] where Indices.Iterator.Element == Index { var result: [Element] = [] for index in indices { result.append(self[index]) } return result } } let words = "Lorem ipsum dolor sit amet".split(separator: " ") words[indices: [1,2]] 

Bridge for NSNumber


SE-0170 corrects some dangerous behavior with a bridge between the numeric type in Swift and NSNumber .


 import Foundation let n = NSNumber(value: UInt32(543)) let v = n as? Int8 // nil in Swift 4. This would be 31 in Swift 3 (try it!). 

Instances of classes and subtypes


Now you can write the equivalent of Objective-C UIViewController <SomeProtocol> * code in Swift,
for example, declare a variable with a specific type and link it to one or several protocols simultaneously ( SE-0156 ). let variable: SomeClass & SomeProtocol1 & SomeProtocol2


 import Cocoa protocol HeaderView {} class ViewController: NSViewController { let header: NSView & HeaderView init(header: NSView & HeaderView) { self.header = header super.init(nibName: nil, bundle: nil)! } required init(coder decoder: NSCoder) { fatalError("not implemented") } } //    NSView     // ViewController(header: NSView()) // error: argument type 'NSView' does not conform to expected type 'NSView & HeaderView' //    NSView ()      extension NSImageView: HeaderView {} ViewController(header: NSImageView()) //  

')

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


All Articles