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

Codable Tips集

Codable Tips集

#potatotips 45

Tatsuya Tanaka

November 28, 2017
Tweet

More Decks by Tatsuya Tanaka

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

  3. ͓஌Βͤ

    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

    View Slide

  4. Codableͱ͸

    View Slide

  5. ܕͷΤϯίʔυͱσίʔυΛαϙʔτ͢Δػೳ

    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"}

    View Slide

  6. ܕͷΤϯίʔυͱσίʔυΛαϙʔτ͢Δػೳ

    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")

    View Slide

  7. ίίΒ΁ΜΛΈΔͱΘ͔Γ·͢
    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

    View Slide

  8. ·ͣ͸͜ΕΛ఻͍͑ͨ

    View Slide

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

    View Slide

  10. Swift 3.2Ͱ΋࢖͑ΔΑ
    Xcode 9.0Ҏ্Ͱ͋Ε͹ɺ࢖༻Մೳ
    Swift 4ʹ·্ͩ͛ΒΕͳ͍൵͍͠ঢ়گͰ΋࢖͑Δ

    Swift 4ʹ্͛ΒΕΔ࣌Λ଴ͭඞཁ͸ͳ͍Α

    View Slide

  11. ݟམͱ͕ͪ͠ͳ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

    View Slide

  12. CodableͷTipsू

    View Slide

  13. Key໊͕Swifty͡Όͳ͍

    View Slide

  14. JSONͷkeyͷ಄͕େจࣈ(´ɾωɾʆ)

    {
    "Id": 100,
    "Name": "Taro"
    }
    struct Model: Decodable {
    let id: Int
    let name: String
    }
    ←͜͏͍͚ͨ͠Ͳ

    ɹͰ͖ͳ͍ɻɻ
    (ϓϩύςΟ໊ͱkey໊͕Ұக͍ͯ͠ͳ͍ͨΊ)

    View Slide

  15. ී௨ͷରԠํ๏

    struct Model: Decodable {
    let id: Int
    let name: String
    enum CodingKeys: String, CodingKey {
    case id = "Id"
    case name = "Name"
    }
    }
    ←CodingKeysͰ

    ɹϓϩύςΟ໊ͱରԠ෇͚
    {
    "Id": 100,
    "Name": "Taro"
    }

    View Slide

  16. ී௨ͷରԠํ๏

    struct Model: Decodable {
    let id: Int
    let name: String
    enum CodingKeys: String, CodingKey {
    case id = "Id"
    case name = "Name"
    }
    }
    ←typoͦ͠͏

    ɹ৑௕ͳҹ৅
    {
    "Id": 100,
    "Name": "Taro"
    }

    View Slide

  17. ͦ͜Ͱ

    Upper Camel CaseΛkeyͱ͢Δ

    ϓϩτίϧΛ࡞Δ

    View Slide

  18. 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͕༻ҙ͞Ε͍ͯΔ

    View Slide

  19. 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ͷઌ಄Λখจࣈʹม׵ͯ͠ѻ͏

    View Slide

  20. struct Model: Decodable {
    let id: Int
    let name: String
    enum CodingKeys: String, UpperCamelCaseCodingKey {
    case id
    case name
    }
    }
    UCCCodingKeyΛ࢖͏

    ←ܕͰม׵Λදݱ

    ɹtypo͠ͳ͍
    {
    "Id": 100,
    "Name": "Taro"
    }

    View Slide

  21. ͪͳΈʹ

    key໊Λtypo͢ΔͱϏϧυΤϥʔʹͯ͘͠ΕΔ 

    (ϝοηʔδ͕෼͔Γʹ͍͚͘Ͳ...)

    View Slide

  22. key໊ͷม׵ΛܕͰදݱ

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

    View Slide

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

    View Slide

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

    {
    "itemId": "1234"
    }
    struct Model: Decodable {
    let itemId: String
    }
    ඞͣIntʹऩ·Δൣғͷ੔਺ͷidͳͷʹAPI͔ΒจࣈྻͰฦͬͯ͘Δ

    IntͰฦͯ͠΄͍͠Ͱ͢ΑͶ...

    View Slide

  25. ͦ͜Ͱ

    StringΛ೚ҙͷܕͰड͚औΕΔ

    ܕΛ࡞Δ
    (౰ͨΓલͰ͕͢ɺAPIଆΛ௚ͤΔ৔߹͸ͦͷ΄͏͕ྑ͍Ͱ͢)

    View Slide

  26. 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
    }
    }

    View Slide

  27. ຐ๏ͷശΛ࢖͏

    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ܕ
    ↑ܕͰม׵Λදݱ

    View Slide

  28. ஋Λ҆શʹड͚औΓ͍ͨ

    View Slide

  29. ͜Μͳ࣌͋Γ·ͤΜ͔ʁ

    ഑ྻ΍URLΛσίʔυ͍͚ͨ͠Ͳɺ

    ͨ·ʹσίʔυͰ͖ͳ͍஋͕ೖΔ

    View Slide

  30. ͦ͜Ͱ

    σίʔυͷࣦഊΛ

    ڐ༰͢ΔܕΛ࡞Δ

    View Slide

  31. ջͷ޿͍ܕΛ࡞Δ

    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
    }
    }
    }

    View Slide

  32. ഑ྻΛ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

    View Slide

  33. 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)

    View Slide

  34. ෳ਺ͷϞσϧΛ߹੒͢Δ

    View Slide

  35. ͜Μͳ࣌͋Γ·ͤΜ͔ʁ

    ෳ਺ͷϞσϧΫϥεΛ

    1ͭͷϞσϧΫϥεʹ·ͱΊ͍ͨ

    View Slide

  36. ͜Μͳ΋ͷ࡞ͬͯΈ·ͨ͠

    https://github.com/tattn/Mergeable

    View Slide

  37. 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)

    View Slide

  38. Mergeable

    ཪͰJSONʹͯ͠key/valueΛϚʔδ͢Δͱ͍͏

    ͱͯ΋෋߽తͳ࣮૷ʹͳͬͯ·͢ɻ

    ։ൃεϐʔυॏࢹͳϋοΧιϯͳͲͰศརͦ͏Ͱ͢ɻ
    40ߦఔͷ࣮૷ͳͷͰؾʹͳΔํ͸ಡΜͰΈ͍ͯͩ͘͞

    View Slide

  39. ·ͱΊ

    View Slide

  40. ίʔυ͸͜͜ʹ͋Γ·͢
    ࠓճొ৔ͨ͠ίʔυ͸

    ↓ͪ͜Β͔ΒࢀরͰ͖·͢
    https://github.com/tattn/CodableExtensionPack

    View Slide

  41. ·ͱΊ
    ʘૉఢͳܕΛ࡞ͬͯૉఢͳੈքΛ࡞Ζ͏ʗ

    ʘʘ\\٩( 'ω' )و //ʗʗ
    CodableͰࠔͬͨͱ͖͸

    ಈ࡞ΛΧελϚΠζͨ͠ಠࣗͷܕΛ࡞Δͱ

    ޾ͤʹͳΕ·͢

    View Slide