Skip to content
Binary file added .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## macOS
.DS_Store

## Build generated
build/
DerivedData/
Expand Down
113 changes: 59 additions & 54 deletions ParseSwift.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Binary file added Sources/.DS_Store
Binary file not shown.
60 changes: 30 additions & 30 deletions Sources/ParseSwift/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import Foundation

public struct API {

public enum Method: String, Encodable {
case GET, POST, PUT, DELETE
case get = "GET"
case post = "POST"
case put = "PUT"
case delete = "DELETE"
}

public enum Endpoint: Encodable {
Expand Down Expand Up @@ -46,6 +48,32 @@ public struct API {
var container = encoder.singleValueContainer()
try container.encode(urlComponent)
}

func makeRequest(method: Method,
params: [URLQueryItem]? = nil,
body: Encodable? = nil,
options: Option = [],
callback: ((Data?, Error?) -> Void)? = nil) -> Cancellable {

let bodyData = try? getJSONEncoder().encode(body)
let headers = getHeaders(useMasterKey: options.contains(.useMasterKey))
let url = ParseConfiguration.serverURL.appendingPathComponent(self.urlComponent)

var components = URLComponents(url: url, resolvingAgainstBaseURL: false)!
components.queryItems = params

var urlRequest = URLRequest(url: components.url!)
urlRequest.allHTTPHeaderFields = headers
if let bodyData = bodyData {
urlRequest.httpBody = bodyData
}
urlRequest.httpMethod = method.rawValue
let task = URLSession.shared.dataTask(with: urlRequest) { (data, _, error) in
callback?(data, error)
}
task.resume()
return task
}
}

public struct Option: OptionSet {
Expand Down Expand Up @@ -73,32 +101,4 @@ public struct API {

return headers
}

public typealias Response = (Result<Data>) -> Void

internal static func request(method: Method,
path: Endpoint,
params: [URLQueryItem]? = nil,
body: Data? = nil,
options: Option,
callback: Response? = nil) -> URLSessionDataTask {

let headers = getHeaders(useMasterKey: options.contains(.useMasterKey))
let url = ParseConfiguration.serverURL.appendingPathComponent(path.urlComponent)

var components = URLComponents(url: url, resolvingAgainstBaseURL: false)!
components.queryItems = params

var urlRequest = URLRequest(url: components.url!)
urlRequest.allHTTPHeaderFields = headers
if let body = body {
urlRequest.httpBody = body
}
urlRequest.httpMethod = method.rawValue
let task = URLSession.shared.dataTask(with: urlRequest) { (data, _, error) in
callback?(Result(data, error))
}
task.resume()
return task
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

public typealias BatchResultCallback<T> = (Result<[(T, ParseError?)]>) -> Void where T: ObjectType
/*public typealias BatchResultCallback<T> = (Result<[(T, ParseError?)]>) -> Void where T: ObjectType
public extension ObjectType {
public static func saveAll(_ objects: Self...,
callback: BatchResultCallback<Self>?) -> Cancellable {
Expand All @@ -25,3 +25,4 @@ extension Sequence where Element: ObjectType {
return RESTBatchCommand(commands: map { $0.saveCommand() })
}
}
*/
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation

public extension ObjectType {
public static func find(callback: @escaping ((Result<[Self]>) -> Void)) -> Cancellable {
public static func find(callback: @escaping (([Self]?, Error?) -> Void)) -> Cancellable {
return query().find(callback: callback)
}

Expand Down
125 changes: 125 additions & 0 deletions Sources/ParseSwift/Object Protocols/ObjectType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//
// ParseObjectType.swift
// Parse
//
// Created by Florent Vilmart on 17-07-24.
// Copyright © 2017 Parse. All rights reserved.
//

import Foundation

public protocol ObjectType: Fetching, Saving, CustomDebugStringConvertible, Equatable {
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 }
}

extension ObjectType {
// Parse ClassName inference
public static var className: String {
let t = "\(type(of: self))"
return t.components(separatedBy: ".").first! // strip .Type
}

public var className: String {
return Self.className
}

var endpoint: API.Endpoint {
if let objectId = objectId {
return .object(className: className, objectId: objectId)
}
return .objects(className: className)
}

var isSaved: Bool {
return objectId != nil
}
}

extension ObjectType {
func getEncoder() -> ParseEncoder {
return getParseEncoder()
}

func toPointer() -> Pointer<Self> {
return Pointer(self)
}
}

extension ObjectType {
public var debugDescription: String {
guard let descriptionData = try? getJSONEncoder().encode(self),
let descriptionString = String(data: descriptionData, encoding: .utf8) else {
return "\(className) ()"
}
return "\(className) (\(descriptionString))"
}
}

public extension ObjectType {
public func save(options: API.Option, callback: ((Self?, Error?) -> Void)? = nil) -> Cancellable {
let requestMethod: API.Method = isSaved ? .put : .post

return endpoint.makeRequest(method: requestMethod) {(data, error) in
if let data = data {
do {
var object: Self!

if self.isSaved {
object = try getDecoder().decode(UpdateResponse.self, from: data).apply(self)
} else {
object = try getDecoder().decode(SaveResponse.self, from: data).apply(self)
}

callback?(object, nil)
} catch {
callback?(nil, error)
}
} else if let error = error {
callback?(nil, error)
} else {
fatalError()
}
}
}
}

public extension ObjectType {
public func fetch(options: API.Option, callback: ((Self?, Error?) -> Void)? = nil) -> Cancellable? {
guard isSaved else {
let error = ParseError(code: -1, error: "Cannot Fetch an object without id")
callback?(nil, error)
return nil
}

return endpoint.makeRequest(method: .get) {(data, error) in
if let data = data {
do {
let object = try getDecoder().decode(UpdateResponse.self, from: data).apply(self)
callback?(object, nil)
} catch {
callback?(nil, error)
}
} else if let error = error {
callback?(nil, error)
} else {
fatalError()
}
}
}
}

public struct FindResult<T>: Decodable where T: ObjectType {
let results: [T]
let count: Int?
}

public extension ObjectType {
var mutationContainer: ParseMutationContainer<Self> {
return ParseMutationContainer(target: self)
}
}
103 changes: 103 additions & 0 deletions Sources/ParseSwift/Object Protocols/UserType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import Foundation

internal struct CurrentUserInfo {
static var currentUser: Any?
static var currentSessionToken: String?
}

public protocol UserType: ObjectType {
var username: String? { get set }
var email: String? { get set }
var password: String? { get set }
}

public extension UserType {
var sessionToken: String? {
if let currentUser = CurrentUserInfo.currentUser as? Self,
currentUser.objectId != nil && self.objectId != nil &&
currentUser.objectId == self.objectId {
return CurrentUserInfo.currentSessionToken
}

return nil
}

static var className: String {
return "_User"
}
}

public extension UserType {
static var current: Self? {
return CurrentUserInfo.currentUser as? Self
}
}

extension UserType {
static func logIn(username: String, password: String, callback: ((Self?, Error?) -> Void)? = nil) -> Cancellable {
let params = [
"username": username,
"password": password
].map {
URLQueryItem(name: $0, value: $1)
}

return API.Endpoint.login.makeRequest(method: .get, params: params) {(data, error) in
if let data = data {
do {
let user = try getDecoder().decode(Self.self, from: data)
let response = try getDecoder().decode(LoginSignupResponse.self, from: data)
CurrentUserInfo.currentUser = user
CurrentUserInfo.currentSessionToken = response.sessionToken
callback?(user, error)
} catch {
callback?(nil, error)
}
} else if let error = error {
callback?(nil, error)
} else {
fatalError()
}
}
}

static func logOut(callback: (() -> Void)? = nil) -> Cancellable? {
return API.Endpoint.logout.makeRequest(method: .post) {(_, _) in
callback?()
}
}

static func signUp(username: String, password: String, callback: ((Self?, Error?) -> Void)? = nil) -> Cancellable {
let body = SignupBody(username: username, password: password)

return API.Endpoint.signup.makeRequest(method: .post, body: body) {(data, error) in
if let data = data {
do {
let response = try getDecoder().decode(LoginSignupResponse.self, from: data)

var user = try getDecoder().decode(Self.self, from: data)
user.username = username
user.password = password
user.updatedAt = response.updatedAt ?? response.createdAt

// Set the current user
CurrentUserInfo.currentUser = user
CurrentUserInfo.currentSessionToken = response.sessionToken

callback?(user, nil)
} catch {
callback?(nil, error)
}
} else if let error = error {
callback?(nil, error)
} else {
fatalError()
}
}
}
}

public struct SignupBody: Codable {
let username: String
let password: String
}
Loading