Skip to content

Instantly share code, notes, and snippets.

@KoCMoHaBTa
Last active June 11, 2018 08:08
Show Gist options
  • Select an option

  • Save KoCMoHaBTa/a5fe0bd1e3e66b5010b28c33e22397d6 to your computer and use it in GitHub Desktop.

Select an option

Save KoCMoHaBTa/a5fe0bd1e3e66b5010b28c33e22397d6 to your computer and use it in GitHub Desktop.

Revisions

  1. KoCMoHaBTa revised this gist Jun 11, 2018. 4 changed files with 4 additions and 4 deletions.
    2 changes: 1 addition & 1 deletion AnyEncodableValue.swift
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    //
    // AnyEncodableValue.swift
    //
    // https://gist.github.com/KoCMoHaBTa/a5fe0bd1e3e66b5010b28c33e22397d6#file-anyencodablevalue-swift
    //
    // Created by Milen Halachev on 27.09.17.
    // Copyright © 2017 Milen Halachev. All rights reserved.
    2 changes: 1 addition & 1 deletion Codable+JSON.swift
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    //
    // Codable+JSON.swift
    //
    // https://gist.github.com/KoCMoHaBTa/a5fe0bd1e3e66b5010b28c33e22397d6#file-codable-json-swift
    //
    // Created by Milen Halachev on 11.06.18.
    // Copyright © 2018 Milen Halachev. All rights reserved.
    2 changes: 1 addition & 1 deletion DecodingContainer+Generics.swift
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    //
    // DecodingContainer+Generics.swift
    //
    // https://gist.github.com/KoCMoHaBTa/a5fe0bd1e3e66b5010b28c33e22397d6#file-decodingcontainer-generics-swift
    //
    // Created by Milen Halachev on 11.06.18.
    // Copyright © 2018 Milen Halachev. All rights reserved.
    2 changes: 1 addition & 1 deletion DynamicCodingKey.swift
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    //
    // DynamicCodingKey.swift
    //
    // https://gist.github.com/KoCMoHaBTa/a5fe0bd1e3e66b5010b28c33e22397d6#file-dynamiccodingkey-swift
    //
    // Created by Milen Halachev on 27.09.17.
    // Copyright © 2017 Milen Halachev. All rights reserved.
  2. KoCMoHaBTa revised this gist Jun 11, 2018. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions - Codable Utilities.md
    Original file line number Diff line number Diff line change
    @@ -3,6 +3,6 @@
    ### Contents

    - [Codable+JSON.swift](#file-codable-json-swift)
    - [DecodingContainer+Generics.swift](#file-decodingcontainer+generics-swift)
    - [DynamicCodingKey.swift](#file-dynamiccodingKey-swift)
    - [DecodingContainer+Generics.swift](#file-decodingcontainer-generics-swift)
    - [DynamicCodingKey.swift](#file-dynamiccodingkey-swift)
    - [AnyEncodableValue.swift](#file-anyencodablevalue-swift)
  3. KoCMoHaBTa revised this gist Jun 11, 2018. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions - Codable Utilities.md
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@

    ### Contents

    - [Codable+JSON.swift](#file-Codable+JSON-swift)
    - [DecodingContainer+Generics.swift](#file-DecodingContainer+Generics-swift)
    - [DynamicCodingKey.swift](#file-DynamicCodingKey-swift)
    - [AnyEncodableValue.swift](#file-AnyEncodableValue-swift)
    - [Codable+JSON.swift](#file-codable-json-swift)
    - [DecodingContainer+Generics.swift](#file-decodingcontainer+generics-swift)
    - [DynamicCodingKey.swift](#file-dynamiccodingKey-swift)
    - [AnyEncodableValue.swift](#file-anyencodablevalue-swift)
  4. KoCMoHaBTa renamed this gist Jun 11, 2018. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. KoCMoHaBTa created this gist Jun 11, 2018.
    80 changes: 80 additions & 0 deletions AnyEncodableValue.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,80 @@
    //
    // AnyEncodableValue.swift
    //
    //
    // Created by Milen Halachev on 27.09.17.
    // Copyright © 2017 Milen Halachev. All rights reserved.
    //

    import Foundation

    //A type eraser that can represents any encodable primitive value
    public struct AnyEncodableValue: Encodable {

    public var value: Any?

    public init(value: Any?) {

    self.value = value
    }

    public func encode(to encoder: Encoder) throws {

    var container = encoder.singleValueContainer()

    guard let value = self.value else {

    try container.encodeNil()
    return
    }

    switch type(of: value) {

    case is Date.Type:
    try container.encode(value as! Date)

    case is URL.Type:
    try container.encode(value as! URL)

    case is String.Type:
    try container.encode(value as! String)

    case is Bool.Type:
    try container.encode(value as! Bool)

    case is Int.Type:
    try container.encode(value as! Int)

    case is Float.Type:
    try container.encode(value as! Float)

    case is Double.Type:
    try container.encode(value as! Double)

    default:
    throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: encoder.codingPath, debugDescription: "Unsupported JSON type"))
    }
    }
    }

    extension AnyEncodableValue: ExpressibleByStringLiteral {

    public init(stringLiteral value: String) { self.value = value }
    public init(unicodeScalarLiteral value: String) { self.value = value }
    public init(extendedGraphemeClusterLiteral value: String) { self.value = value }
    }

    extension AnyEncodableValue: ExpressibleByFloatLiteral {

    public init(floatLiteral value: Double) { self.value = value }
    }

    extension AnyEncodableValue: ExpressibleByIntegerLiteral {

    public init(integerLiteral value: Int) { self.value = value }
    }

    extension AnyEncodableValue: ExpressibleByBooleanLiteral {

    public init(booleanLiteral value: Bool) { self.value = value }
    }
    8 changes: 8 additions & 0 deletions Codable Utilities.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,8 @@
    # Codable Utilities

    ### Contents

    - [Codable+JSON.swift](#file-Codable+JSON-swift)
    - [DecodingContainer+Generics.swift](#file-DecodingContainer+Generics-swift)
    - [DynamicCodingKey.swift](#file-DynamicCodingKey-swift)
    - [AnyEncodableValue.swift](#file-AnyEncodableValue-swift)
    125 changes: 125 additions & 0 deletions Codable+JSON.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,125 @@
    //
    // Codable+JSON.swift
    //
    //
    // Created by Milen Halachev on 11.06.18.
    // Copyright © 2018 Milen Halachev. All rights reserved.
    //

    import Foundation

    extension Decodable {

    ///Creates an instance of the receiver from a JSON data
    public init?(json data: Data?) {

    guard
    let data = data
    else {

    return nil
    }

    do {

    let result = try JSONDecoder().decode(Self.self, from: data)
    self = result
    }
    catch {

    print(error)
    return nil
    }
    }

    ///Creates an instance of the receiver from a JSON string
    public init?(json string: String?) {

    guard
    let data = string?.data(using: .utf8)
    else {

    return nil
    }

    self.init(json: data)
    }

    ///Creates an instance of the receiver from a JSON object or array
    public init?(json object: Any?) {

    guard
    let object = object,
    let data = try? JSONSerialization.data(withJSONObject: object, options: [])
    else {

    return nil
    }

    self.init(json: data)
    }
    }

    extension Encodable {

    ///JSON string representation of the receiver
    public func json() -> String? {

    guard
    let data: Data = self.json()
    else {

    return nil
    }

    return String(data: data, encoding: .utf8)
    }

    ///JSON data representation of the receiver
    public func json() -> Data? {

    do {

    return try JSONEncoder().encode(self)
    }
    catch {

    print(error)
    return nil
    }
    }

    ///JSON object or array representation of the receiver
    public func json() -> Any? {

    guard
    let data: Data = self.json()
    else {

    return nil
    }

    return try? JSONSerialization.jsonObject(with: data, options: [])
    }
    }

    extension Encodable {

    ///JSON string representation of the receiver
    public var jsonString: String? {

    return self.json()
    }

    ///JSON data representation of the receiver
    public var jsonData: Data? {

    return self.json()
    }

    ///JSON object or array representation of the receiver
    public var jsonObject: Any? {

    return self.json()
    }
    }
    154 changes: 154 additions & 0 deletions DecodingContainer+Generics.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,154 @@
    //
    // DecodingContainer+Generics.swift
    //
    //
    // Created by Milen Halachev on 11.06.18.
    // Copyright © 2018 Milen Halachev. All rights reserved.
    //

    import Foundation

    extension KeyedDecodingContainer {

    ///Decodes a value for the given key.
    public func decode<T>(forKey key: Key) throws -> T where T: Decodable {

    return try self.decode(T.self, forKey: key)
    }

    ///Decodes a value for the given key or alternative keys.
    public func decode<T>(forKey key: Key, or additionalKeys: [Key]) throws -> T where T: Decodable {

    if let value: T = try? self.decode(forKey: key) {

    return value
    }

    for key in additionalKeys {

    if let value: T = try? self.decode(forKey: key) {

    return value
    }
    }

    return try self.decode(forKey: key)
    }

    ///Decodes a value for the given key, if present.
    public func decodeIfPresent<T>(forKey key: Key) throws -> T? where T: Decodable {

    return try self.decodeIfPresent(T.self, forKey: key)
    }

    ///Decodes a value for the given key, applying a transformation block from one type to another.
    public func decode<T, U>(forKey key: Key, transform: (U) throws -> T) throws -> T where T: Decodable, U: Decodable {

    let original: U = try self.decode(U.self, forKey: key)
    return try transform(original)
    }

    ///Decodes a value for the given key, if present, applying a transformation block from one type to another.
    public func decodeIfPresent<T, U>(forKey key: Key, transform: (U?) throws -> T?) throws -> T? where T: Decodable, U: Decodable {

    let original = try self.decodeIfPresent(U.self, forKey: key)
    return try transform(original)
    }

    ///Decodes an array value for the given key, throwing away elements that cannot be decoded and returning successfully decoded elements.
    public func decodeArray<T>(forKey key: Key) throws -> [T] where T: Decodable {

    var result: [T] = []

    guard var container = try? self.nestedUnkeyedContainer(forKey: key) else {

    return result
    }

    for _ in 0..<(container.count ?? 0) {

    do {

    let element: T = try container.decode()
    result.append(element)
    }
    catch {

    print(error)

    //https://bugs.swift.org/browse/SR-5953
    //throw the data away
    _ = try? container.decode(UselessCodable.self)
    }
    }

    return result
    }

    ///Decodes an array value for the given key, throwing away elements that cannot be decoded and returning successfully decoded elements, applying a transformation block from one type to another.
    public func decodeArray<T, U>(forKey key: Key, transform: ([U]) throws -> [T]) throws -> [T] where T: Decodable, U: Decodable {

    let original: [U] = try self.decodeArray(forKey: key)
    return try transform(original)
    }
    }

    extension UnkeyedDecodingContainer {

    ///Decodes a value.
    public mutating func decode<T>() throws -> T where T: Decodable {

    return try self.decode(T.self)
    }

    ///Decodes a value, if prsent.
    public mutating func decodeIfPresent<T>() throws -> T? where T: Decodable {

    return try self.decodeIfPresent(T.self)
    }

    ///Decodes a value, applying a transformation block from one type to another.
    public mutating func decode<T, U>(transform: (U) throws -> T) throws -> T where T: Decodable, U: Decodable {

    let original = try self.decode(U.self)
    return try transform(original)
    }

    ///Decodes a value, if present, applying a transformation block from one type to another.
    public mutating func decodeIfPresent<T, U>(transform: (U?) throws -> T?) throws -> T? where T: Decodable, U: Decodable {

    let original = try self.decodeIfPresent(U.self)
    return try transform(original)
    }

    ///Decodes an array value, throwing away elements that cannot be decoded and returning successfully decoded elements.
    public mutating func decodeArray<T>() throws -> [T] where T: Decodable {

    var result: [T] = []

    guard var container = try? self.nestedUnkeyedContainer() else {

    return result
    }

    for _ in 0..<(container.count ?? 0) {

    if let element: T = try? container.decode() {

    result.append(element)
    }
    }

    return result
    }

    ///Decodes an array value, throwing away elements that cannot be decoded and returning successfully decoded elements, applying a transformation block from one type to another.
    public mutating func decodeArray<T, U>(transform: ([U]) throws -> [T]) throws -> [T] where T: Decodable, U: Decodable {

    let original: [U] = try self.decodeArray()
    return try transform(original)
    }
    }

    ///A type used when decoding array to throw away invalid elements
    private struct UselessCodable: Codable {}
    40 changes: 40 additions & 0 deletions DynamicCodingKey.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,40 @@
    //
    // DynamicCodingKey.swift
    //
    //
    // Created by Milen Halachev on 27.09.17.
    // Copyright © 2017 Milen Halachev. All rights reserved.
    //

    import Foundation

    ///A type that can represent any single coding key dynamically
    public struct DynamicCodingKey: CodingKey {

    public let stringValue: String
    public let intValue: Int?

    public init?(stringValue: String) {

    self.stringValue = stringValue
    self.intValue = Int(stringValue)
    }

    public init?(intValue: Int) {

    self.stringValue = String(intValue)
    self.intValue = intValue
    }
    }

    extension DynamicCodingKey: ExpressibleByStringLiteral {

    public init(stringLiteral value: String) { self.init(stringValue: value)! }
    public init(unicodeScalarLiteral value: String) { self.init(stringValue: value)! }
    public init(extendedGraphemeClusterLiteral value: String) { self.init(stringValue: value)! }
    }

    extension DynamicCodingKey: ExpressibleByIntegerLiteral {

    public init(integerLiteral value: Int) { self.init(intValue: value)! }
    }