Slide 1

Slide 1 text

Codable Tipsू #potatotips 45 ాத ୡ໵ (@tattn)

Slide 2

Slide 2 text

• Yahoo!৐׵Ҋ಺ • iOSΞϓϦΤϯδχΞ • GitHub: @tattn • Qiita: @tattn • Twitter: @tanakasan2525 ాத ୡ໵ (@tattn)

Slide 3

Slide 3 text

͓஌Βͤ  Yahoo! JAPAN Tech Advent Calendar 2017
 https://qiita.com/advent-calendar/2017/yahoojapan-tech ↑11೔ʹCI/CDͷ࿩Λॻ͖·͢
 ↓ଞʹ΋iosͱswiftͷΞυϕϯτΧϨϯμʔʹࢀՃͯ͠·͢ https://qiita.com/advent-calendar/2017/swift https://qiita.com/advent-calendar/2017/ios

Slide 4

Slide 4 text

Codableͱ͸

Slide 5

Slide 5 text

ܕͷΤϯίʔυͱσίʔυΛαϙʔτ͢Δػೳ  struct User: Codable { let id: String let name: String } let encoder = JSONEncoder() let user = User(id: "abc", name: "Taro") let data = try! encoder.encode(user) let json = try! JSONSerialization.jsonObject( with: data, options: .allowFragments ) print(json) // {"id": "abc", "name": "Taro"}

Slide 6

Slide 6 text

ܕͷΤϯίʔυͱσίʔυΛαϙʔτ͢Δػೳ  struct User: Codable { let id: String let name: String } let json = """ {"id": "abc", "name": "Taro"} """.data(using: .utf8)! let decoder = JSONDecoder() let user = try! decoder.decode(User.self, from: data) print(user) // User(id: "abc", name: "Taro")

Slide 7

Slide 7 text

ίίΒ΁ΜΛΈΔͱΘ͔Γ·͢ Swift 4 Codable by Yasuhiro Inami Swift 4͔Β࣮૷͞ΕΔCodable/codable_in_foundation_with_swift4 by fromkk Codableͷinit(from:)ΛͲ͏ॻ͔͘ by takasek Swift4.0 Ͱ௥Ճ͞ΕΔ Codable - Qiita Codableʹ͍ͭͯ৭ʑ·ͱΊͨ[Swift4] - Qiita IUUQTTQFBLFSEFDLDPNJOBNJZTXJGUDPEBCMF IUUQTTQFBLFSEFDLDPNGSPNLLDPEBCMFJOGPVOEBUJPOXJUITXJGU IUUQTRJJUBDPNIBOBXBUJUFNTG⒎GBDBFBFB IUUQTRJJUBDPN@IBGJUFNTCGBBEFBFGGE IUUQTTQFBLFSEFDLDPNUBLBTFLOVNCFSXXEDFCJTV IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOGPVOEBUJPOBSDIJWFT@BOE@TFSJBMJ[BUJPOFODPEJOH@BOE@EFDPEJOH@DVTUPN@UZQFT Encoding and Decoding Custom Types

Slide 8

Slide 8 text

·ͣ͸͜ΕΛ఻͍͑ͨ

Slide 9

Slide 9 text

(´ɾωɾʆ) Swift 3.2Ͱ΋࢖͑ΔΑ (Swift 4͔ΒͷػೳͩͱࢥͬͯΔਓଟ͍ؾ͕͢Δ)

Slide 10

Slide 10 text

Swift 3.2Ͱ΋࢖͑ΔΑ Xcode 9.0Ҏ্Ͱ͋Ε͹ɺ࢖༻Մೳ Swift 4ʹ·্ͩ͛ΒΕͳ͍൵͍͠ঢ়گͰ΋࢖͑Δ  Swift 4ʹ্͛ΒΕΔ࣌Λ଴ͭඞཁ͸ͳ͍Α

Slide 11

Slide 11 text

ݟམͱ͕ͪ͠ͳSwift 3.2Ͱ࢖͑Δػೳ ෳ਺ߦจࣈྻϦςϥϧ  let str = """ Θʔ͍ʋ(ʆ▽´)/ ෳ਺ߦͷจࣈྻϦςϥϧ͕ ॻ͚ΔΑʔʘʘ\\٩( 'ω' )و //ʗʗ """ class Hoge: NSObject { dynamic var value = 0 } let hoge = Hoge() let token = hoge.observe(\.value) { (object, change) in print(object.value) } KeyPathϦςϥϧͱΫϩʔδϟܕKVO

Slide 12

Slide 12 text

CodableͷTipsू

Slide 13

Slide 13 text

Key໊͕Swifty͡Όͳ͍

Slide 14

Slide 14 text

JSONͷkeyͷ಄͕େจࣈ(´ɾωɾʆ)  { "Id": 100, "Name": "Taro" } struct Model: Decodable { let id: Int let name: String } ←͜͏͍͚ͨ͠Ͳ
 ɹͰ͖ͳ͍ɻɻ (ϓϩύςΟ໊ͱkey໊͕Ұக͍ͯ͠ͳ͍ͨΊ)

Slide 15

Slide 15 text

ී௨ͷରԠํ๏  struct Model: Decodable { let id: Int let name: String enum CodingKeys: String, CodingKey { case id = "Id" case name = "Name" } } ←CodingKeysͰ
 ɹϓϩύςΟ໊ͱରԠ෇͚ { "Id": 100, "Name": "Taro" }

Slide 16

Slide 16 text

ී௨ͷରԠํ๏  struct Model: Decodable { let id: Int let name: String enum CodingKeys: String, CodingKey { case id = "Id" case name = "Name" } } ←typoͦ͠͏
 ɹ৑௕ͳҹ৅ { "Id": 100, "Name": "Taro" }

Slide 17

Slide 17 text

ͦ͜Ͱ  Upper Camel CaseΛkeyͱ͢Δ
 ϓϩτίϧΛ࡞Δ

Slide 18

Slide 18 text

CodingKey ϓϩτίϧ  public protocol CodingKey { public var stringValue: String { get } public init?(stringValue: String) public var intValue: Int? { get } public init?(intValue: Int) } CodingKeyʹ͸key໊ͱϓϩύςΟ໊ͷରԠΛ
 ΧελϚΠζͰ͖ΔI/F͕༻ҙ͞Ε͍ͯΔ

Slide 19

Slide 19 text

UCCͳkeyΛѻ͏protocolΛ࡞Δ  protocol UpperCamelCaseCodingKey: CodingKey {} extension UpperCamelCaseCodingKey where Self: RawRepresentable, Self.RawValue == String { var stringValue: String { return String(rawValue.prefix(1).capitalized + rawValue[rawValue.index(after: rawValue.startIndex)...]) } } keyͷઌ಄Λখจࣈʹม׵ͯ͠ѻ͏

Slide 20

Slide 20 text

struct Model: Decodable { let id: Int let name: String enum CodingKeys: String, UpperCamelCaseCodingKey { case id case name } } UCCCodingKeyΛ࢖͏  ←ܕͰม׵Λදݱ
 ɹtypo͠ͳ͍ { "Id": 100, "Name": "Taro" }

Slide 21

Slide 21 text

ͪͳΈʹ  key໊Λtypo͢ΔͱϏϧυΤϥʔʹͯ͘͠ΕΔ 
 (ϝοηʔδ͕෼͔Γʹ͍͚͘Ͳ...)

Slide 22

Slide 22 text

key໊ͷม׵ΛܕͰදݱ  SnakeCaseCodingKeyͳͲΛඞཁʹԠͯ͡࡞ΕΔ จࣈྻॲཧͷύϑΥʔϚϯε͕ؾʹͳΔ৔߹͸ • ม׵ޙͷkey໊ΛΩϟογϡ͢Δ (᩵୔ʹϝϞϦʹࡌͤΔ) • ίʔυੜ੒͢Δ ↑SourceryΛ࢖͏ͱ࣮ݱͰ͖·͢ (https://github.com/krzysztofzablocki/Sourcery/)

Slide 23

Slide 23 text

΍͍ͤͷ String ͕͋ΒΘΕͨʂ

Slide 24

Slide 24 text

΍͍ͤͷ String ͕͋ΒΘΕͨʂ  { "itemId": "1234" } struct Model: Decodable { let itemId: String } ඞͣIntʹऩ·Δൣғͷ੔਺ͷidͳͷʹAPI͔ΒจࣈྻͰฦͬͯ͘Δ
 IntͰฦͯ͠΄͍͠Ͱ͢ΑͶ...

Slide 25

Slide 25 text

ͦ͜Ͱ  StringΛ೚ҙͷܕͰड͚औΕΔ
 ܕΛ࡞Δ (౰ͨΓલͰ͕͢ɺAPIଆΛ௚ͤΔ৔߹͸ͦͷ΄͏͕ྑ͍Ͱ͢)

Slide 26

Slide 26 text

StringΛผͷܕʹม׵͢Δຐ๏ͷശ  struct StringTo: Decodable { let value: T init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let stringValue = try container.decode(String.self) guard let value = T(stringValue) else { throw DecodingError.dataCorrupted( .init(codingPath: decoder.codingPath, debugDescription: "ʘ(^o^)ʗ") ) } self.value = value } }

Slide 27

Slide 27 text

ຐ๏ͷശΛ࢖͏  let json = """ { "itemId": "1234" } """.data(using: .utf8)! struct Model: Decodable { let itemId: StringTo } let model = try! decoder.decode(Model.self, from: json) print(model.itemId.value) // value͸Intܕ ↑ܕͰม׵Λදݱ

Slide 28

Slide 28 text

஋Λ҆શʹड͚औΓ͍ͨ

Slide 29

Slide 29 text

͜Μͳ࣌͋Γ·ͤΜ͔ʁ  ഑ྻ΍URLΛσίʔυ͍͚ͨ͠Ͳɺ
 ͨ·ʹσίʔυͰ͖ͳ͍஋͕ೖΔ

Slide 30

Slide 30 text

ͦ͜Ͱ  σίʔυͷࣦഊΛ
 ڐ༰͢ΔܕΛ࡞Δ

Slide 31

Slide 31 text

ջͷ޿͍ܕΛ࡞Δ  struct Safe: Decodable { let value: Wrapped? init(from decoder: Decoder) throws { do { let container = try decoder.singleValueContainer() self.value = try container.decode(Wrapped.self) } catch { self.value = nil } } }

Slide 32

Slide 32 text

഑ྻΛSafeͰแΉ  let json = """ [ {"name": "Taro", "age": 20}, {"name": "Hanako", "age": "ʹΌʔΜ"} ] """.data(using: .utf8)! struct User: Decodable { let name: String let age: Int } let users = try! JSONDecoder().decode([Safe].self, from: json) users[0].value // User(name: "Taro", age: 20) users[1].value // nil

Slide 33

Slide 33 text

URLΛSafeͰแΉ  let json = """ {"url": "https://foo.com", "url2": "urlͰ͸ͳ͍จࣈྻ"} """.data(using: .utf8)! struct Model: Decodable { let url: Safe let url2: Safe } let model = try! JSONDecoder().decode(Model.self, from: json) // Model(url: Safe(value:Optional(https://foo.com)), url2: Safe(value: nil)) model.url.value // Optional(https://foo.com)

Slide 34

Slide 34 text

ෳ਺ͷϞσϧΛ߹੒͢Δ

Slide 35

Slide 35 text

͜Μͳ࣌͋Γ·ͤΜ͔ʁ  ෳ਺ͷϞσϧΫϥεΛ
 1ͭͷϞσϧΫϥεʹ·ͱΊ͍ͨ

Slide 36

Slide 36 text

͜Μͳ΋ͷ࡞ͬͯΈ·ͨ͠  https://github.com/tattn/Mergeable

Slide 37

Slide 37 text

https://github.com/tattn/Mergeable  struct APIResponse: Codable { let id: Int let title: String let foo: String } struct APIResponse2: Codable { let tags: [String] } struct Model: Codable, Mergeable { let id: Int let title: String let tags: [String] } let response = APIResponse(id: 0, title: "ʹΌʔΜ", foo: "bar") let response2 = APIResponse2(tags: ["swift", "ios", "macos"]) let model = try! Model.merge(response, response2) XCTAssertEqual(model.id, response.id) XCTAssertEqual(model.title, response.title) XCTAssertEqual(model.tags, response2.tags)

Slide 38

Slide 38 text

Mergeable  ཪͰJSONʹͯ͠key/valueΛϚʔδ͢Δͱ͍͏
 ͱͯ΋෋߽తͳ࣮૷ʹͳͬͯ·͢ɻ 
 ։ൃεϐʔυॏࢹͳϋοΧιϯͳͲͰศརͦ͏Ͱ͢ɻ 40ߦఔͷ࣮૷ͳͷͰؾʹͳΔํ͸ಡΜͰΈ͍ͯͩ͘͞

Slide 39

Slide 39 text

·ͱΊ

Slide 40

Slide 40 text

ίʔυ͸͜͜ʹ͋Γ·͢ ࠓճొ৔ͨ͠ίʔυ͸
 ↓ͪ͜Β͔ΒࢀরͰ͖·͢ https://github.com/tattn/CodableExtensionPack

Slide 41

Slide 41 text

·ͱΊ ʘૉఢͳܕΛ࡞ͬͯૉఢͳੈքΛ࡞Ζ͏ʗ
 ʘʘ\\٩( 'ω' )و //ʗʗ CodableͰࠔͬͨͱ͖͸
 ಈ࡞ΛΧελϚΠζͨ͠ಠࣗͷܕΛ࡞Δͱ
 ޾ͤʹͳΕ·͢