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…
18. 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) }
}
}
21. 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() }
22. 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
23. 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)
}
}
24. 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)
}
}
26. 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
}
}
🤦♀️
- Many of you may have already used, it
- Schema less
Unlike other SDK’s like FB/Github, schemas are not defined by us
We provide the facility to create custom schemas
Which makes the whole shebang quite more difficult.
But, we know response types, error codes, bare minimal object (objectId, createdAt… , errors, responses)
start with those 2 fundamental protocols
Becaust that’s the base (for files and object and Pointers etc…)
Provides the abstraction necessary.
Could add Destroying but …
Easily extensible
- Encodable, because batch API 🤓
- params is [String: String?]? 😪 (QueryStringEncoder?)
- tied to HTTP semantics 🤷🏻♀️
- executor in options
func data() throws -> Data?
Response (simple wrapper)
I cannot subclass, butt……
not a big fan of deps of deps of deps
taking a side, is usually hindering adoption
Who’S better? Rx, Reactive, Bolts, PromiseKit… Don’t care much