Skip to content

tattn/MoreCodable

Repository files navigation

MoreCodable

MoreCodable expands the possibilities of "Codable".

Installation

Carthage

github "tattn/MoreCodable"

CocoaPods

pod 'MoreCodable'

Feature

DictionaryEncoder / DictionaryDecoder

struct User: Codable {
    let id: Int
    let name: String
}

let encoder = DictionaryEncoder()
let user = User(id: 123, name: "tattn")
let dictionary: [String: Any] = try! encoder.encode(user) // => {"id": 123, "name": "tattn"}
let decoder = DictionaryDecoder()
let user = try decoder.decode(User.self, from: dictionary)

URLQueryItemsEncoder / URLQueryItemsDecoder

struct Parameter: Codable {
    let query: String
    let offset: Int
    let limit: Int
}
let parameter = Parameter(query: "ねこ", offset: 10, limit: 20)
let encoder = URLQueryItemsEncoder()
let params: [URLQueryItem] = try! encoder.encode(parameter)

var components = URLComponents(string: "https://example.com")
components?.queryItems = params
components?.url // https://example.com?query=%E3%81%AD%E3%81%93&offset=10&limit=20
let decoder = URLQueryItemsDecoder()
let parameter = try decoder.decode(Parameter.self, from: params)

ObjectMerger

struct APIResponse: Encodable {
    let id: Int
    let title: String
    let foo: String
}

struct APIResponse2: Encodable {
    let tags: [String]
}

struct Model: Decodable {
    let id: Int
    let title: String
    let tags: [String]
}

let response = APIResponse(id: 0, title: "Awesome article", foo: "bar")
let response2 = APIResponse2(tags: ["swift", "ios", "macos"])
let model = try ObjectMerger().merge(Model.self, response, response2)

// success
XCTAssertEqual(model.id, response.id)
XCTAssertEqual(model.title, response.title)
XCTAssertEqual(model.tags, response2.tags)

RuleBasedCodingKey

struct User: Codable {
    let userId: String
    let name: String

    enum CodingKeys: String, RuleBasedCodingKey {
        case userId
        case name

        func codingKeyRule(key: String) -> String {
            return key.uppercased() // custom rule
        }
    }
}

let json = """
{"USERID": "abc", "NAME": "tattn"}
""".data(using: .utf8)!

let user = try! JSONDecoder().decode(User.self, from: json) // => User(userId: "abc", name: "tattn")

SnakeCaseCodingKey

struct User: Codable {
    let userId: String
    let name: String

    enum CodingKeys: String, SnakeCaseCodingKey {
        case userId
        case name
    }
}

let json = """
{"user_id": "abc", "name": "tattn"}
""".data(using: .utf8)!

let user = try! JSONDecoder().decode(User.self, from: json) // ok

UpperCamelCaseCodingKey

struct User: Codable {
    let userId: String
    let name: String

    enum CodingKeys: String, UpperCamelCaseCodingKey {
        case userId
        case name
    }
}

let json = """
{"UserId": "abc", "Name": "tattn"}
""".data(using: .utf8)!

let user = try! JSONDecoder().decode(User.self, from: json) // ok

Failable

let json = """
[
    {"name": "Taro", "age": 20},
    {"name": "Hanako"}
]
""".data(using: .utf8)! // Hanako has no "age"

struct User: Codable {
    let name: String
    let age: Int
}

let users = try! JSONDecoder().decode([Failable<User>].self,
                                      from: json)

// success
XCTAssertEqual(users[0].value?.name, "Taro")
XCTAssertEqual(users[0].value?.age, 20)
XCTAssertNil(users[1].value)

StringTo

let json = """
{
    "int": "100",
    "articleId": "abc"
}
""".data(using: .utf8)!

struct Root: Codable {
    let int: StringTo<Int>
    let articleId: StringTo<ArticleId>

    struct ArticleId: LosslessStringConvertible, Codable {
        var description: String

        init?(_ description: String) {
            self.description = description
        }
    }
}

let root = try! JSONDecoder().decode(Root.self, from: json)

// success
XCTAssertEqual(root.int.value, 100)
XCTAssertEqual(root.articleId.value.description, "abc")

MultiDateFormat

let json = """
{
    "date": "2019.05.27",
    "dateTime": "2019-05-27T17:26:59+0000",
    "timestamp": 1558978068,
    "timestampMilliseconds": 1558978141863,
    "custom": "1558978068"
}
""".data(using: .utf8)!

struct Document: Codable {
    var date: Date
    var dateTime: Date
    var timestamp: Date
    var timestampMilliseconds: Date
    var custom: Date
}

extension Document: MultiDateFormat {
    
    static var dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.timeZone = TimeZone(identifier: "UTC")
        formatter.dateFormat = "yyyy.MM.dd"
        return formatter
    }()
    
    static func dateFormat(for codingKey: CodingKey) -> DateFormat? {
        switch codingKey {
        case CodingKeys.date: return .formatted(dateFormatter)
        case CodingKeys.dateTime: return .iso8601
        case CodingKeys.timestamp: return .secondsSince1970
        case CodingKeys.timestampMilliseconds: return .millisecondsSince1970
        case CodingKeys.custom: return .custom({ (date, encoder) in
            var container = encoder.singleValueContainer()
            try container.encode(String(date.timeIntervalSince1970))
        }, { (decoder) -> Date in
            let container = try decoder.singleValueContainer()
            let string = try container.decode(String.self)
            let timeInterval = TimeInterval(string)!
            return Date(timeIntervalSince1970: timeInterval)
        })
        default: return nil
        }
    }
    
}

let decoded = try! MoreJSONDecoder().decode(Document.self, from: json)
let encoded = try! MoreJSONEncoder().encode(document)

DictionaryCachableEncoder

struct User: Codable, Hashable { // conform to Hashable
    let id: Int
    let name: String
}

let encoder = DictionaryCachableEncoder()
let user = User(id: 123, name: "tattn")
let dictionary: [String: Any] = try! encoder.encode(user) // => {"id": 123, "name": "tattn"}
try! encoder.encode(user) // use the previous encoded result for the second time 

ToDo

  • XMLDecoder/XMLEncoder
  • CSVDecoder/CSVEncoder

Related project

DataConvertible
https://github.com/tattn/DataConvertible

Contributing

  1. Fork it!
  2. Create your feature branch: git checkout -b my-new-feature
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin my-new-feature
  5. Submit a pull request :D

Support this project

Donating to help me continue working on this project.

Donate

License

MoreCodable is released under the MIT license. See LICENSE for details.

About

MoreCodable expands the possibilities of `Codable`.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Languages