Slide 1

Slide 1 text

Swift Smart KeyPath
 Better Key-Value Coding for Swift iOS@Taipei 艾倫倫 Ni

Slide 2

Slide 2 text

What is Smart KeyPath?

Slide 3

Slide 3 text

What is Smart KeyPath? \UIView.frame

Slide 4

Slide 4 text

\UIView.frame

Slide 5

Slide 5 text

\UIView.frame BackSlash

Slide 6

Slide 6 text

\UIView.frame Dot

Slide 7

Slide 7 text

\UIView.frame BaseType

Slide 8

Slide 8 text

\UIView.frame BaseType PropertyName

Slide 9

Slide 9 text

Proposal: SE-0161 Implemented (Swift 4) Defer way to get / set property With type information (#keyPath( ) only return ‘Any’)
 Applicable to any type (#keyPath( ) only applicable to NSObjects) \UIView.frame

Slide 10

Slide 10 text

Proposal: SE-0161 Implemented (Swift 4) Defer way to get / set property With type information (#keyPath( ) only return ‘Any’)
 Applicable to any type (#keyPath( ) only applicable to NSObjects) let keyPath = \UIView.frame view[keyPath: keyPath] \UIView.frame

Slide 11

Slide 11 text

Proposal: SE-0161 Implemented (Swift 4) Defer way to get / set property With type information (#keyPath( ) only return ‘Any’)
 Applicable to any type (#keyPath( ) only applicable to NSObjects) let keyPath = \UIView.frame view[keyPath: keyPath] \UIView.frame

Slide 12

Slide 12 text

Proposal: SE-0161 Implemented (Swift 4) Defer way to get / set property With type information (#keyPath( ) only return ‘Any’)
 Applicable to any type (#keyPath( ) only applicable to NSObjects) let keyPath = \UIView.frame view[keyPath: keyPath] let frame = view[keyPath: path] \UIView.frame

Slide 13

Slide 13 text

Proposal: SE-0161 Implemented (Swift 4) Defer way to get / set property With type information (#keyPath( ) only return ‘Any’)
 Applicable to any type (#keyPath( ) only applicable to NSObjects) let keyPath = \UIView.frame view[keyPath: keyPath] let frame = view[keyPath: path] \UIView.frame //frame: CGRect

Slide 14

Slide 14 text

Proposal: SE-0161 Implemented (Swift 4) Defer way to get / set property With type information (#keyPath( ) only return ‘Any’)
 Applicable to any type (#keyPath( ) only applicable to NSObjects) let keyPath = \UIView.frame view[keyPath: keyPath] let frame = view[keyPath: path] struct User { var id: Int } \UIView.frame //frame: CGRect

Slide 15

Slide 15 text

Proposal: SE-0161 Implemented (Swift 4) Defer way to get / set property With type information (#keyPath( ) only return ‘Any’)
 Applicable to any type (#keyPath( ) only applicable to NSObjects) let keyPath = \UIView.frame view[keyPath: keyPath] let frame = view[keyPath: path] struct User { var id: Int } \User.id \UIView.frame //frame: CGRect

Slide 16

Slide 16 text

KeyPath \UIView.frame \UIView.frame.size

Slide 17

Slide 17 text

AnyKeyPath PartialKeyPath KeyPath WritableKeyPath ReferenceWritableKeyPath

Slide 18

Slide 18 text

KeyPath WritableKeyPath ReferenceWritableKeyPath

Slide 19

Slide 19 text

KeyPath WritableKeyPath ReferenceWritableKeyPath read only

Slide 20

Slide 20 text

KeyPath WritableKeyPath ReferenceWritableKeyPath read only read / write (variable)

Slide 21

Slide 21 text

KeyPath WritableKeyPath ReferenceWritableKeyPath read only read / write (variable) read / write (variable/constant) Class only

Slide 22

Slide 22 text

\UIView.frame.size \UIView.frame.width

Slide 23

Slide 23 text

\UIView.frame.size \UIView.frame.width -> ReferenceWritableKeyPath

Slide 24

Slide 24 text

\UIView.frame.size \UIView.frame.width -> ReferenceWritableKeyPath -> KeyPath

Slide 25

Slide 25 text

\UIView.frame.size \UIView.frame.width struct User { var id: Int } -> ReferenceWritableKeyPath -> KeyPath

Slide 26

Slide 26 text

\UIView.frame.size \UIView.frame.width struct User { var id: Int } -> ReferenceWritableKeyPath -> KeyPath -> WritableKeyPath \User.id

Slide 27

Slide 27 text

struct Ticket { var price: Int } let tickets = [Int](0...5) .compactMap{ Ticket(price: $0) } // [ {price 0}, ..., {price 5} ]

Slide 28

Slide 28 text

extension Array { } func sum (of keyPath: KeyPath) -> T { return reduce(0, {$0 += $1[keyPath: keyPath]} ) }

Slide 29

Slide 29 text

extension Array { } func sum (of keyPath: KeyPath) -> T { return reduce(0, {$0 += $1[keyPath: keyPath]} ) } tickets.sum(of: \.price) // 15

Slide 30

Slide 30 text

extension Array { } func sorted (_ keyPath: KeyPath, by comparation: (T, T) -> Bool) -> [Element] { return sorted(by: { comparation($0[keyPath: keyPath], $1[keyPath: keyPath]) }) }

Slide 31

Slide 31 text

extension Array { } func sorted (_ keyPath: KeyPath, by comparation: (T, T) -> Bool) -> [Element] { return sorted(by: { comparation($0[keyPath: keyPath], $1[keyPath: keyPath]) }) } tickets.sorted(\.price, by: >) // {price 5}, ..., {price 1}

Slide 32

Slide 32 text

extension Array { } func calculate (_ keyPath: KeyPath, with operation: (T, T) -> T) -> T? { guard !isEmpty else { return nil } var copy = self let initValue = copy.removeFirst()[keyPath: keyPath] return copy.reduce(initValue, { operation($0, $1[keyPath: keyPath]) }) }

Slide 33

Slide 33 text

extension Array { } func calculate (_ keyPath: KeyPath, with operation: (T, T) -> T) -> T? { guard !isEmpty else { return nil } var copy = self let initValue = copy.removeFirst()[keyPath: keyPath] return copy.reduce(initValue, { operation($0, $1[keyPath: keyPath]) }) } tickets.calculate(\.price, with: +) // 15

Slide 34

Slide 34 text

tickets.sorted(\.price, by: >) tickets.calculate(\.price, with: +) tickets.sum(of: \.price)

Slide 35

Slide 35 text

tickets.sorted(\.price, by: >) tickets.calculate(\.price, with: +) tickets.sum(of: \.price) tickets.reduce(0, { $0 + $1.price })

Slide 36

Slide 36 text

Property Validation

Slide 37

Slide 37 text

let pswdField: UITextField = .init() pswdField.text = "1234" pswdField .check(\.text, with: { $0?.isEmpty == false }) .check(\.text, with: { $0?.count ?? 0 >= 6 }) .check(\.text, with: { $0 != "123456" }) .validate() // false

Slide 38

Slide 38 text

protocol Validation: NSObject { }

Slide 39

Slide 39 text

extension Validation { var currentResult: Bool? { get { return objc_getAssociatedObject(self, &AssociaKeys.currResult) as? Bool } set { objc_setAssociatedObject(self, &AssociaKeys.currResult, newValue, .OBJC_ASSOCIATION_RETAIN)} } } extension NSObject: Validation { fileprivate struct AssociaKeys { static var currResult = "currResult" } } protocol Validation: NSObject { }

Slide 40

Slide 40 text

func check(_ target: KeyPath, with condition: (T) -> Bool) -> Self { if currentResult == false { return self } else { self.currentResult = condition(self[keyPath: target]) return self } } protocol Validation: NSObject { } extension Validation { }

Slide 41

Slide 41 text

func check(_ target: KeyPath, with condition: (T) -> Bool) -> Self { if currentResult == false { return self } else { self.currentResult = condition(self[keyPath: target]) return self } } func validate() -> Bool { if let validate = currentResult { currentResult = nil print(validate) return validate } else { return true } } protocol Validation: NSObject { } extension Validation { }

Slide 42

Slide 42 text

let pswdField: UITextField = .init() pswdField .check(\.text, with: { $0?.isEmpty == false }) .check(\.text, with: { $0?.count ?? 0 >= 6 }) .check(\.text, with: { $0 != "123456" }) .validate()

Slide 43

Slide 43 text

let pswdField: UITextField = .init() pswdField .check(\.text, with: { $0?.isEmpty == false }) .check(\.text, with: { $0?.count ?? 0 >= 6 }) .check(\.text, with: { $0 != "123456" }) .check(\.frame.size.width, with: { $0 >= 100 }) .validate()

Slide 44

Slide 44 text

Property Validation

Slide 45

Slide 45 text

Property Validation Second Case

Slide 46

Slide 46 text

let pswdField: UITextField = .init() pswdField .check(\.text, with: { $0?.isEmpty == false }) .check(\.text, with: { $0?.count ?? 0 >= 8 }) .check(\.text, with: { $0 != "123456" }) // do something else ... pswdField.text = "123456" // do something else again … pswdField.validate() // false

Slide 47

Slide 47 text

struct ValidationStash { var condition: (T) -> Bool var keypath: KeyPath } protocol Validation: class { associatedtype ClassType associatedtype ValueType var stashes: [ValidationStash ]? { set get } } extension Validation { func check( _ target: KeyPath, with condition: @escaping (ValueType? ) -> Bool) -> Self { //... } func validate() -> Bool { //... } }

Slide 48

Slide 48 text

struct ValidationStash { var condition: (T) -> Bool var keypath: KeyPath } protocol Validation: class { associatedtype ClassType associatedtype ValueType var stashes: [ValidationStash ]? { set get } } extension Validation { func check( _ target: KeyPath, with condition: @escaping (ValueType? ) -> Bool) -> Self { //... } func validate() -> Bool { //... } }

Slide 49

Slide 49 text

func check( _ target: KeyPath, with condition: @escaping (ValueType? ) -> Bool) -> Self { let model = ValidationStash(condition: condition, keypath: target) if var array = stashes { array.append(model) stashes = array } else { stashes = [model] } return self }

Slide 50

Slide 50 text

func validate() -> Bool { for stash in stashes ?? [] { guard let self = self as? ClassType else { continue } let target = self[keyPath: stash.keypath] let result = stash.condition(target) if result == false { return result } } return true }

Slide 51

Slide 51 text

extension UITextField: Validation { typealias ClassType = UITextField typealias ValueType = String var stashes: [ValidationStash]? { get { return objc_getAssociatedObject(self, &AssociatedKeys.array) as? [ValidationStash] } set { objc_setAssociatedObject(self, &AssociatedKeys.array, newValue, .OBJC_ASSOCIATION_RETAIN)} } fileprivate struct AssociatedKeys { static var array = “AssociatedKeys_array" } }

Slide 52

Slide 52 text

userField.validate() // false pswdField.validate() // true let userField: UITextField = .init() userField .check(\.text, with: { $0?.isEmpty == false }) .check(\.text, with: { $0?.count ?? 0 >= 8 }) .check(\.text, with: { $0 != "123456" }) let pswdField: UITextField = .init() pswdField .check(\.text, with: { $0?.isEmpty == false }) .check(\.text, with: { $0?.count ?? 0 >= 6 }) .check(\.text, with: { $0 != "123456" }) userField.text = "123456" pswdField.text = "demo123456"

Slide 53

Slide 53 text

userField.validate() // false pswdField.validate() // true let userField: UITextField = .init() userField .check(\.text, with: { $0?.isEmpty == false }) .check(\.text, with: { $0?.count ?? 0 >= 8 }) .check(\.text, with: { $0 != "123456" }) let pswdField: UITextField = .init() pswdField .check(\.text, with: { $0?.isEmpty == false }) .check(\.text, with: { $0?.count ?? 0 >= 6 }) .check(\.text, with: { $0 != "123456" }) userField.text = "123456" pswdField.text = "demo123456"

Slide 54

Slide 54 text

userField.validate() // false pswdField.validate() // true let userField: UITextField = .init() userField .check(\.text, with: { $0?.isEmpty == false }) .check(\.text, with: { $0?.count ?? 0 >= 8 }) .check(\.text, with: { $0 != "123456" }) let pswdField: UITextField = .init() pswdField .check(\.text, with: { $0?.isEmpty == false }) .check(\.text, with: { $0?.count ?? 0 >= 6 }) .check(\.text, with: { $0 != "123456" }) userField.text = "123456" pswdField.text = "demo123456"

Slide 55

Slide 55 text

Fluent AutoLayout

Slide 56

Slide 56 text

var label = UILabel() .set(\.text, to: "Hello World.") .set(\.font, to: .systemFont(ofSize: 14)) .set(\.lineBreakMode, to: .byWordWrapping) .set(\.numberOfLines, to: 0) .set(\.textColor, to: .darkGray) func set(_ keyPath: ReferenceWritableKeyPath, to value: T) -> Self { self[keyPath: keyPath] = value return self } Reference: http://www.swifttube.co/video/the-underestimated-power-of- keypaths

Slide 57

Slide 57 text

var label = UILabel() .set(\.text, to: "Hello World.") .set(\.font, to: .systemFont(ofSize: 14)) .set(\.lineBreakMode, to: .byWordWrapping) .set(\.numberOfLines, to: 0) .set(\.textColor, to: .darkGray) func set(_ keyPath: ReferenceWritableKeyPath, to value: T) -> Self { self[keyPath: keyPath] = value return self } Reference: http://www.swifttube.co/video/the-underestimated-power-of- keypaths Easy to implement Fluent Interface!

Slide 58

Slide 58 text

var label = UILabel() .set(\.text, to: "Hello World.") .set(\.font, to: .systemFont(ofSize: 14)) .set(\.lineBreakMode, to: .byWordWrapping) .set(\.numberOfLines, to: 0) .set(\.textColor, to: .darkGray) func set(_ keyPath: ReferenceWritableKeyPath, to value: T) -> Self { self[keyPath: keyPath] = value return self } Reference: http://www.swifttube.co/video/the-underestimated-power-of- keypaths Easy to implement Fluent Interface! -> Self -> Self -> Self -> Self -> Self

Slide 59

Slide 59 text

descLabel .translatesAutoresizingMaskIntoConstraints = false let constraints = [ descLabel.widthAnchor .constraint(equalTo: view.widthAnchor, multiplier: 0.9, constant: 10), descLabel.heightAnchor .constraint(greaterThanOrEqualTo: view.heightAnchor), descLabel.centerYAnchor .constraint(equalTo: view.centerYAnchor, constant: 0), descLabel.centerXAnchor .constraint(equalTo: view.centerXAnchor, constant: 0) ] NSLayoutConstraint.activate(constraints)

Slide 60

Slide 60 text

nameLabel.translatesAutoresizingMaskIntoConstraints = false nameLabel.widthAnchor.constraint(equalTo: view.widthAnchor) .isActive = true nameLabel.heightAnchor.constraint(greaterThanOrEqualTo: view.heightAnchor).isActive = true nameLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

Slide 61

Slide 61 text

nameLabel.translatesAutoresizingMaskIntoConstraints = false nameLabel.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.2, constant: 10).isActive = true nameLabel.heightAnchor.constraint(greaterThanOrEqualTo: view.heightAnchor, multiplier: 0.2).isActive = true nameLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true

Slide 62

Slide 62 text

nameLabel .setAnchor(\.widthAnchor, .equal, to: view.widthAnchor) .setAnchor(\.heightAnchor, .greaterThanOrEqual, to: view.heightAnchor) .setAnchor(\.topAnchor, .equal, to: view.topAnchor) .setAnchor(\.centerXAnchor, .equal, to:view.centerXAnchor)

Slide 63

Slide 63 text

nameLabel .setAnchor(\.widthAnchor, .equal, to: view.widthAnchor, constant: 10, multiplier: 0.2) .setAnchor(\.heightAnchor, .greaterThanOrEqual, to: view.heightAnchor, multiplier: 0.2) .setAnchor(\.topAnchor, .equal, to: view.topAnchor) .setAnchor(\.centerXAnchor, .equal, to:view.centerXAnchor)

Slide 64

Slide 64 text

let topConstraint = label.setConstraint(\.topAnchor, .equal, to: view.topAnchor) nameLabel .setAnchor(\.widthAnchor, .equal, to: view.widthAnchor, constant: 10, multiplier: 0.2) .setAnchor(\.heightAnchor, .greaterThanOrEqual, to: view.heightAnchor, multiplier: 0.2) .setAnchor(\.topAnchor, .equal, to: view.topAnchor) .setAnchor(\.centerXAnchor, .equal, to:view.centerXAnchor)

Slide 65

Slide 65 text

( _ keyPath: KeyPath, _ relation: NSLayoutConstraint.Relation, to anchor: LayoutType, ) -> NSLayoutConstraint { translatesAutoresizingMaskIntoConstraints = false return constraint } constant: CGFloat = 0, multiplier: CGFloat? = nil, priority: UILayoutPriority = .required if let multiplier = multiplier, //... } else { //... } @discardableResult func setConstraint let constraint: NSLayoutConstraint constraint.priority = priority constraint.isActive = true , AnchorType>

Slide 66

Slide 66 text

( _ keyPath: KeyPath, _ relation: NSLayoutConstraint.Relation, to anchor: LayoutType, ) -> NSLayoutConstraint { translatesAutoresizingMaskIntoConstraints = false return constraint } constant: CGFloat = 0, multiplier: CGFloat? = nil, priority: UILayoutPriority = .required if let multiplier = multiplier, //... } else { //... } @discardableResult func setConstraint let constraint: NSLayoutConstraint constraint.priority = priority constraint.isActive = true , AnchorType>

Slide 67

Slide 67 text

( _ keyPath: KeyPath, _ relation: NSLayoutConstraint.Relation, to anchor: LayoutType, ) -> NSLayoutConstraint { translatesAutoresizingMaskIntoConstraints = false return constraint } constant: CGFloat = 0, multiplier: CGFloat? = nil, priority: UILayoutPriority = .required if let multiplier = multiplier, //... } else { //... } @discardableResult func setConstraint let constraint: NSLayoutConstraint constraint.priority = priority constraint.isActive = true , AnchorType>

Slide 68

Slide 68 text

( _ keyPath: KeyPath, _ relation: NSLayoutConstraint.Relation, to anchor: LayoutType, ) -> NSLayoutConstraint { translatesAutoresizingMaskIntoConstraints = false return constraint } constant: CGFloat = 0, multiplier: CGFloat? = nil, priority: UILayoutPriority = .required if let multiplier = multiplier, //... } else { //... } @discardableResult func setConstraint let constraint: NSLayoutConstraint constraint.priority = priority constraint.isActive = true , AnchorType>

Slide 69

Slide 69 text

if let multiplier = multiplier, let dimension = self[keyPath: keyPath] as? NSLayoutDimension, let anchor = anchor as? NSLayoutDimension { //... } else { //... } to anchor: LayoutType, constant: CGFloat = 0, multiplier: CGFloat? = nil, priority: UILayoutPriority = .required @discardableResult ( _ keyPath: KeyPath, _ relation: NSLayoutConstraint.Relation, to anchor: LayoutType, ) -> NSLayoutConstraint { translatesAutoresizingMaskIntoConstraints = false constant: CGFloat = 0, multiplier: CGFloat? = nil, priority: UILayoutPriority = .required @discardableResult func setConstraint let constraint: NSLayoutConstraint constraint.priority = priority constraint.isActive = true , AnchorType>

Slide 70

Slide 70 text

if let multiplier = multiplier, let dimension = self[keyPath: keyPath] as? NSLayoutDimension, let anchor = anchor as? NSLayoutDimension { //... } else { //... } to anchor: LayoutType, constant: CGFloat = 0, multiplier: CGFloat? = nil, priority: UILayoutPriority = .required @discardableResult ( _ keyPath: KeyPath, _ relation: NSLayoutConstraint.Relation, to anchor: LayoutType, ) -> NSLayoutConstraint { translatesAutoresizingMaskIntoConstraints = false constant: CGFloat = 0, multiplier: CGFloat? = nil, priority: UILayoutPriority = .required @discardableResult func setConstraint let constraint: NSLayoutConstraint constraint.priority = priority constraint.isActive = true , AnchorType>

Slide 71

Slide 71 text

if let multiplier = multiplier, let dimension = self[keyPath: keyPath] as? NSLayoutDimension, let anchor = anchor as? NSLayoutDimension { switch relation { //NSLayoutDimension //... } } else { switch relation { //NSLayoutAnchor //... } }

Slide 72

Slide 72 text

switch relation { //NSLayoutDimension case .equal: constraint = dimension .constraint(equalTo: anchor, multiplier:multiplier, constant: constant) case .greaterThanOrEqual: constraint = dimension .constraint(greaterThanOrEqualTo: anchor, multiplier: multiplier, constant: constant) case .lessThanOrEqual: constraint = dimension .constraint(lessThanOrEqualTo: anchor, multiplier: multiplier, constant: constant) }

Slide 73

Slide 73 text

switch relation { //NSLayoutAnchor case .equal: constraint = self[keyPath: keyPath] .constraint(equalTo: anchor, constant: constant) case .greaterThanOrEqual: constraint = self[keyPath: keyPath] .constraint(greaterThanOrEqualTo: anchor, constant: constant) case .lessThanOrEqual: constraint = self[keyPath: keyPath] .constraint(lessThanOrEqualTo: anchor, constant: constant) }

Slide 74

Slide 74 text

@discardableResult , AnchorType>( ) -> Self { setConstraint(keyPath, relation, to: anchor, constant: constant, multiplier: multiplier, priority: priority) return self } _ keyPath: KeyPath, _ relation: NSLayoutConstraint.Relation, to anchor: LayoutType, constant: CGFloat = 0, multiplier: CGFloat? = nil, priority: UILayoutPriority = .required func setAnchor

Slide 75

Slide 75 text

@discardableResult , AnchorType>( ) -> Self { setConstraint(keyPath, relation, to: anchor, constant: constant, multiplier: multiplier, priority: priority) return self } _ keyPath: KeyPath, _ relation: NSLayoutConstraint.Relation, to anchor: LayoutType, constant: CGFloat = 0, multiplier: CGFloat? = nil, priority: UILayoutPriority = .required func setAnchor

Slide 76

Slide 76 text

nameLabel.translatesAutoresizingMaskIntoConstraints = false nameLabel.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.2, constant: 10) .isActive = true nameLabel.heightAnchor.constraint(greaterThanOrEqualTo: view.heightAnchor, multiplier: 0.2).isActive = true nameLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true descLabel.translatesAutoresizingMaskIntoConstraints = false descLabel.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9, constant: 10) .isActive = true descLabel.heightAnchor.constraint(greaterThanOrEqualTo: view.heightAnchor) .isActive = true descLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true descLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true

Slide 77

Slide 77 text

nameLabel .setAnchor(\.widthAnchor, .equal, to: view.widthAnchor, constant: 10, multiplier: 0.2) .setAnchor(\.heightAnchor, .greaterThanOrEqual, to: view.heightAnchor, multiplier: 0.2) .setAnchor(\.centerXAnchor, .equal, to: view.centerXAnchor) .setAnchor(\.topAnchor, .equal, to: view.topAnchor) descLabel .setAnchor(\.widthAnchor, .equal, to: view.widthAnchor, multiplier: 0.2) .setAnchor(\.heightAnchor, .greaterThanOrEqual, to: view.heightAnchor) .setAnchor(\.centerXAnchor, .equal, to: view.centerXAnchor) .setAnchor(\.centerYAnchor, .equal, to: view.centerYAnchor)

Slide 78

Slide 78 text

Conclusion

Slide 79

Slide 79 text

• Swift KeyPath is a defer way to get / set property with Type safety

Slide 80

Slide 80 text

• Swift KeyPath is a defer way to get / set property with Type safety • Better Key-Value Coding for Swift (Compared with #KeyPath( ) )

Slide 81

Slide 81 text

• Swift KeyPath is a defer way to get / set property with Type safety • Better Key-Value Coding for Swift • Create interface more flexible than ever (Compared with #KeyPath( ) )

Slide 82

Slide 82 text

• Swift KeyPath is a defer way to get / set property with Type safety • Better Key-Value Coding for Swift • Create interface more flexible than ever (Compared with #KeyPath( ) ) • Easy to implement Fluent Interface!

Slide 83

Slide 83 text

WWDC17 - What's New in Foundation https://developer.apple.com/videos/play/wwdc2017/212/ Proposal: SE-0161 https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md Proposal SE-0252 https://github.com/apple/swift-evolution/blob/master/proposals/0252-keypath- dynamic-member-lookup.md Introduction to Swift Keypaths - Benedikt Terhechte
 http://www.swifttube.co/video/the-underestimated-power-of-keypaths The underestimated power of KeyPaths - Vincent Pradeilles http://www.swifttube.co/video/the-underestimated-power-of-keypaths FluentInterface https://martinfowler.com/bliki/FluentInterface.html Builder, Fluent Interface and classic builder https://medium.com/@sawomirkowalski/design-patterns-builder-fluent-interface- and-classic-builder-d16ad3e98f6c Reference

Slide 84

Slide 84 text

Proposal SE-0249 https://github.com/apple/swift-evolution/blob/master/proposals/0249-key-path-literal- function-expressions.md KeyPathKit - Vincent Pradeilles https://github.com/vincent-pradeilles/KeyPathKit The power of key paths in Swift - Sundell https://www.swiftbysundell.com/articles/the-power-of-key-paths-in-swift/ Using Swift KeyPaths for Beautiful User Preferences - Kane https://edit.theappbusiness.com/using-swift-keypaths-for-beautiful-user- preferences-c83c2f7ea7be More Information

Slide 85

Slide 85 text

活動時間: 每週⼆ pm: 7:30 ~ 10:00 活動地點:建國北路⼆段1號5樓 ( Meet.jobs 辦公室 ) (感謝Meet.jobs 提供場地)

Slide 86

Slide 86 text

Thanks for your attention.