Created
October 30, 2020 16:56
-
-
Save JasonCanCode/084108ad8e4428427e149bb45bceedab to your computer and use it in GitHub Desktop.
A protocol to add JSON conversion abilities to any Codable object
This file contains hidden or 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
import Foundation | |
public typealias JSON = [String: Any] | |
/// A Codable object with added JSON conversion abilities | |
public protocol JSONCodable: Codable, JSONAttributable { | |
// Replace declaration in adoptor's extension to change expected date format | |
static var dateFormatter: DateFormatter { get } | |
} | |
public extension JSONCodable { | |
static var dateFormatter: DateFormatter { | |
let formatter = DateFormatter() | |
formatter.locale = Locale(identifier: "en-US_POSIX") | |
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" | |
return formatter | |
} | |
var attributes: [String: Any?] { | |
let encoder = JSONEncoder() | |
encoder.outputFormatting = .prettyPrinted | |
encoder.dateEncodingStrategy = .formatted(Self.dateFormatter) | |
do { | |
let jsonData = try encoder.encode(self) | |
return try JSONSerialization.jsonObject(with: jsonData, options: []) as? JSON ?? [:] | |
} catch { | |
return [:] | |
} | |
} | |
init(json: JSON) throws { | |
guard let value = Self.generateNew(fromJSON: json) else { | |
throw NetworkError.dataConversion | |
} | |
self = value | |
} | |
static func generateNew(fromJSON json: JSON) -> Self? { | |
let decoder = JSONDecoder() | |
decoder.dateDecodingStrategy = .formatted(dateFormatter) | |
do { | |
let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) | |
let decoded = try decoder.decode(Self.self, from: data) | |
return decoded | |
} catch { | |
print( | |
"🗺💥 Failed to map JSONCodable model with:\n", | |
prettifyJSON(json), | |
"\n\nReceived Error:", | |
error | |
) | |
return nil | |
} | |
} | |
/// Convert JSON into readable text for logging to console | |
static func prettifyJSON(_ json: JSON) -> String { | |
do { | |
let data: Data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) | |
return String(data: data, encoding: .utf8) ?? "" | |
} catch _ { | |
return "" | |
} | |
} | |
/// Convert JSON into readable text for logging to console | |
static func prettifyJSONArray(_ jsonArray: [JSON]) -> String { | |
let arrayString = jsonArray.reduce("") { result, json -> String in | |
let addition = self.prettifyJSON(json) | |
if result.isEmpty { | |
return addition | |
} else { | |
return result + ",\n" + addition | |
} | |
} | |
return "[\n\(arrayString)\n]" | |
} | |
} | |
/** | |
A model that can provide a dictionary of its attributes. This is used to provide parameters for creating and updating entites on your server. | |
An adopter only needs to include the `attributes` computed property in order to access `validAttributes`. This provides a dictionary of only attributes that have a value (removing optionals that are nil). | |
*/ | |
public protocol JSONAttributable { | |
/// A dictionary representation of an entity's attributes. | |
var attributes: [String: Any?] { get } | |
var valuedAttributes: [String: Any] { get } | |
var nullifiedAttributes: [String: Any] { get } | |
} | |
public extension JSONAttributable { | |
/// Provides a dictionary of only attributes that have a value (removing optionals that are nil). | |
var valuedAttributes: [String: Any] { | |
var validAttributes: [String: Any] = [:] | |
for case let (key, value?) in attributes { | |
validAttributes[key] = value | |
} | |
return validAttributes | |
} | |
/// Provides a dictionary of attributes in which nil values are converted to `NSNull`. | |
var nullifiedAttributes: [String: Any] { | |
var validAttributes: [String: Any] = [:] | |
for (key, value) in attributes { | |
if let value = value { | |
validAttributes[key] = value | |
} else { | |
validAttributes[key] = NSNull() | |
} | |
} | |
return validAttributes | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment