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

Swift 5's Custom String Interpolation in Practice

Bas Broek
February 06, 2020

Swift 5's Custom String Interpolation in Practice

Bas Broek

February 06, 2020
Tweet

More Decks by Bas Broek

Other Decks in Programming

Transcript

  1. Using Swift 5's
    Custom String
    Interpolation in
    Practice
    @basthomas, Copenhagen Cocoa, 06-02-2020 1

    View Slide

  2. Who am I
    • Swift Weekly Brief
    • Contravariance (stickers)
    • RayWenderlich tech editor
    • iOS Platform at XING
    @basthomas, Copenhagen Cocoa, 06-02-2020 2

    View Slide

  3. Where do we come
    from?
    @basthomas, Copenhagen Cocoa, 06-02-2020 3

    View Slide

  4. !
    @basthomas, Copenhagen Cocoa, 06-02-2020 4

    View Slide

  5. NSString *name = @"Bas";
    [NSString stringWithFormat:@"Hello, %@", name];
    [NSString stringWithFormat:@"%.2f", 9.0234];
    [NSString stringWithFormat:
    @"Hello %@, how are you doing this %@? %@",
    @"Bas",
    @"evening",
    @"Great!"
    ];
    @basthomas, Copenhagen Cocoa, 06-02-2020 5

    View Slide


  6. @basthomas, Copenhagen Cocoa, 06-02-2020 6

    View Slide

  7. let name = "Bas"
    "Hello, \(name)"
    String(format: "%.2f", 9.0234) //
    "Hello \(name), how are you doing this \(timeOfDay)?"
    @basthomas, Copenhagen Cocoa, 06-02-2020 7

    View Slide

  8. It is very good.
    @basthomas, Copenhagen Cocoa, 06-02-2020 8

    View Slide

  9. Interlude
    @basthomas, Copenhagen Cocoa, 06-02-2020 9

    View Slide

  10. Swift Evolution
    @basthomas, Copenhagen Cocoa, 06-02-2020 10

    View Slide

  11. Multiline String Literals (SE-0168)
    """
    Hello,
    how are you doing
    this \(timeOfDay)?
    """
    @basthomas, Copenhagen Cocoa, 06-02-2020 11

    View Slide

  12. String Literal Delimiters (SE-0200)
    // before
    "\"Did you put your name into the Goblet of Fire, Harry?\" he asked calmly."
    // after
    #""Harry! Did you put your name in the Goblet of Fire?!""#
    @basthomas, Copenhagen Cocoa, 06-02-2020 12

    View Slide

  13. And then...
    @basthomas, Copenhagen Cocoa, 06-02-2020 13

    View Slide

  14. Custom String
    Interpolation
    (aka Fix ExpressibleByStringInterpolation)
    @basthomas, Copenhagen Cocoa, 06-02-2020 14

    View Slide

  15. Localization
    @basthomas, Copenhagen Cocoa, 06-02-2020 15

    View Slide

  16. let message: LocalizableString =
    #"The document "\(name)" could not be saved."#
    alert.messageText = String(localized: message)
    @basthomas, Copenhagen Cocoa, 06-02-2020 16

    View Slide

  17. Errors
    @basthomas, Copenhagen Cocoa, 06-02-2020 17

    View Slide

  18. extension String.StringInterpolation {
    mutating func appendInterpolation(_ error: Error) {
    appendLiteral(error.localizedDescription)
    }
    }
    fatalError(
    "Something went wrong: \(ConnectionError.invalidRequest)"
    )
    @basthomas, Copenhagen Cocoa, 06-02-2020 18

    View Slide

  19. Number formatting
    @basthomas, Copenhagen Cocoa, 06-02-2020 19

    View Slide

  20. extension String.StringInterpolation {
    mutating func appendInterpolation(
    number: Double,
    formatter: NumberFormatter
    ) {
    appendInterpolation(
    ifNotNil: formatter.string(from: NSNumber(value: number))
    )
    }
    }
    let priceFormatter = NumberFormatter()
    priceFormatter.numberStyle = .currency
    "\(number: 100.1234, formatter: priceFormatter)"
    @basthomas, Copenhagen Cocoa, 06-02-2020 20

    View Slide

  21. Logging and privacy
    @basthomas, Copenhagen Cocoa, 06-02-2020 21

    View Slide

  22. extension String.StringInterpolation {
    mutating func appendInterpolation(private: String) {
    #if DEBUG
    appendLiteral(`private`)
    #else
    appendLiteral("@@@")
    #endif
    }
    }
    let name = "Bas"
    let password = "Broek"
    print("User \(name) has password \(private: password)")
    @basthomas, Copenhagen Cocoa, 06-02-2020 22

    View Slide

  23. Other instances
    @basthomas, Copenhagen Cocoa, 06-02-2020 23

    View Slide

  24. "\(properties == nil ? "nil" : String(describing: properties ?? [:]))"
    @basthomas, Copenhagen Cocoa, 06-02-2020 24

    View Slide

  25. extension String.StringInterpolation {
    mutating func appendInterpolation(
    dictionary: Dictionary?,
    default: @autoclosure () -> String = "nil"
    ) {
    if let dictionary = dictionary {
    appendLiteral(String(describing: dictionary))
    } else {
    appendLiteral(`default`())
    }
    }
    }
    "\(dictionary: properties)"
    @basthomas, Copenhagen Cocoa, 06-02-2020 25

    View Slide

  26. 123 > maxBadgeCount ? String("\(maxBadgeCount)+") : String(123)
    @basthomas, Copenhagen Cocoa, 06-02-2020 26

    View Slide

  27. extension String.StringInterpolation {
    mutating func appendInterpolation(
    number: Int,
    maximum: @autoclosure () -> Int
    ) {
    let max = maximum()
    if number > max {
    appendLiteral("\(max)+")
    } else {
    appendLiteral("\(number)")
    }
    }
    }
    "\(number: number, maximum: maxBadgeCount)"
    @basthomas, Copenhagen Cocoa, 06-02-2020 27

    View Slide

  28. !
    @basthomas, Copenhagen Cocoa, 06-02-2020 28

    View Slide

  29. Polluting a
    namespace
    @basthomas, Copenhagen Cocoa, 06-02-2020 29

    View Slide

  30. Code, documentation,
    tests
    @basthomas, Copenhagen Cocoa, 06-02-2020 30

    View Slide

  31. struct BadgeCount: CustomStringConvertible {
    let value: Int
    var maximum = 99
    var description: String {
    if value > maximum {
    return "\(maximum)+"
    } else {
    return "\(value)"
    }
    }
    }
    "\(BadgeCount(value: 123, maximum: 99))"
    "\(BadgeCount(value: 123))"
    @basthomas, Copenhagen Cocoa, 06-02-2020 31

    View Slide

  32. Going all in
    @basthomas, Copenhagen Cocoa, 06-02-2020 32

    View Slide

  33. struct HTMLComponent: ExpressibleByStringLiteral, ExpressibleByStringInterpolation, CustomStringConvertible {
    struct StringInterpolation: StringInterpolationProtocol {
    var output = ""
    init(literalCapacity: Int, interpolationCount: Int) {
    output.reserveCapacity(literalCapacity * 2)
    }
    mutating func appendLiteral(_ literal: String) {
    output.append(literal)
    }
    mutating func appendInterpolation(twitter: String) {
    output.append("@\(twitter)")
    }
    mutating func appendInterpolation(email: String) {
    output.append("\(email)")
    }
    }
    let description: String
    init(stringLiteral value: String) {
    description = "\(value)"
    }
    init(stringInterpolation: StringInterpolation) {
    self.init(stringLiteral: stringInterpolation.output)
    }
    }
    @basthomas, Copenhagen Cocoa, 06-02-2020 33

    View Slide

  34. struct StringInterpolation: StringInterpolationProtocol {
    var output = ""
    init(literalCapacity: Int, interpolationCount: Int) {
    output.reserveCapacity(literalCapacity * 2)
    }
    mutating func appendLiteral(_ literal: String) {
    output.append(literal)
    }
    mutating func appendInterpolation(twitter: String) {
    output.append("@\(twitter)")
    }
    mutating func appendInterpolation(email: String) {
    output.append("\(email)")
    }
    }
    let html: HTMLComponent = "Find me on \(twitter: "basthomas") or send an email to \(email: "[email protected]")"
    // Find me on @basthomas
    // or send an email to [email protected]
    @basthomas, Copenhagen Cocoa, 06-02-2020 34

    View Slide

  35. Optimize code for
    reading, not writing
    @basthomas, Copenhagen Cocoa, 06-02-2020 35

    View Slide

  36. Communication is
    hard
    @basthomas, Copenhagen Cocoa, 06-02-2020 36

    View Slide

  37. Thanks!
    @basthomas
    @basthomas, Copenhagen Cocoa, 06-02-2020 37

    View Slide