LoginSignup
10
5

More than 5 years have passed since last update.

CodingKeyのマッピングを動的に切り替える

Last updated at Posted at 2017-06-19

Codableのkeyとプロパティの対応関係、Genericを使えば外から動的に注入できそうだなーと思ってやってみた。

こんなかんじ

CodingKeyMapper.swift
/// CodingKeyのマッピングを動的に切り替える
protocol CodingKeyMapper {
    static var mapper: [String: S<Self>.CodingKeys] { get }
}
extension CodingKeyMapper {
    static var mapper: [String: S<Self>.CodingKeys] { return [:] }

    static func key(_ stringValue: String) -> S<Self>.CodingKeys? {
        return mapper[stringValue]
    }
    static func stringValue(_ key: S<Self>.CodingKeys) -> String {
        return mapper.first { $0.value == key }?.key ?? String(describing: key)
    }
}
CodingKeyMapperImpl.swift
///Mapperの実体にはdictを定義するだけ
struct Pattern1: CodingKeyMapper {
    static let mapper: [String: S<Pattern1>.CodingKeys] = [
        "i_another": .i,
        "s_another": .s,
        ]
}

/// 欠けたkey(👇では .s ) は、String(describing: key) として扱われる
struct Pattern2: CodingKeyMapper {
    static let mapper: [String: S<Pattern2>.CodingKeys] = [
        "i_yet_another": .i,
        ]
}

/// key名の変更がないなら何も書く必要がない
struct Pattern0: CodingKeyMapper {}


Execution.swift
struct S<Mapper: CodingKeyMapper>: Codable {
    let i: Int
    let s: String

    enum CodingKeys: CodingKey {
        case i
        case s

        init?(stringValue: String) {
            guard let k = Mapper.key(stringValue) else { return nil }
            self = k
        }
        var stringValue: String {
            return Mapper.stringValue(self)
        }
    }
}


import Foundation

let data = """
{
    "i": 1,
    "s": "デフォルトのsですよ",

    "i_another": 10,
    "s_another": "別のsですよ",

    "i_yet_another": 999,
}
""".data(using: .utf8)!

let decoder: JSONDecoder = JSONDecoder()
let s0 = try! decoder.decode(S<Pattern0>.self, from: data)
let s1 = try! decoder.decode(S<Pattern1>.self, from: data)
let s2 = try! decoder.decode(S<Pattern2>.self, from: data)
dump([s0, s1, s2])

実行結果

▿ 3 elements
  ▿ __lldb_expr_26.S<__lldb_expr_26.Pattern0>
    - i: 1
    - s: "デフォルトのsですよ"
  ▿ __lldb_expr_26.S<__lldb_expr_26.Pattern1>
    - i: 10
    - s: "別のsですよ"
  ▿ __lldb_expr_26.S<__lldb_expr_26.Pattern2>
    - i: 999
    - s: "デフォルトのsですよ"

思ったこと

一応できたけど、Genericな型なの気になる…。
Decoder.decode の引数で渡せればいいんですけどね。

10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5