O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

Value protocols and codables

267 visualizações

Publicada em

Rebuilding the Parse SDK in Swift 4 with protocols and codables

Publicada em: Software
  • Seja o primeiro a comentar

  • Seja a primeira pessoa a gostar disto

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

×