Last active
June 11, 2018 08:08
-
-
Save KoCMoHaBTa/a5fe0bd1e3e66b5010b28c33e22397d6 to your computer and use it in GitHub Desktop.
A set of utilities related to Swift Codable API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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. | |
// | |
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 } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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. | |
// | |
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() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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. | |
// | |
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 {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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. | |
// | |
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)! } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment