Slide 1

Slide 1 text

ABOUTOBJECTS Swi$ 4 Highlights Jonathan Lehr, Founder and VP, Training 1

Slide 2

Slide 2 text

About Objects • Reston, VA • Full-stack consul3ng (NFL, Marrio;, Chicos, etc.) and training • Strong focus on iOS and middleware • Roots in NeXT, OpenStep, WebObjects + enterprise backends 2

Slide 3

Slide 3 text

Resources GitHub.com/AboutObjectsTraining • Swi% 4 Highlights • Swi% 3 Lowlights Ole Begemann (oleb.net/blog) • Playground: What's new in Swi7 4 Copyright © 2018, About Objects, Inc. 3

Slide 4

Slide 4 text

Swi$ 4 Overview Copyright © 2018, About Objects, Inc. 4

Slide 5

Slide 5 text

New/Enhanced in Swi. 4 • Strings and One-Sided Ranges • Collec4ons • Dictionary • Set • Key-Value Coding • Codable Protocol and JSON Support Copyright © 2018, About Objects, Inc. 5

Slide 6

Slide 6 text

Migra&ng From Swi. 3 • Explicit @obj direc-ves required on a per-method basis to enable dynamic dispatch • Some Swi< 4 String APIs now return a new type, Substring Copyright © 2018, About Objects, Inc. 6

Slide 7

Slide 7 text

Strings 7

Slide 8

Slide 8 text

Strings in Swi* 2 and Swi* 3 • Dropped Collection conformance • Added characters property containing collec4on of Character (extended grapheme cluster) • Substrings referred to original string's storage • ✅ Very efficient • ❌ Poten4al memory leak Copyright © 2018, About Objects, Inc. 8

Slide 9

Slide 9 text

Swi$ 3 String API Clutziness let stars = "✭✭✭✭✭✩✩✩✩✩" let charView1 = stars.characters.dropFirst(2) print(charView1) // CharacterView(_core: // Swift._StringCore(_baseAddress: Optional(0x0000000101362454), // _countAndFlags: 9223372036854775816, _owner: nil), _coreOffset: 2) let charView2 = charView1.dropLast(3) // Still a CharacterView ! print(String(charView2)) // ✭✭✭✩✩ // Swift 4 print(stars.dropFirst(2).dropLast(3)) // ✭✭✭✩✩ Copyright © 2018, About Objects, Inc. 9

Slide 10

Slide 10 text

Swi$ 3 String API Clutziness let stars = "✭✭✭✭✭✩✩✩✩✩" let charView1 = stars.characters.dropFirst(2) print(charView1) // CharacterView(_core: // Swift._StringCore(_baseAddress: Optional(0x0000000101362454), // _countAndFlags: 9223372036854775816, _owner: nil), _coreOffset: 2) let charView2 = charView1.dropLast(3) // Still a CharacterView ! print(String(charView2)) // ✭✭✭✩✩ // Swift 4 print(stars.dropFirst(2).dropLast(3)) // ✭✭✭✩✩ Copyright © 2018, About Objects, Inc. 10

Slide 11

Slide 11 text

Swi$ 3 String API Clutziness let stars = "✭✭✭✭✭✩✩✩✩✩" let charView1 = stars.characters.dropFirst(2) print(charView1) // CharacterView(_core: // Swift._StringCore(_baseAddress: Optional(0x0000000101362454), // _countAndFlags: 9223372036854775816, _owner: nil), _coreOffset: 2) let charView2 = charView1.dropLast(3) // Still a CharacterView ! print(String(charView2)) // ✭✭✭✩✩ // Swift 4 print(stars.dropFirst(2).dropLast(3)) // ✭✭✭✩✩ Copyright © 2018, About Objects, Inc. 11

Slide 12

Slide 12 text

Swi$ 4 Strings (SE-0163) • Adds back Collection conformance and deprecates characters property • Adds Substring type • Prevents leaks by helping developers avoid accidental storage of Substring instances • String and Substring share API by conforming to StringProtocol Copyright © 2018, About Objects, Inc. 12

Slide 13

Slide 13 text

String Collec-on API Examples // Looping through a string's characters: let s = "abc" for c in s { print(c) } // a // b // c // Inserting characters: var name = "Fred Smith" let index = name.index(of: " ") ?? name.endIndex name.insert(contentsOf: " W.", at: index) // Fred W. Smith Copyright © 2018, About Objects, Inc. 13

Slide 14

Slide 14 text

String Collec-on API Examples // Looping through a string's characters: let s = "abc" for c in s { print(c) } // a // b // c // Inserting characters: var name = "Fred Smith" let index = name.index(of: " ") ?? name.endIndex name.insert(contentsOf: " W.", at: index) // Fred W. Smith Copyright © 2018, About Objects, Inc. 14

Slide 15

Slide 15 text

Substring Example let name = "Fred Smith" let last: Substring = name.dropFirst(5) // type ^^^^^^^^^^^ shown for clarity print(last) // "Smith" struct Dude { var name: String? } var dude = Dude() dude.name = name // ! Doesn't compile Copyright © 2018, About Objects, Inc. 15

Slide 16

Slide 16 text

Substring Example let name = "Fred Smith" let last: Substring = name.dropFirst(5) // type ^^^^^^^^^^^ shown for clarity print(last) // "Smith" struct Dude { var name: String? } var dude = Dude() dude.name = name // ! Doesn't compile Copyright © 2018, About Objects, Inc. 16

Slide 17

Slide 17 text

Mul$-Line String Literals (SE-168) • Enclosed in triple-quotes • Whitespace up to trailing quotes ignored let year = 2017 let numPages = 240 let jsonText = """ { "title": "War of the Worlds", "author": "H. G. Wells", "publication_year": \(year), "number_of_pages": \(numPages) } """ Copyright © 2018, About Objects, Inc. 17

Slide 18

Slide 18 text

One-Sided Ranges (SE-172) • Ranges can be expressed without explicit star6ng or ending values let s = "Hello !!" // Compute an index relative to start of string. let index = s.index(s.startIndex, offsetBy: 6) let head = s[..

Slide 19

Slide 19 text

Collec&ons 19

Slide 20

Slide 20 text

Dic$onary Keys and Values (SE-154) • Adds type-specific collec0ons for keys and values • Faster key lookups • More effecient value muta0on let books = ["Emma": 11.95, "Henry V": 14.99, "1984": 14.99, "Utopia": 11.95] guard let index = books.index(forKey: "Emma") else { return } print(books.values[index]) // 11.95 Copyright © 2018, About Objects, Inc. 20

Slide 21

Slide 21 text

Dic$onary & Set Enhancements(SE-165) • Dic%onary-specific map and filter • Grouping sequence elements • Default values for subscripts • Merging dic%onaries Copyright © 2018, About Objects, Inc. 21

Slide 22

Slide 22 text

Dic$onary-Specific Filter let books = ["Emma": 11.95, "Henry V": 14.99, "1984": 14.99, "Utopia": 11.95] // In Swift 3, Dictionary's `filter` method returned an // array of key-value tuples instead of a dictionary. let cheapBooks = books.filter { $0.value < 12.00 } // [(key: "Utopia", value: 11.95), (key: "Emma", value: 11.95)] // If you need a Dictionary result, you have to produce one manually let cheapBooksDict = cheapBooks.reduce([:]) { var dict = $0 dict[$1.key] = $1.value return dict } // ["Utopia": 11.95, "Emma": 11.95] Copyright © 2018, About Objects, Inc. 22

Slide 23

Slide 23 text

