Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Value protocols and codables

154 views

Published on

Rebuilding the Parse SDK in Swift 4 with protocols and codables

Published in: Software
  • Be the first to comment

  • Be the first to like this

Value protocols and codables

  1. 1. Values, Protocols and Codables Florent Vilmart
  2. 2. • • • • What we’ll cover
  3. 3. Disclaimer I have no idea what I’m doin’
  4. 4. • • • • • Parse SDK not your average SDK
  5. 5. An API, to build API’s
  6. 6. Parse SDK iOS/OSX Parse Swift Runtime Dynamism Compile-time Dynamism Object Oriented PoP NSObject struct async (Bolts) sync iOS/macOS/tvOS/watchOS Just Swift Objective-C vs Swift The past, the present…
  7. 7. o
  8. 8. An REST API is a contract … but …
  9. 9. Object Data Data Object Serialize ⚠️💣 Send Responds 💣 ⚠️ Deserialize 💥 💥 Any API Client… Till today
  10. 10. T: Encodable Data Data U: Decodable JSONEncoder Send Responds JSONDecoderFor Free! For Free! URLSession REST with Swift 4
  11. 11. T: Encodable URLSession U: Decodable Great spot to fake / mock your server! Or datastore Or anything really… REST abstract
  12. 12. Encodable / Decodable Strings, Numbers, enum, Arrays, Dictionaries (of Codables) … Objects / Structs with only Codable’s Custom *(through protocol conformance) Implement CodingKeys for encoding subset Composability Codable’s
  13. 13. • • • • Encoders / Decoders
  14. 14. • • • • • JSONEncoder / ParseEncoder JSON and friends
  15. 15. Protocols protocol Saving: Encodable { associatedtype SavingType func save(options: Options) throws -> SavingType func save() throws -> SavingType } protocol Fetching: Decodable { associatedtype FetchingType func fetch(options: Options) throws -> FetchingType func fetch() throws -> FetchingType }
  16. 16. ObjectType Protocol public protocol ObjectType: Fetching, Saving { static var className: String { get } var objectId: String? { get set } var createdAt: Date? { get set } var updatedAt: Date? { get set } var ACL: ACL? { get set } } public extension ObjectType { public func save(options: Options = []) throws -> Self { return try Command<Self, SaveResponse>(method: .POST, path: object.endpoint, params: nil, body: object) .execute(options: options) .map { $0.apply(self) } } }
  17. 17. Extensible extension Saving { typealias Callback = (Self.SavingType?, Error?) -> Void func save(options: API.Options = [], callback: @escaping Callback) { runAsync(options: options, function: self.save, callback: callback) } }
  18. 18. runAsync let queue = DispatchQueue(label: "org.parse-community.ParseSwift.async") private func runAsync<T>(options: API.Options, function: @escaping (API.Options) throws -> T?, callback: @escaping (T?, Error?) -> Void) { queue.async { do { callback(try function(options), nil) } catch let e { callback(nil, e) } } }
  19. 19. As a SDK user struct GameScore: ParseSwift.ObjectType { //: Those are required for ObjectType var objectId: String? var createdAt: Date? var updatedAt: Date? var ACL: ACL? //: Your own properties var score: Int //: a custom initializer init(score: Int) { self.score = score } } let score = GameScore(score: 10) guard let savedScore = try? score.save() else { fatalError() }
  20. 20. public struct Command<T, U>: Encodable where T: Encodable, U: Decodable { let method: Method let path: Endpoint let params: [String: String?]? let body: T? public func data() throws -> Data? { return try JSONEncoder().encode(body) } public func execute(options: Options) throws -> Response<U> { return try options.executor.execute(command: self, options: options) } internal func decode(responseData: Data) throws -> Response<U> { do { return Response(object: try Decoder.json.decode(U.self, from: responseData)) } catch _ { throw try Decoder.json.decode(ParseError.self, from: responseData) } } } Command
  21. 21. Commands extension Command where T: ObjectType { // MARK: custom encoding func data() throws -> Data? { return try API.Encoder.parse.encode(body) } } struct Response<T> where T: Decodable { let object: T func map<U>(_ mapper: (T) throws -> U) rethrows -> U { return try mapper(object) } }
  22. 22. Executor extension API.Command { internal func getURLRequest(options: API.Options) throws -> URLRequest { /* build a URLRequest from the command */ } } extension URLSession: APIExecutor { public func execute<T, U>(command: API.Command<T, U>, options: API.Options) throws -> API.Response<U> { let urlRequest = try command.getURLRequest(options: options) let responseData = try self.syncDataTask(with: urlRequest) return try command.decode(responseData: responseData) } }
  23. 23. • • • • • About Async
  24. 24. A Quick Word on sync code no async is the best async extension URLSession { internal func syncDataTask(with request: URLRequest) throws -> Data { let semaphore = DispatchSemaphore(value: 0) var data: Data? var error: Error? var response: URLResponse? dataTask(with: request) { (responseData, urlResponse, responseError) in data = responseData; error = responseError; response = urlResponse; semaphore.signal() }.resume() semaphore.wait() guard let responseData = data else { guard let error = error else { // no err no res? throw NSError(domain: "unknown", code: -1, userInfo: ["response": response!]) } throw error } return responseData } } 🤦♀️
  25. 25. • • • • • What’s next Because all is not green!
  26. 26. Values, Protocols and Codables Florent Vilmart

×