Skip to content

Instantly share code, notes, and snippets.

@shawnthroop
Last active May 14, 2018 09:42
Show Gist options
  • Save shawnthroop/705b90343b791feccd98637d32eaa4c2 to your computer and use it in GitHub Desktop.
Save shawnthroop/705b90343b791feccd98637d32eaa4c2 to your computer and use it in GitHub Desktop.
Possible typesafe keys and values for representing pnut.io's Raw data
import Foundation
let data = """
{
"version": "1.0",
"type": "photo",
"width": 240,
"height": 160,
"title": "ZB8T0193",
"url": "http://farm4.static.flickr.com/3123/2341623661_7c99f48bbf_m.jpg",
"author_name": "Bees",
"author_url": "http://www.flickr.com/photos/bees/",
"provider_name": "Flickr",
"provider_url": "http://www.flickr.com/",
"embeddable_url": "http://www.flickr.com/photos/bees/2341623661/"
}
""".data(using: .utf8)!
do {
dump(try JSONDecoder().decode(RawValue.self, from: data))
} catch {
print(error)
}
// Swift 4.1
/// A value representing possible OEmbed response parameters
public struct RawKey: Hashable {
public let rawValue: String
public init(_ rawValue: String) {
self.rawValue = rawValue
}
}
extension RawKey: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.init(try container.decode(String.self))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(rawValue)
}
}
extension RawKey: CodingKey {
public init?(intValue: Int) {
return nil
}
public var intValue: Int? {
return nil
}
public init?(stringValue: String) {
self.init(stringValue)
}
public var stringValue: String {
return rawValue
}
}
// Swift 4.1
/// A value representing possible OEmbed response values.
private enum RawValue: Equatable {
case integer(Int)
case string(String)
case array([RawValue])
case dictionary([RawKey: RawValue])
}
extension RawValue: Decodable {
init(from decoder: Decoder) throws {
var object: RawValue?
if let container = try? decoder.container(keyedBy: RawKey.self) {
object = try RawValue(from: container)
} else if let container = try? decoder.singleValueContainer() {
object = try RawValue(from: container)
}
guard let this = object else {
throw DecodingError.rawValueDataCorrupted(codingPath: decoder.codingPath)
}
self = this
}
}
private extension RawValue {
init?(from container: SingleValueDecodingContainer) throws {
if let value = try container.decode(possible: String.self) {
self = .string(value)
} else if let value = try container.decode(possible: Int.self) {
self = .integer(value)
} else if let value = try container.decode(possible: [RawValue].self) {
self = .array(value)
} else {
return nil
}
}
init(from container: KeyedDecodingContainer<RawKey>) throws {
let value = try container.allKeys.reduce(into: [RawKey: RawValue]()) { values, key in
values[key] = try container.decode(RawValue.self, forKey: key)
}
self = .dictionary(value)
}
}
private extension SingleValueDecodingContainer {
func decode<T>(possible type: T.Type) throws -> T? where T: Decodable {
var value: T?
do {
value = try decode(T.self)
} catch let error as DecodingError {
guard case .typeMismatch(_) = error else {
throw error
}
} catch {
throw error
}
return value
}
}
private extension DecodingError {
static func rawValueDataCorrupted(codingPath path: [CodingKey]) -> DecodingError {
return .dataCorrupted(Context(codingPath: path, debugDescription: "Unable to decode RawValue"))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment