$30 off During Our Annual Pro Sale. View Details »

Swift 4 Highlights

Swift 4 Highlights

Presentation given at Capital One in McLean, VA, March 5, 2018

Jonathan Lehr

March 05, 2018
Tweet

More Decks by Jonathan Lehr

Other Decks in Programming

Transcript

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

    View Slide

  2. 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

    View Slide

  3. 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

    View Slide

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

    View Slide

  5. 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

    View Slide

  6. 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

    View Slide

  7. Strings
    7

    View Slide

  8. 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

    View Slide

  9. 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

    View Slide

  10. 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

    View Slide

  11. 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

    View Slide

  12. 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

    View Slide

  13. 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

    View Slide

  14. 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

    View Slide

  15. 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

    View Slide

  16. 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

    View Slide

  17. 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

    View Slide

  18. 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[..print(head) // "Hello "
    let tail = s[index...]
    print(tail) // "!!"
    Copyright © 2018, About Objects, Inc. 18

    View Slide

  19. Collec&ons
    19

    View Slide

  20. 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

    View Slide

  21. 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

    View Slide

  22. 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

    View Slide

  23. 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

    View Slide

  24. 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

    View Slide

  25. 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

    View Slide

  26. 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

    View Slide

  27. 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

    View Slide

  28. 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

    View Slide

  29. 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

    View Slide

  30. 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

    View Slide

  31. 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

    View Slide

  32. Key-Value Coding
    32

    View Slide

  33. 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

    View Slide

  34. 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

    View Slide

  35. 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

    View Slide

  36. 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

    View Slide

  37. Codable
    37

    View Slide

  38. 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

    View Slide

  39. 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

    View Slide

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

    View Slide

  41. 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

    View Slide

  42. 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

    View Slide

  43. 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

    View Slide

  44. 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

    View Slide

  45. 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

    View Slide

  46. 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

    View Slide

  47. 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

    View Slide

  48. Codable Demo
    48

    View Slide

  49. ABOUTOBJECTS
    49

    View Slide

  50. 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

    View Slide

  51. Q & A
    51

    View Slide