Table of contents
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.
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)
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 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") }
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 ).
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) }
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())
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
Character.unicodeScalars
You can now access Character
points directly without turning them into a string ( SE-0178 ).
let c: Character = "" Array(c.unicodeScalars)
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
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]
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
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)]
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)
let decoder = JSONDecoder() let decoded = try decoder.decode([Card].self, from: jsonData)
Dictionary
and Set
ImprovementsSE-0165 adds several improvements for Dictionary
and Set
.
Creating a dictionary from a sequence of key-value pairs.
let names = ["Cagney", "Lacey", "Bensen"] let dict = Dictionary(uniqueKeysWithValues: zip(1..., names)) dict[2]
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 }
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
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)
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
methodSE-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
supportSE-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()
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]]
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!).
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