Created
March 2, 2019 18:52
-
-
Save regularberry/e296dfaa43a0184a6cddd6eacd93bbd1 to your computer and use it in GitHub Desktop.
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
// if you want a property to be atomic, we lost that in Swift. This enables that | |
final class Atomic<A> { | |
private let queue = DispatchQueue(label: "Atomic serial queue") | |
private var _value: A | |
init(_ value: A) { | |
self._value = value | |
} | |
var value: A { | |
get { | |
return queue.sync { self._value } | |
} | |
} | |
func mutate(_ transform: (inout A) -> ()) { | |
queue.sync { | |
transform(&self._value) | |
} | |
} | |
} | |
/// Decoding syntax sugar | |
extension KeyedDecodingContainerProtocol { | |
public func decode<T>(_ key: Self.Key) throws -> T where T : Decodable { | |
return try decode(T.self, forKey: key) | |
} | |
public func decode<T>(_ key: Self.Key, or defaultValue: T) -> T where T : Decodable { | |
return decode(T.self, forKey: key, or: defaultValue) | |
} | |
public func decode<T>(_ type: T.Type, forKey key: Self.Key, or defaultValue: T) -> T where T : Decodable { | |
return (try? self.decode(type, forKey: key)) ?? defaultValue | |
} | |
} | |
/// Unit Test | |
import Nimble | |
import Quick | |
@testable import <# Target #> | |
class <# TypeName #>Tests: QuickSpec { | |
override func spec() { | |
describe("<# Type Name #>") { | |
var subject: <# TypeName #>! | |
beforeEach { | |
subject = <# Type #> | |
} | |
context("<# Method 1 #>") { | |
it("<# Does the thing #>") { | |
} | |
} | |
} | |
} | |
} | |
/// Init from Decoder | |
let trackingJson = """ | |
{ | |
"ee": | |
{ | |
"code": "testCode", | |
"source": "testSource" | |
}, | |
"qc": 8, | |
"qe": "expr", | |
"qp": 1, | |
"qt": "treat" | |
} | |
""".data(using: .utf8)! | |
tracking = try! JSONDecoder().decode(FireflySapphire.TrackingInfo.self, from: trackingJson) | |
/// Equatable JSON | |
public enum JSON: Codable, Equatable { | |
case bool(Bool) | |
case empty | |
case number(NSNumber) | |
case string(String) | |
case array([JSON]) | |
case dictionary([String : JSON]) | |
public var value: Any? { | |
switch self { | |
case .bool(let bool): return bool | |
case .empty: return nil | |
case .number(let number): return number | |
case .string(let string): return string | |
case .array(let array): return array.map { $0.value } | |
case .dictionary(let dictionary): return dictionary.mapValues { $0.value } | |
} | |
} | |
public init?(_ value: Any?) { | |
guard let value = value else { | |
self = .empty | |
return | |
} | |
if let int = value as? Int { | |
self = .number(NSNumber(value: int)) | |
} else if let double = value as? Double { | |
self = .number(NSNumber(value: double)) | |
} else if let string = value as? String { | |
self = .string(string) | |
} else if let bool = value as? Bool { | |
self = .bool(bool) | |
} else if let array = value as? [Any] { | |
var mapped = [JSON]() | |
for inner in array { | |
guard let inner = JSON(inner) else { | |
return nil | |
} | |
mapped.append(inner) | |
} | |
self = .array(mapped) | |
} else if let dictionary = value as? [String: Any] { | |
var mapped = [String: JSON]() | |
for (key, inner) in dictionary { | |
guard let inner = JSON(inner) else { | |
return nil | |
} | |
mapped[key] = inner | |
} | |
self = .dictionary(mapped) | |
} else { | |
return nil | |
} | |
} | |
public init(from decoder: Decoder) throws { | |
let container = try decoder.singleValueContainer() | |
guard !container.decodeNil() else { | |
self = .empty | |
return | |
} | |
if let int = try container.decodeIfMatched(Int.self) { | |
self = .number(NSNumber(value: int)) | |
} else if let double = try container.decodeIfMatched(Double.self) { | |
self = .number(NSNumber(value: double)) | |
} else if let string = try container.decodeIfMatched(String.self) { | |
self = .string(string) | |
} else if let bool = try container.decodeIfMatched(Bool.self) { | |
self = .bool(Bool(bool)) | |
} else if let array = try container.decodeIfMatched([JSON].self) { | |
self = .array(array) | |
} else if let dictionary = try container.decodeIfMatched([String: JSON].self) { | |
self = .dictionary(dictionary) | |
} else { | |
throw DecodingError.typeMismatch(JSON.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unable to decode JSON as any of the possible types.")) | |
} | |
} | |
public func encode(to encoder: Encoder) throws { | |
var container = encoder.singleValueContainer() | |
switch self { | |
case .bool(let bool): try container.encode(bool) | |
case .empty: try container.encodeNil() | |
case .number(let number): | |
if number.objCType.pointee == 0x64 /* 'd' */ { | |
try container.encode(number.doubleValue) | |
} else { | |
try container.encode(number.intValue) | |
} | |
case .string(let string): try container.encode(string) | |
case .array(let array): try container.encode(array) | |
case .dictionary(let dictionary): try container.encode(dictionary) | |
} | |
} | |
} | |
fileprivate extension SingleValueDecodingContainer { | |
func decodeIfMatched<T: Decodable>(_ type: T.Type) throws -> T? { | |
do { | |
return try self.decode(T.self) | |
} catch DecodingError.typeMismatch { | |
return nil | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment