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

Realm meetup Japan #23

Realm meetup Japan #23

Realm Objective-C/Swift 2.8.3
Swift 4
- @objc/@objcMembers
- Codable (Decodable/Encodable)
- KeyPath
- Type-safe Query

Kishikawa Katsumi

June 29, 2017
Tweet

More Decks by Kishikawa Katsumi

Other Decks in Programming

Transcript

  1. Recent Updates • Realm Objective-C/Swift 2.8.0 (was 2.7.0) • Realm

    Java 3.4.0 (was 3.3.1) • Realm JS 1.8.2 (was 1.3.1) • Realm .NET 1.5.0 (1.4.0) [email protected]
  2. Model Defenitions (Swift) class Person: Object { @objc dynamic var

    name = "" @objc dynamic var age = 0 let dogs = LinkingObjects(fromType: Dog.self, property: "owner") } [email protected] @objcMembers class Dog: Object, Codable { dynamic var name = "" dynamic var age = 0 dynamic var owner: Person? }
  3. Model Definitions (Swift) class Person: Object { @objc dynamic var

    name = "" @objc dynamic var age = 0 let dogs = LinkingObjects(fromType: Dog.self, property: "owner") } [email protected] @objcMembers class Dog: Object, Codable { dynamic var name = "" dynamic var age = 0 dynamic var owner: Person? }
  4. Codable (Decodable & Encodable) • SwiftωΠςΟϒͷσʔλߏ଄ʢClass, Structure, EnumerationʣΛγ ϦΞϥΠζՄೳͳσʔλʹ૬ޓʹม׵͢Δ࢓૊ΈΛఏڙ͢Δ •

    DecodableʹରԠ͍ͯ͠ΔͱγϦΞϥΠζՄೳͳσʔλʢJSONͳ Ͳʣ͔ΒΦϒδΣΫτ΁ͷม׵͕ՄೳʹͳΔɻEncodable͸ͦͷٯ ʢStructure => JSONͳͲʣ • ίϯύΠϥͷࢧԉ͕ड͚ΒΕΔʢϝιου΍Ωʔͷࣗಈੜ੒ͳͲʣ ͷͰγϯϓϧʹॻ͚Δʢ৔߹͕͋Δʣ [email protected]
  5. Codable (Decodable & Encodable) [email protected] struct Person: Codable { ...

    } let person = try! JSONDecoder().decode(Person.self, from: json) let json = try! JSONEncoder().encode(person)
  6. Codable (Decodable & Encodable) [email protected] struct Person: Codable { private

    enum CodingKeys : String, CodingKey { case fullName = “full_name" ... } ... }
  7. Codable (Decodable & Encodable) [email protected] class Person: Object, Codable {

    @objc dynamic var name = "" @objc dynamic var age = 0 let dogs = LinkingObjects(fromType: Dog.self, property: "owner") private enum CodingKeys : String, CodingKey { case name case age } required convenience init(from decoder: Decoder) throws { self.init() let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(String.self, forKey: .name) age = try container.decode(Int.self, forKey: .age) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name) try container.encode(age, forKey: .age) } }
  8. Codable (Decodable & Encodable) [email protected] @objcMembers class Dog: Object, Codable

    { dynamic var name = "" dynamic var age = 0 dynamic var owner: Person? private enum CodingKeys : String, CodingKey { case name case age case owner } required convenience init(from decoder: Decoder) throws { self.init() let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(String.self, forKey: .name) age = try container.decode(Int.self, forKey: .age) owner = try container.decode(Person?.self, forKey: .owner) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name) try container.encode(age, forKey: .age) try container.encode(owner, forKey: .owner) } }
  9. Decodable [email protected] class Person: Object, Decodable { @objc dynamic var

    name = "" @objc dynamic var age = 0 let dogs = LinkingObjects(fromType: Dog.self, property: "owner") private enum CodingKeys : String, CodingKey { case name case age } required convenience init(from decoder: Decoder) throws { self.init() let container = try decoder.container(keyedBy: CodingKeys.self) name = try container.decode(String.self, forKey: .name) age = try container.decode(Int.self, forKey: .age) } }
  10. Encodable [email protected] class Person: Object, Encodable { @objc dynamic var

    name = "" @objc dynamic var age = 0 let dogs = LinkingObjects(fromType: Dog.self, property: "owner") private enum CodingKeys : String, CodingKey { case name case age } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name) try container.encode(age, forKey: .age) } }
  11. Example [email protected] let json = """ { "name": "Hachi", "age":

    4, "owner": { "name": "Katsumi Kishikawa", "age": 36 } } """.data(using: .utf8)! let dog = try! JSONDecoder().decode(Dog.self, from: json) print(dog) let realm = try! Realm() try! realm.write { realm.add(dog) } print(realm.objects(Dog.self))
  12. Example (GitHub API) [email protected] { "total_count": 410867, "incomplete_results": false, "items":

    [ { "id": 1828795, "name": "AFNetworking", "full_name": "AFNetworking/AFNetworking", "owner": { "login": "AFNetworking", "id": 1181541, "avatar_url": "https://avatars1.githubusercontent.com/u/1181541?v=3", "gravatar_id": "", "url": "https://api.github.com/users/AFNetworking", "html_url": "https://github.com/AFNetworking", "followers_url": "https://api.github.com/users/AFNetworking/followers", "following_url": "https://api.github.com/users/AFNetworking/following{/other_user}", "gists_url": "https://api.github.com/users/AFNetworking/gists{/gist_id}", "starred_url": "https://api.github.com/users/AFNetworking/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/AFNetworking/subscriptions", "organizations_url": "https://api.github.com/users/AFNetworking/orgs", "repos_url": "https://api.github.com/users/AFNetworking/repos", "events_url": "https://api.github.com/users/AFNetworking/events{/privacy}", "received_events_url": "https://api.github.com/users/AFNetworking/received_events", "type": "Organization", "site_admin": false }, ... ] } curl -i https://api.github.com/search/repositories?q=language:objc
  13. [email protected] @objcMembers class Repository: Object { dynamic var identifier =

    0 dynamic var name = "" dynamic var fullName = "" dynamic var isPrivate = false dynamic var htmlUrl = "" dynamic var createdAt = Date() dynamic var updatedAt = Date() dynamic var owner: User? private enum CodingKeys : String, CodingKey { case identifier = "id" case name case fullName = "full_name" case isPrivate = "private" case htmlUrl = "html_url" case createdAt = "created_at" case updatedAt = "updated_at" case owner } required convenience init(from decoder: Decoder) throws { self.init() let container = try decoder.container(keyedBy: CodingKeys.self) identifier = try container.decode(Int.self, forKey: .identifier) name = try container.decode(String.self, forKey: .name) fullName = try container.decode(String.self, forKey: .fullName) isPrivate = try container.decode(Bool.self, forKey: .isPrivate) htmlUrl = try container.decode(String.self, forKey: .htmlUrl) createdAt = try container.decode(Date.self, forKey: .createdAt) updatedAt = try container.decode(Date.self, forKey: .updatedAt) owner = try container.decode(User.self, forKey: .owner) } }
  14. [email protected] @objcMembers class User: Object, Decodable { dynamic var login

    = "" dynamic var identifier = 0 dynamic var type = "" dynamic var avatarUrl = "" private enum CodingKeys : String, CodingKey { case login case identifier = "id" case type case avatarUrl = "avatar_url" } required convenience init(from decoder: Decoder) throws { self.init() let container = try decoder.container(keyedBy: CodingKeys.self) login = try container.decode(String.self, forKey: .login) identifier = try container.decode(Int.self, forKey: .identifier) type = try container.decode(String.self, forKey: .type) avatarUrl = try container.decode(String.self, forKey: .avatarUrl) } }
  15. [email protected] struct RepositoryResponse : Decodable { let totalCount: Int let

    incompleteResults: Bool let items: [Repository] private enum CodingKeys : String, CodingKey { case totalCount = "total_count" case incompleteResults = "incomplete_results" case items } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) totalCount = try container.decode(Int.self, forKey: .totalCount) incompleteResults = try container.decode(Bool.self, forKey: .incompleteResults) items = try container.decode([Repository].self, forKey: .items) } }
  16. [email protected] var components = URLComponents(string: "https://api.github.com/search/repositories")! components.queryItems = [ URLQueryItem(name:

    "q", value: "language:objc"), URLQueryItem(name: "sort", value: "stars"), URLQueryItem(name: "order", value: "desc") ] URLSession.shared.dataTask(with: URLRequest(url: components.url!)) { data, response, error in guard let data = data else { return } do { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 let repositoryResponse = try decoder.decode(RepositoryResponse.self, from: data) let repositories = repositoryResponse.items ...
  17. KeyPath [email protected] @objcMembers class Dog: Object { dynamic var name

    = "" dynamic var age = 0 dynamic var owner: Person! } \Dog.age => KeyPath<Dog, Int> \Dog.name => KeyPath<Dog, String> \Dog.owner.name => KeyPath<Dog, String>
  18. Type-safe Query [email protected] extension Results { public func filter<P: Predicate>(_

    closure: () -> P) -> Results<T> where P.ObjectType == T { return filter(closure().predicate) } } public func == <RealmObject: Object>(lhs: KeyPath<RealmObject, String>, rhs: String) -> BasicPredicate<RealmObject> { return BasicPredicate<RealmObject>(format: "%K == %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs as NSString]) } public func < <RealmObject: Object, RealmProperty: Numeric>(lhs: KeyPath<RealmObject, RealmProperty>, rhs: RealmProperty) -> BasicPredicate<RealmObject> { return BasicPredicate<RealmObject>(format: "%K < %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs as! NSNumber]) } public func > <RealmObject: Object, RealmProperty: Numeric>(lhs: KeyPath<RealmObject, RealmProperty>, rhs: RealmProperty) -> BasicPredicate<RealmObject> { return BasicPredicate<RealmObject>(format: "%K > %@", arguments: [lhs._kvcKeyPathString! as NSString, rhs as! NSNumber]) } ...
  19. Type-safe Query [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>() }
  20. Type-safe Query [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 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  21. Type-safe Query [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 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  22. Type-safe Query [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 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  23. Type-safe Query [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 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  24. Type-safe Query [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 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  25. Type-safe Query [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 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  26. Type-safe Query [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 } // ❌ realm.objects(Dog.self) .filter { \Dog.age < 2 && \Dog.owner.name == "Katsumi" } realm.objects(Dog.self) .filter { \Dog.age < 2 && \Person.name == "Katsumi" } // ❌
  27. 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]