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

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

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

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

9bf923e39671cde83584e3e926296c13?s=128

Kishikawa Katsumi

August 09, 2017
Tweet

More Decks by Kishikawa Katsumi

Other Decks in Programming

Transcript

  1. KeyPathͷศརͳ࢖͍ಓΛ ݚڀ͢Δ Otemachi.swift x Kyobashi.swift #01 Katsumi Kishikawa kk@realm.io

  2. Katsumi Kishikawa Realm Inc. kk@realm.io

  3. KeyPathͱ͸ kk@realm.io

  4. KeyPath in Swift 3 kk@realm.io #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 ... }
  5. KeyPath in Swift 3 kk@realm.io let titleKeyPath: String = #keyPath(Book.title)

    let priceKeyPath: String = #keyPath(Book.price) let authorNameKeyPath: String = #keyPath(Book.author.name)
  6. KeyPath in Swift 3 kk@realm.io 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))
  7. Smart KeyPath in Swift 4 kk@realm.io 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? }
  8. Smart KeyPath in Swift 4 kk@realm.io 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))
  9. Smart KeyPath in Swift 4 kk@realm.io 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] // "ੁݪ ༞"
  10. Smart KeyPath in Swift 4 kk@realm.io 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
  11. kk@realm.io 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
  12. kk@realm.io 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
  13. ϓϩύςΟΞΫηε kk@realm.io let title = book.title // => "Realmೖ໳" let

    price = book.price // => 3110 let authorName = book.author.name // "ੁݪ ༞" ΄ͱΜͲͷέʔε͸ϓϩύςΟΞΫηεͷํ͕؆୯͔ͭద͍ͯ͠ΔͷͰɺ KVOͳͲݹ͍࢓૊Έͷޓ׵ੑҎ֎ͷ໨తͰΘ͟Θ͟KeyPathΛ࢖͏৔໘͸গͳ͍ ̍ͭ͸NSPredicateͳͲϓϩύςΟͷ৘ใΛจࣈྻͰ૊Έཱ͍ͯͯΔΑ͏ͳ APIΛ҆શʹॻ͚ΔΑ͏ʹ͢Δ͜ͱ͕Ͱ͖ͦ͏ɻ ଞʹศརͳར༻ํ๏͸͋ΔͩΖ͏͔ʁ
  14. Type-safe KVO kk@realm.io

  15. Type-unsafe KVO in Swift 3 kk@realm.io 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 { } } } }
  16. Type-safe KVO kk@realm.io book.observe(\Book.price) { (observed, change) in if (observed.price

    < 2000) { } } KVOΛੵۃతʹ࢖͏͜ͱ͸ͳ͍͚ΕͲɺiOSϓϩάϥϛϯάͰ͸·ͩඞཁʹͳΔɻ ࠓճͷվળʹΑΓΩϟετ͕ෆཁʹͳΔͳͲɺSwiit͔Βඇৗʹѻ͍΍͘͢ͳͬͨɻ
  17. Type-safe Query (Experimental) kk@realm.io

  18. Type-unsafe Query in Swift 4 kk@realm.io NSPredicate(format: "name == %@",

    "Katsumi") NSPredicate(format: "age > %@", 20) NSPredicate(format: "nsme == %@", "Katsumi") // Runtime Error NSPredicate(format: "name > %@", 20) // Runtime Error NSPredicateͳͲϓϩύςΟͷ৘ใΛจࣈྻͰࢦఆ͢Δඞཁ͕͋ΔAPIΛ KeyPathΛ࢖ͬͯΑΓ҆શʹॻ͚Δ
  19. Type-safe Query kk@realm.io 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͕ͳ͘ͳΔʢίʔυิ׬΋Մೳʣ ͜ͱ͸΋ͪΖΜɺܕνΣοΫ΋ػೳ͢Δɻ
  20. Type-safe Query kk@realm.io ... 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]) } ...
  21. Type-safe Query (for Realm) kk@realm.io @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>() }
  22. Type-safe Query (for Realm) kk@realm.io 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 } // ❌
  23. Proof of Concept https://github.com/kishikawakatsumi/Kuery kk@realm.io

  24. 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 kk@realm.io ݱࡏKeyPathͷจࣈྻදݱ͸ඇެ։ͳͷͰ࢖͑ΔΑ͏ʹͯ͠΄͍͠ͱ͍͏ϦΫΤετΛ ग़͍ͯ͠Δ
  25. Feature Request https://bugs.swift.org/browse/SR-5220 kk@realm.io

  26. kk@realm.io

  27. Lenses in Swift kk@realm.io

  28. Lensͱ͸ kk@realm.io • ؔ਺ܕϓϩάϥϛϯάʹ͓͍ͯGetter/SetterΛந৅Խ͢ΔσβΠϯύλʔϯ • ෆมੑΛอͬͨ··ෳࡶͳσʔλߏ଄΁ͷ༰қͳΞΫηεΛఏڙ͢Δ

  29. ΦϒδΣΫτࢦ޲ݴޠͳΒ... kk@realm.io let title = book.title let price = book.price

    let authorName = book.author.name book.author.name = "..." Lens͸ͦ΋ͦ΋ؔ਺ܕϓϩάϥϛϯάʹ͓͍ͯΦϒδΣΫτࢦ޲ݴޠͷػೳΛ ΤϛϡϨʔτ͢Δ΋ͷͳͷͰɺSwiftʹಋೖͯ͠ศརͳ఺͸͋ΔͷͩΖ͏͔ʁ
  30. Lenses in Swift kk@realm.io 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
  31. Lenses in Swift kk@realm.io 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
  32. Lenses in Swift kk@realm.io 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
  33. Lenses in Swift kk@realm.io var pass = BoardingPass(plane: plane, gate:

    gate, departureDate: Date.distantFuture, arrivalDate: Date.distantFuture) pass.plane.status = .late
  34. Lenses in Swift kk@realm.io let newPass = pass |> (\BoardingPass.plane.status)

    .~ .late Lens͸ͦ΋ͦ΋ؔ਺ܕϓϩάϥϛϯάʹ͓͍ͯΦϒδΣΫτࢦ޲ݴޠͷػೳΛ ΤϛϡϨʔτ͢Δ΋ͷͳͷͰɺSwiftʹಋೖͯ͠ศརͳ఺͸͋ΔͷͩΖ͏͔ʁ
  35. Lenses in Swift kk@realm.io 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
  36. LensͰςετΛΘ͔Γ΍͘͢ kk@realm.io 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(...) } }
  37. LensͰςετΛΘ͔Γ΍͘͢ kk@realm.io 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ʹΑͬͯಛఆͷϓϩύςΟ͚ͩΛมߋͰ͖ΔͷͰ ೥ྸʹϑΥʔΧε͍ͯ͠Δ͜ͱ͕ΑΓΘ͔Γ΍͍͢ʁ
  38. LensͰςετΛΘ͔Γ΍͘͢ kk@realm.io Quick + LensΛ༻͍ͨςετͷߏ଄Խ https://speakerdeck.com/nonchalant/quick-plus-lenswoyong-itatesutofalsegou-zao-hua kickstarter/ios-oss https://github.com/kickstarter/ios-oss/blob/3e112451a306c27319704e5a67d4edea7ed66014/Kickstarter-iOS/Views/Controllers/ActivitiesViewControllerTests.swift#L9-L15

  39. Questions? Katsumi Kishikawa kk@realm.io www.realm.io @k_katsumi kk@realm.io