Dic$onary-Specific Filter let books = ["Emma": 11.95, "Henry V": 14.99, "1984": 14.99, "Utopia": 11.95] // In Swift 3, Dictionary's `filter` method returned an // array of key-value tuples instead of a dictionary. let cheapBooks = books.filter { $0.value < 12.00 } // ["Utopia": 11.95, "Emma": 11.95] // If you need a Dictionary result, you have to produce one manually let cheapBooksDict = cheapBooks.reduce([:]) { var dict = $0 dict[$1.key] = $1.value return dict } // ["Utopia": 11.95, "Emma": 11.95] Copyright © 2018, About Objects, Inc. 23

Slide 24

Slide 24 text

Dic$onary-Specific Map let books = ["Emma": 11.95, "Henry V": 14.99, "1984": 14.99, "Utopia": 11.95] // Similarly, Dictionary's `map` method returns an array of values let discount = 0.10 let discountedPrices = books.map { $0.value * (1 - discount) } // [10.75, 13.49, 10.75, 13.49] // That's fine if you simply want to sum the values, but suppose // you want to produce a list of discounted prices? // Swift 4 adds `mapValues`, which returns a Dictionary let discount = 0.10 let discountedBooks = books.mapValues { $0 * (1 - discount) } // ["Utopia": 10.75, "1984": 13.49, "Emma": 10.75, "Henry V": 13.49] Copyright © 2018, About Objects, Inc. 24

Slide 25

Slide 25 text

Dic$onary-Specific Map let books = ["Emma": 11.95, "Henry V": 14.99, "1984": 14.99, "Utopia": 11.95] // Similarly, Dictionary's `map` method returns an array of values let discount = 0.10 let discountedPrices = books.map { $0.value * (1 - discount) } // [10.75, 13.49, 10.75, 13.49] // That's fine if you simply want to sum the values, but suppose // you want to produce a list of discounted prices? // Swift 4 adds `mapValues`, which returns a Dictionary let discount = 0.10 let discountedBooks = books.mapValues { $0 * (1 - discount) } // ["Utopia": 10.75, "1984": 13.49, "Emma": 10.75, "Henry V": 13.49] Copyright © 2018, About Objects, Inc. 25

Slide 26

Slide 26 text

Dic$onary-Specific Map let books = ["Emma": 11.95, "Henry V": 14.99, "1984": 14.99, "Utopia": 11.95] // Similarly, Dictionary's `map` method returns an array of values let discount = 0.10 let discountedPrices = books.map { $0.value * (1 - discount) } // [10.75, 13.49, 10.75, 13.49] // That's fine if you simply want to sum the values, but suppose // you want to produce a list of discounted prices? // Swift 4 adds `mapValues`, which returns a Dictionary let discount = 0.10 let discountedBooks = books.mapValues { $0 * (1 - discount) } // ["Utopia": 10.75, "1984": 13.49, "Emma": 10.75, "Henry V": 13.49] Copyright © 2018, About Objects, Inc. 26

Slide 27

Slide 27 text

Grouping Sequence Elements • Swi% 4 adds a new ini-alizer for grouping sequences of values. ! let books = ["Emma": 11.95, "Henry V": 14.99, "1984": 14.99, "Utopia": 11.95] let booksByPrice = Dictionary(grouping: books, by: { $0.value }) // [11.95: [(key: "Utopia", value: 11.95), // (key: "Emma", value: 11.95)], // 14.99: [(key: "1984", value: 14.99), // (key: "Henry V", value: 14.99)]] Copyright © 2018, About Objects, Inc. 27

Slide 28

Slide 28 text

Default Values for Subscripts // Access with default value may not seem like a huge win let books = ["Emma": 11.95, "Henry V": 14.99, "1984": 14.99, "Utopia": 11.95] // Swift 3: let price = books["Foo"] ?? 0 // Swift 4: let price2 = books["Foo", default: 0] // ...but mutation with a default value is ! var discountedBooks = books let keys = ["Emma", "1984", "Foo"] for key in keys { discountedBooks[key, default: 0] *= 0.9 } // ["Utopia": 11.95, "1984": 13.49, "Foo": 0.0, "Emma": 10.75, "Henry V": 14.99] Copyright © 2018, About Objects, Inc. 28

Slide 29

Slide 29 text

Default Values for Subscripts // Access with default value may not seem like a huge win let books = ["Emma": 11.95, "Henry V": 14.99, "1984": 14.99, "Utopia": 11.95] // Swift 3: let price = books["Foo"] ?? 0 // Swift 4: let price2 = books["Foo", default: 0] // ...but mutation with a default value is ! var discountedBooks = books let keys = ["Emma", "1984", "Foo"] for key in keys { discountedBooks[key, default: 0] *= 0.9 } // ["Utopia": 11.95, "1984": 13.49, "Foo": 0.0, "Emma": 10.75, "Henry V": 14.99] Copyright © 2018, About Objects, Inc. 29

Slide 30

Slide 30 text

Merging Dic*onaries let personal = ["home": "703-333-4567", "cell": "202-444-1234"] let work = ["main": "571-222-9876", "cell": "703-987-5678"] // If keys match, replaces the current value with the newer value var phones1 = personal phones1.merge(work) { _, new in new } ["main": "571-222-9876", "cell": "703-987-5678", "home": "703-333-4567"] // If keys match, replaces the current value with a tuple of both values var phones2: [String: Any] = personal phones2.merge(work) { (personal: $0, work: $1) } ["main": "571-222-9876", "cell": (personal: "202-444-1234", work: "703-987-5678"), "home": "703-333-4567"] Copyright © 2018, About Objects, Inc. 30

Slide 31

Slide 31 text

Merging Dic*onaries let personal = ["home": "703-333-4567", "cell": "202-444-1234"] let work = ["main": "571-222-9876", "cell": "703-987-5678"] // If keys match, replaces the current value with the newer value var phones1 = personal phones1.merge(work) { _, new in new } ["main": "571-222-9876", "cell": "703-987-5678", "home": "703-333-4567"] // If keys match, replaces the current value with a tuple of both values var phones2: [String: Any] = personal phones2.merge(work) { (personal: $0, work: $1) } ["main": "571-222-9876", "cell": (personal: "202-444-1234", work: "703-987-5678"), "home": "703-333-4567"] Copyright © 2018, About Objects, Inc. 31

Slide 32

Slide 32 text

Key-Value Coding 32

Slide 33

Slide 33 text

Smart KeyPaths (SE-161) • Allows key paths to be used with non-objc types • New expression syntax for key paths • Similar to property reference, but prefixed with \ for example, \Book.rating • Expression result is an instance of KeyPath Copyright © 2018, About Objects, Inc. 33

Slide 34

Slide 34 text

Smart KeyPaths Example (1) struct Person { var name: String var address: Address } struct Address: CustomStringConvertible { var street: String var city: String } let address = Address(street: "21 Elm", city: "Reston") let person = Person(name: "Jo", address: address) let name = person[keyPath: \Person.name] // "Jo" let city = person[keyPath: \Person.address.city] // "Reston" Copyright © 2018, About Objects, Inc. 34

Slide 35

Slide 35 text

Smart KeyPaths Example (2) • Instances of KeyPath can be stored let address = Address(street: "21 Elm", city: "Reston") let person = Person(name: "Jo", address: address) // Initialize an array of KeyPaths let keyPaths = [\Person.name, \Person.address.city, \Person.address.street] // Map KeyPaths to an array of property values let values = keyPaths.map { person[keyPath: $0] } // ["Jo", "Reston", "21 Elm"] Copyright © 2018, About Objects, Inc. 35

Slide 36

Slide 36 text

Smart KeyPaths Example (3) • You can use KeyPaths to mutate proper3es of non- ObjC types // KeyPaths allow you to mutate properties of Swift types let address = Address(street: "21 Elm", city: "Reston") var mutablePerson = Person(name: "Jo", address: address) mutablePerson[keyPath: \Person.name] = "Kay" mutablePerson[keyPath: \Person.address.city] = "Herndon" // Person(name: "Kay", address: // Address(street: "21 Elm", city: "Herndon")) Copyright © 2018, About Objects, Inc. 36

Slide 37

Slide 37 text

Codable 37

Slide 38

Slide 38 text

Swi$ Archival and Serializa1on (SE-166) • Adds protocols for • Encoders and decoders • Encodable and decodable types • Property keys • User info keys Copyright © 2018, About Objects, Inc. 38

Slide 39

Slide 39 text

Codable Protocols • Compiler can synthesize default implementa6ons /// A type that can encode values into a native format /// for external representation. public protocol Encodable { public func encode(to encoder: Encoder) throws } /// A type that can decode itself from an external representation. public protocol Decodable { public init(from decoder: Decoder) throws } public typealias Codable = Decodable & Encodable Copyright © 2018, About Objects, Inc. 39

Slide 40

Slide 40 text

Standard Library Codable Types • Optional • Array, Dictionary • String, Int, Double • Date, Data, URL Copyright © 2018, About Objects, Inc. 40

Slide 41

Slide 41 text

Declaring Codable Types // Declare Person and Dog structs conforming to Codable struct Person: Codable { var name: String var age: Int var dog: Dog } struct Dog: Codable { var name: String var breed: Breed // Codable has built-in support for enums with raw values. enum Breed: String, Codable { case collie = "Collie" case beagle = "Beagle" case greatDane = "Great Dane" } } Copyright © 2018, About Objects, Inc. 41

Slide 42

Slide 42 text

Swi$ Encoders (SE-167) • Founda(on framework classes are bridged across as Swi7 types Swi$ Standard Library Founda1on JSONEncoder NSJSONSerialization JSONDecoder NSJSONSerialization PropertyListEncoder NSPropertyListSerialization PropertyListDecoder NSPropertyListSerialization Copyright © 2018, About Objects, Inc. 42

Slide 43

Slide 43 text

Encoding to JSON let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let fred = Person(name: "Fred", age: 30, dog: Dog(name: "Spot", breed: .beagle)) let data = try! encoder.encode(fred) { "name" : "Fred", "age" : 30, "dog" : { "name" : "Spot", "breed" : "Beagle" } } Copyright © 2018, About Objects, Inc. 43

Slide 44

Slide 44 text

Encoding to JSON let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let fred = Person(name: "Fred", age: 30, dog: Dog(name: "Spot", breed: .beagle)) let data = try! encoder.encode(fred) { "name" : "Fred", "age" : 30, "dog" : { "name" : "Spot", "breed" : "Beagle" } } Copyright © 2018, About Objects, Inc. 44

Slide 45

Slide 45 text

Encoding to JSON let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let fred = Person(name: "Fred", age: 30, dog: Dog(name: "Spot", breed: .beagle)) let data = try! encoder.encode(fred) { "name" : "Fred", "age" : 30, "dog" : { "name" : "Spot", "breed" : "Beagle" } } Copyright © 2018, About Objects, Inc. 45

Slide 46

Slide 46 text

Decoding from JSON let decoder = JSONDecoder() let fredsClone = try! decoder.decode(Person.self, from: data) // Person(name: "Fred", // age: 30, // dog: Dog(name: "Spot", // breed: Dog.Breed.beagle)) Copyright © 2018, About Objects, Inc. 46

Slide 47

Slide 47 text

Decoding from JSON let decoder = JSONDecoder() let fredsClone = try! decoder.decode(Person.self, from: data) // Person(name: "Fred", // age: 30, // dog: Dog(name: "Spot", // breed: Dog.Breed.beagle)) !" Copyright © 2018, About Objects, Inc. 47

Slide 48

Slide 48 text

Codable Demo 48

Slide 49

Slide 49 text

ABOUTOBJECTS 49

Slide 50

Slide 50 text

Upcoming Classes View online: Public schedule Date Title Mar 12 – 14 Transi-oning to Swi3 Apr 14 – Apr 20 iOS Development in Swi3: Comprehensive Apr 30 – May 4 Advanced iOS Development 50

Slide 51

Slide 51 text

Q & A 51