MoreCodable expands the possibilities of `Codable`.

Overview

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.

Comments
  • Added testEncodingMultipleTimesUsingTheSameEncoder

    Added testEncodingMultipleTimesUsingTheSameEncoder

    Hi :)

    I found an issue with the DictionaryEncoder: encoding multiple objects into the same encoder does not work, and only the last encoded prevails.

    See the newly added testEncodingMultipleTimesUsingTheSameEncoder for a demo of the problem.

    I have been trying to fix it thinking that it would be a fast fix, but seems to me that the solution is not straight forward due to the way it is currently architecture.

    opened by acecilia 8
  • Added the DictionaryCachableEncoder

    Added the DictionaryCachableEncoder

    I have an application where I needed to encode huge structs multiple times (in the order of thousands). I needed to cache the intermediate results of an encoding operation to get better speeds. I came up with this PR, in case you think its a good addition to this repo :)

    opened by acecilia 4
  • Decoding optional values

    Decoding optional values

    I think this will solve the issue #5.

    The url param decoder was always throwing an exception if the query item existed for a key that represented an optional property.

    I have executed the tests and all seems working pretty well.

    opened by mt81 2
  • Type 'Any' does not conform to protocol 'Decodable'

    Type 'Any' does not conform to protocol 'Decodable'

    In DictionaryDecoder, we found some build errors, which are caused from the type 'Any' does not conform to protocol 'Decodable' . Please check and fix these if you don't mind.

    open func container<Key: CodingKey>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> {
            let container = try lastContainer(forType: [String: Any].self)
            return KeyedDecodingContainer(KeyedContainer<Key>(decoder: self, codingPath: [], container: container)) // <- error: Type `Any` does not conform to protocol `Decodable`
        }
    
        open func unkeyedContainer() throws -> UnkeyedDecodingContainer {
            let container = try lastContainer(forType: [Any].self) // <- error: Type `Any` does not conform to protocol `Decodable`
            return UnkeyedContanier(decoder: self, container: container)
        }
    
    ...
    
    func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
        decoder.codingPath.append(key)
        defer { decoder.codingPath.removeLast() }
    
         let value = try find(forKey: key)
         let dictionary = try decoder.unbox(value, as: [String: Any].self) // <- error: Type `Any` does not conform to protocol `Decodable`
         return KeyedDecodingContainer(KeyedContainer<NestedKey>(decoder: decoder, codingPath: [], container: dictionary))
    }
    
    func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
        decoder.codingPath.append(key)
        defer { decoder.codingPath.removeLast() }
    
         let value = try find(forKey: key)
         let array = try decoder.unbox(value, as: [Any].self) // <- error: Type `Any` does not conform to protocol `Decodable`
          return UnkeyedContanier(decoder: decoder, container: array)
    }
    
    opened by d-date 2
  • Added cross-platform support and GitHub Actions CI to test Swift package on macOS an Linux

    Added cross-platform support and GitHub Actions CI to test Swift package on macOS an Linux

    • With this code change now the same code works on Apple and non-Apple platforms.
    • Added GitHub Action to ensure no regressions appear on non-Apple platforms.
    • Test results for macOS and Linux: https://github.com/pvieito/MoreCodable/commit/f9b1dd734c8bc8eb67925bf09a0e4210ccb36e59/checks?check_suite_id=355981438
    opened by pvieito 1
  • Remove .h and .m files from source_files

    Remove .h and .m files from source_files

    As a result umbrella header doesn't include MoreCodable.h and my project is able to build.

    I'm unsure if it's right thing to do in general, but works for me. I hope someone with more experience with podspecs can pick this up.

    opened by zintus 1
Releases(1.3.1)
Owner
Tatsuya Tanaka
https://twitter.com/tanakasan2525 https://qiita.com/tattn https://zenn.dev/tattn
Tatsuya Tanaka
Modern interface to UserDefaults + Codable support

Default Modern interface to UserDefaults + Codable support What is Default? Default is a library that extends what UserDefaults can do by providing ex

Nicholas Maccharoli 475 Dec 20, 2022
Why not use UserDefaults to store Codable objects 😉

tl;dr You love Swift's Codable protocol and use it everywhere, who doesn't! Here is an easy and very light way to store and retrieve -reasonable amoun

Omar Albeik 452 Oct 17, 2022
CodableFiles - Save and load Codable objects from DocumentDirectory on iOS Devices.

Welcome to CodableFiles, a simple library that provides an easier way to save, load or delete Codable objects in Documents directory. It’s primarily a

Egzon Pllana 36 Dec 20, 2022
AppCodableStorage - Extends `@AppStorage` in SwiftUI to support any Codable object

AppCodableStorage Extends @AppStorage in SwiftUI to support any Codable object.

Andrew Pouliot 19 Nov 25, 2022
CodableCloudKit allows you to easily save and retrieve Codable objects to iCloud Database (CloudKit)

CodableCloudKit CodableCloudKit allows you to easily save and retrieve Codable objects to iCloud Database (CloudKit) Features ℹ️ Add CodableCloudKit f

Laurent Grondin 65 Oct 23, 2022
Typed key-value storage solution to store Codable types in various persistence layers with few lines of code!

?? Stores A typed key-value storage solution to store Codable types in various persistence layers like User Defaults, File System, Core Data, Keychain

Omar Albeik 94 Dec 31, 2022
a TableView have thumbnail cell only, and you can use gesture let it expands other expansionView, all diy

ZYThumbnailTableView #####可展开型预览TableView,开放接口,完全自由定制 #####An expandable preview TableView, custom-made all the modules completely with open API you c

null 950 Oct 17, 2022
BaseConverter-iOS - The fast and easy way to convert numbers with tons of possibilities!

BaseConverter-iOS The fast and easy way to convert numbers with tons of possibilities! With BaseConverter, convert your numbers from and to: Decimal B

Groupe MINASTE 3 Feb 8, 2022
A SwiftUI button that expands to reveal more content.

CUIExpandableButton A SwiftUI button that expands to reveal more content. CUIExpandableButton is part of the Crystal UI framework (under developement)

Robert Cole 6 Dec 10, 2022
Codable, but with Super power made custom Codable behavior easy.

Codable, but with Super power made custom Codable behavior easy.

Tsungyu Yu 24 Aug 30, 2022
A Collection of PropertyWrappers to make custom Serialization of Swift Codable Types easy

CodableWrappers Simplified Serialization with Property Wrappers Move your Codable and (En/De)coder customization to annotations! struct YourType: Coda

null 393 Jan 5, 2023
CodableCSV - Read and write CSV files row-by-row or through Swift's Codable interface.

CodableCSV provides: Imperative CSV reader/writer. Declarative CSV encoder/decoder. Support multiple inputs/outputs: Strings, Data blobs, URLs, and St

Marcos Sánchez-Dehesa 369 Jan 8, 2023
Default is a Modern interface to UserDefaults + Codable support

Default is a library that extends what UserDefaults can do by providing extensions for saving custom objects that conform to Codable and also providing a new interface to UserDefaults described below, via the protocol DefaultStorable. You can use only the Codable support extensions or the DefaultStorable protocol extensions or both. (or none, that's cool too)

Nicholas Maccharoli 475 Dec 20, 2022
tl;dr You love Swift's Codable protocol and use it everywhere

tl;dr You love Swift's Codable protocol and use it everywhere, who doesn't! Here is an easy and very light way to store and retrieve -reasonable amoun

Omar Albeik 452 Oct 17, 2022
Store and retrieve Codable objects to various persistence layers, in a couple lines of code!

tl;dr You love Swift's Codable protocol and use it everywhere, who doesn't! Here is an easy and very light way to store and retrieve Codable objects t

null 149 Dec 15, 2022
Easy XML parsing using Codable protocols in Swift

XMLCoder Encoder & Decoder for XML using Swift's Codable protocols. This package is a fork of the original ShawnMoore/XMLParsing with more features an

Max Desiatov 657 Dec 30, 2022
🌸 Powerful Codable API requests builder and manager for iOS.

This lib is about network requests with blackjack, roulette and craps! Using it you will be able to convert your massive API layer code into an awesom

CodyFire 251 Jan 8, 2023
Extensions giving Swift's Codable API type inference super powers 🦸‍♂️🦹‍♀️

Welcome to Codextended — a suite of extensions that aims to make Swift’s Codable API easier to use by giving it type inference-powered capabilities an

John Sundell 1.4k Jan 2, 2023
Powerful property wrapper to back codable properties.

BackedCodable Powerful property wrapper to back codable properties. Why Swift's Codable is a great language feature but easily becomes verbose and req

Jérôme Alves 472 Dec 18, 2022