Skip to content

Instantly share code, notes, and snippets.

@ilyapuchka
Created November 6, 2017 20:58
Show Gist options
  • Save ilyapuchka/0edce20631d0d4a8d0d394e30c750d0e to your computer and use it in GitHub Desktop.
Save ilyapuchka/0edce20631d0d4a8d0d394e30c750d0e to your computer and use it in GitHub Desktop.
Codable tips suggestions
/*
Instead of using enum it's possible to use RawRepresentable struct that will give you support for "unsupported" values,
but will cost you excastive switches (you'll alwyas have to handle default case, which will stand for those "unsupported" values)
It's defenetely more code than if using optional, but might be better if it comes to unwrapping this value everywhere.
*/
//enum System: String, Decodable {
// case ios, macos, tvos, watchos
//}
struct System: RawRepresentable, Decodable {
let rawValue: String
init(rawValue: String) {
self.rawValue = rawValue
}
static let ios = System(rawValue: "ios")
static let macos = System(rawValue: "macos")
static let tvos = System(rawValue: "tvos")
static let watchos = System(rawValue: "watchos")
}
final class Device: Decodable {
let location: Location
let system: System
}
let json = """
{
"location": {
"latitude": 37.3317,
"longitude": 122.0302
},
"system": "caros"
}
"""
let device = try JSONDecoder().decode(Device.self, from: json.data(using: .utf8)!)
print(device.system) // > caros
protocol SafeDecodable: Decodable {
associatedtype T: Decodable
var value: T? { get }
}
public struct Safe<Base: Decodable>: SafeDecodable {
public let value: Base?
public init(from decoder: Decoder) throws {
do {
let container = try decoder.singleValueContainer()
self.value = try container.decode(Base.self)
} catch {
print("ERROR: \(error)")
// TODO: automatically send a report about a corrupted data
self.value = nil
}
}
}
extension JSONDecoder {
func decode<T>(_ type: T.Type, from data: Data) -> T.T? where T: SafeDecodable {
return (try? decode(type, from: data) as T)?.value
}
func decode<T>(_ type: [T].Type, from data: Data) -> [T.T] where T: SafeDecodable {
return (try? decode(type, from: data) as [T])?.flatMap({ $0.value }) ?? []
}
}
let posts: [Post] = try JSONDecoder().decode([Safe<Post>].self, from: json.data(using: .utf8)!)
// will return only one Post
/*
For approach witch schemes we can save a call to decode scheme first using some protocols and defining default Decodable
constructor implementation. Does not save a lot, but arguable a bit cleaner.
*/
protocol DecodingScheme: Decodable {
associatedtype Entity: SchemeDecodable
}
protocol SchemeDecodable: Decodable {
associatedtype Scheme: DecodingScheme
init(from scheme: Scheme)
}
extension SchemeDecodable where Scheme.Entity == Self {
init(from decoder: Decoder) throws {
try self.init(from: Scheme(from: decoder))
}
}
final class Post: SchemeDecodable {
let id: Id<Post>
let title: String
let webURL: URL?
init(from scheme: Scheme) {
self.id = scheme.id.
self.title = scheme.title
self.webURL = scheme.webURL?.value
}
final class Scheme: DecodingScheme {
typealias Entity = Post
let id: Id<Post>
let title: String
let webURL: Safe<URL>?
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment