Skip to content

Instantly share code, notes, and snippets.

@KoCMoHaBTa
Last active June 11, 2018 08:08
Show Gist options
  • Save KoCMoHaBTa/a5fe0bd1e3e66b5010b28c33e22397d6 to your computer and use it in GitHub Desktop.
Save KoCMoHaBTa/a5fe0bd1e3e66b5010b28c33e22397d6 to your computer and use it in GitHub Desktop.
A set of utilities related to Swift Codable API
//
// 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 }
}
//
// 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()
}
}
//
// 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 {}
//
// 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