Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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