Upgrade to Pro — share decks privately, control downloads, hide ads and more …

KeyPathの便利な使い道を研究する

 KeyPathの便利な使い道を研究する

KeyPathの便利な使い道を研究する
- KVO以外にKeyPathの活用方法はあるか
- NSPredicateの改善
- Lens?

Kishikawa Katsumi

August 09, 2017
Tweet

More Decks by Kishikawa Katsumi

Other Decks in Programming

Transcript

  1. KeyPath in Swift 3 [email protected] #keyPath(Book.title) #keyPath(Book.price) #keyPath(Book.author.name) @objcMembers class

    Book { var isbn: String var title: String var price: Int var author: Author ... }
  2. KeyPath in Swift 3 [email protected] let titleKeyPath: String = #keyPath(Book.title)

    let priceKeyPath: String = #keyPath(Book.price) let authorNameKeyPath: String = #keyPath(Book.author.name)
  3. KeyPath in Swift 3 [email protected] let title: Any? = book.value(forKeyPath:

    #keyPath(Book.title)) let price: Any? = book.value(forKeyPath: #keyPath(Book.price)) let authorName: Any? = book.value(forKeyPath: #keyPath(Book.author.name))
  4. Smart KeyPath in Swift 4 [email protected] struct Book { let

    isbn: String let title: String let price: Int let author: Author } struct Author { let name: String let age: Int? let birthday: Date? }
  5. Smart KeyPath in Swift 4 [email protected] struct Book { let

    isbn: String let title: String let price: Int let author: Author } struct Author { let name: String let age: Int? let birthday: Date? } let book = Book(isbn: "978-4774188485", title: "Realmೖ໳", price: 3110, author: Author(name: "ੁݪ ༞", age: nil, birthday: nil))
  6. Smart KeyPath in Swift 4 [email protected] let book = Book(isbn:

    "978-4774188485", title: "Realmೖ໳", price: 3110, author: Author(name: "ੁݪ ༞", age: nil, birthday: nil)) let title = book[keyPath: \Book.title] // => "Realmೖ໳" let price = book[keyPath: \Book.price] // => 3110 let authorName = book[keyPath: \Book.author.name] // "ੁݪ ༞"
  7. Smart KeyPath in Swift 4 [email protected] let title = book[keyPath:

    \Book.title] // => "Realmೖ໳" let price = book[keyPath: \Book.price] // => 3110 let authorName = book[keyPath: \Book.author.name] // "ੁݪ ༞" \Book.title \Book.price \Book.author.name
  8. [email protected] let titleKeyPath: KeyPath<Book, String> = \Book.title let priceKeyPath: KeyPath<Book,

    Int> = \Book.price let authorNameKeyPath: KeyPath<Book, String> = \Book.author.name Smart KeyPath in Swift 4
  9. [email protected] let title: String = book[keyPath: \Book.title] let price: Int

    = book[keyPath: \Book.price] let authorName: String = book[keyPath: \Book.author.name] Smart KeyPath in Swift 4
  10. ϓϩύςΟΞΫηε [email protected] let title = book.title // => "Realmೖ໳" let

    price = book.price // => 3110 let authorName = book.author.name // "ੁݪ ༞" ΄ͱΜͲͷέʔε͸ϓϩύςΟΞΫηεͷํ͕؆୯͔ͭద͍ͯ͠ΔͷͰɺ KVOͳͲݹ͍࢓૊Έͷޓ׵ੑҎ֎ͷ໨తͰΘ͟Θ͟KeyPathΛ࢖͏৔໘͸গͳ͍ ̍ͭ͸NSPredicateͳͲϓϩύςΟͷ৘ใΛจࣈྻͰ૊Έཱ͍ͯͯΔΑ͏ͳ APIΛ҆શʹॻ͚ΔΑ͏ʹ͢Δ͜ͱ͕Ͱ͖ͦ͏ɻ ଞʹศརͳར༻ํ๏͸͋ΔͩΖ͏͔ʁ
  11. Type-unsafe KVO in Swift 3 [email protected] book.addObserver(self, forKeyPath: #keyPath(Book.price), options:

    [], context: nil) override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "price" { if let book = object as? Book { if book.price < 2000 { } } } }
  12. Type-safe KVO [email protected] book.observe(\Book.price) { (observed, change) in if (observed.price

    < 2000) { } } KVOΛੵۃతʹ࢖͏͜ͱ͸ͳ͍͚ΕͲɺiOSϓϩάϥϛϯάͰ͸·ͩඞཁʹͳΔɻ ࠓճͷվળʹΑΓΩϟετ͕ෆཁʹͳΔͳͲɺSwiit͔Βඇৗʹѻ͍΍͘͢ͳͬͨɻ
  13. Type-unsafe Query in Swift 4 [email protected] NSPredicate(format: "name == %@",

    "Katsumi") NSPredicate(format: "age > %@", 20) NSPredicate(format: "nsme == %@", "Katsumi") // Runtime Error NSPredicate(format: "name > %@", 20) // Runtime Error NSPredicateͳͲϓϩύςΟͷ৘ใΛจࣈྻͰࢦఆ͢Δඞཁ͕͋ΔAPIΛ KeyPathΛ࢖ͬͯΑΓ҆શʹॻ͚Δ
  14. Type-safe Query [email protected] Query(Person.self).filter(\Person.name == "Katsumi") Query(Person.self).filter(\Person.age > 20) Query(Person.self).filter(\Person.name

    > 20) // Compile error Query(Person.self).filter(\Dog.name == "John") // Compile error KeyPath͸ίϯύΠϥͷνΣοΫ͕ಇ͘ͷͰɺTypo͕ͳ͘ͳΔʢίʔυิ׬΋Մೳʣ ͜ͱ͸΋ͪΖΜɺܕνΣοΫ΋ػೳ͢Δɻ
  15. Type-safe Query [email protected] ... public func == <ManagedObject: NSManagedObject>(lhs: KeyPath<ManagedObject,

    String?>, rhs: String) -> BasicPredicate<ManagedObject> { return BasicPredicate<ManagedObject>(format: "%K == %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs as NSString]) } public func != <ManagedObject: NSManagedObject>(lhs: KeyPath<ManagedObject, String?>, rhs: String) -> BasicPredicate<ManagedObject> { return BasicPredicate<ManagedObject>(format: "%K != %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs as NSString]) } public func < <ManagedObject: NSManagedObject, Property: Numeric>(lhs: KeyPath<ManagedObject, Property>, rhs: Property) -> BasicPredicate<ManagedObject> { return BasicPredicate<ManagedObject>(format: "%K < %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs as! NSNumber]) } public func > <ManagedObject: NSManagedObject, Property: Numeric>(lhs: KeyPath<ManagedObject, Property>, rhs: Property) -> BasicPredicate<ManagedObject> { return BasicPredicate<ManagedObject>(format: "%K > %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs as! NSNumber]) } ...
  16. Type-safe Query (for Realm) [email protected] @objcMembers class Dog: Object {

    dynamic var name = "" dynamic var age = 0 dynamic var owner: Person! let owners = LinkingObjects(fromType: Person.self, property: "dogs") } @objcMembers class Person: Object { dynamic var name = "" dynamic var age = 0 dynamic var birthdate = Date() let dogs = List<Dog>() }
  17. Type-safe Query (for Realm) [email protected] realm.objects(Person.self) .filter { \Person.name ==

    "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 } realm.objects(Dog.self) .filter { \Dog.name < 2 } // ❌ realm.objects(Dog.self) .filter { \Person.age < 2 } // ❌
  18. t>(lhs: KeyPath<RealmObject, String>, rhs: String) -> ect>(format: "%K == %@",

    arguments: [lhs._kvcKeyPathString! as NSString, rhs , RealmProperty: Numeric>(lhs: KeyPath<RealmObject, RealmProperty>, rhs: ealmObject> { ect>(format: "%K < %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs , RealmProperty: Numeric>(lhs: KeyPath<RealmObject, RealmProperty>, rhs: ealmObject> { ect>(format: "%K > %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs [email protected] ݱࡏKeyPathͷจࣈྻදݱ͸ඇެ։ͳͷͰ࢖͑ΔΑ͏ʹͯ͠΄͍͠ͱ͍͏ϦΫΤετΛ ग़͍ͯ͠Δ
  19. ΦϒδΣΫτࢦ޲ݴޠͳΒ... [email protected] let title = book.title let price = book.price

    let authorName = book.author.name book.author.name = "..." Lens͸ͦ΋ͦ΋ؔ਺ܕϓϩάϥϛϯάʹ͓͍ͯΦϒδΣΫτࢦ޲ݴޠͷػೳΛ ΤϛϡϨʔτ͢Δ΋ͷͳͷͰɺSwiftʹಋೖͯ͠ศརͳ఺͸͋ΔͷͩΖ͏͔ʁ
  20. Lenses in Swift [email protected] struct BoardingPass { let plane: Plane

    let gate: Gate let departureDate: Date let arrivalDate: Date } struct Plane { let model: String let freeSeats: Int let takenSeats: Int let status: Status } struct Gate { let number: Int let letter: String } enum Status { case early case onTime case late } // https://github.com/typelift/Focus
  21. Lenses in Swift [email protected] let plane = Plane(model: "SpaceX Raptor",

    freeSeats: 4, takenSeats: 0, status: .onTime) let gate = Gate(number: 1, letter: "A") let pass = BoardingPass(plane: plane, gate: gate, departureDate: Date.distantFuture, arrivalDate: Date.distantFuture) // https://github.com/typelift/Focus
  22. Lenses in Swift [email protected] let newPlane = Plane(model: pass.plane.model, freeSeats:

    pass.plane.freeSeats, takenSeats: pass.plane.takenSeats, status: .late) let newPass = BoardingPass(plane: newPlane, gate: pass.gate, departureDate: pass.departureDate, arrivalDate: pass.arrivalDate) ▿ BoardingPass ▿ plane: Plane - model: "SpaceX Raptor" - freeSeats: 4 - takenSeats: 0 - status: Status.late ▿ gate: Gate - number: 1 - letter: "A" - departureDate: 4001-01-01 00:00:00 +0000 - arrivalDate: 4001-01-01 00:00:00 +0000
  23. Lenses in Swift [email protected] var pass = BoardingPass(plane: plane, gate:

    gate, departureDate: Date.distantFuture, arrivalDate: Date.distantFuture) pass.plane.status = .late
  24. Lenses in Swift [email protected] let newPass = pass |> (\BoardingPass.plane.status)

    .~ .late Lens͸ͦ΋ͦ΋ؔ਺ܕϓϩάϥϛϯάʹ͓͍ͯΦϒδΣΫτࢦ޲ݴޠͷػೳΛ ΤϛϡϨʔτ͢Δ΋ͷͳͷͰɺSwiftʹಋೖͯ͠ศརͳ఺͸͋ΔͷͩΖ͏͔ʁ
  25. Lenses in Swift [email protected] let newPass = pass |> (\BoardingPass.plane.status)

    .~ .late ▿ BoardingPass ▿ plane: Plane - model: "SpaceX Raptor" - freeSeats: 4 - takenSeats: 0 - status: Status.late ▿ gate: Gate - number: 1 - letter: "A" - departureDate: 4001-01-01 00:00:00 +0000 - arrivalDate: 4001-01-01 00:00:00 +0000
  26. LensͰςετΛΘ͔Γ΍͘͢ [email protected] func testUser() { let templateUser = User(name: "Katsumi

    Kishikawa", age: 37, sns: SNS(twitter: "k_katsumi", facebook: nil)) XCTContext.runActivity(named: "User is student") { (activity) in let studentUser = User(name: templateUser.name, age: 37, sns: templateUser.sns) ... XCTAssert(...) } XCTContext.runActivity(named: "...") { (activity) in let silverUser = User(name: templateUser.name, age: 65, sns: templateUser.sns) ... XCTAssert(...) } }
  27. LensͰςετΛΘ͔Γ΍͘͢ [email protected] func testUser() { let templateUser = User(name: "Katsumi

    Kishikawa", age: 37, sns: SNS(twitter: "k_katsumi", facebook: nil)) XCTContext.runActivity(named: "User is student") { (activity) in let studentUser = user |> (\User.age) .~ 15 ... XCTAssert(...) } XCTContext.runActivity(named: "...") { (activity) in let silverUser = user |> (\User.age) .~ 65 ... XCTAssert(...) } } LensʹΑͬͯಛఆͷϓϩύςΟ͚ͩΛมߋͰ͖ΔͷͰ ೥ྸʹϑΥʔΧε͍ͯ͠Δ͜ͱ͕ΑΓΘ͔Γ΍͍͢ʁ