Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Renaming, Reorganization, PrimitiveObjectStore, new ParseEncoder #13

Merged
merged 32 commits into from
Aug 22, 2020
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
bea067c
move stuff around
pranjalsatija Jul 19, 2020
77c84dc
rename things
pranjalsatija Jul 19, 2020
3e6092d
organize ParseObject and ParseUser
pranjalsatija Jul 19, 2020
ef37863
organize Coding
pranjalsatija Jul 20, 2020
00f4622
add IDEWorkspaceChecks
pranjalsatija Jul 20, 2020
3547627
add PrimitiveObjectStore and friends
pranjalsatija Jul 20, 2020
bdfd709
simplify ParseEncoder
pranjalsatija Jul 20, 2020
1819601
remove force unwrap
pranjalsatija Jul 20, 2020
d876bd1
use ParseError
pranjalsatija Jul 20, 2020
b5c5259
retry build
pranjalsatija Jul 20, 2020
69b2136
Merge branch 'master' of https://github.com/parse-community/Parse-Swi…
pranjalsatija Aug 2, 2020
2cd23a9
finish merging
pranjalsatija Aug 3, 2020
1070fe9
add hasSameObjectId
pranjalsatija Aug 3, 2020
f209e2e
add rough NewParseEncoder
pranjalsatija Aug 3, 2020
9a74666
better
pranjalsatija Aug 3, 2020
72684f2
more improvements
pranjalsatija Aug 3, 2020
4cb2019
finished for now
pranjalsatija Aug 4, 2020
38021ae
add key skipping
pranjalsatija Aug 4, 2020
e41acdf
remove old ParseEncoder
pranjalsatija Aug 4, 2020
e48f242
fix tests
pranjalsatija Aug 4, 2020
2e2a164
add MARK comments to ParseEncoder
pranjalsatija Aug 4, 2020
bd09c9e
fix target membership
pranjalsatija Aug 4, 2020
e24503f
Merge branch 'master' of https://github.com/parse-community/Parse-Swi…
pranjalsatija Aug 4, 2020
c5fd3a8
finish merging master
pranjalsatija Aug 4, 2020
55f0d7d
add array support to ParseEncoder; replace _ with <root>
pranjalsatija Aug 4, 2020
81c2cbd
update file credits
pranjalsatija Aug 4, 2020
18c08a5
fix more tests
pranjalsatija Aug 4, 2020
8189db8
address PR feedback
pranjalsatija Aug 5, 2020
c28829c
add ParseEncoderTests
pranjalsatija Aug 22, 2020
f621266
bump test host deployment target
pranjalsatija Aug 22, 2020
a8c43ac
add availability check
pranjalsatija Aug 22, 2020
ee7e1fe
increase coverage target
pranjalsatija Aug 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Parse.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
130 changes: 79 additions & 51 deletions ParseSwift.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

36 changes: 22 additions & 14 deletions Sources/ParseSwift/API/API+Commands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal extension API {
let params: [String: String?]?

internal var data: Data? {
return try? getJSONEncoder().encode(body)
return try? ParseCoding.jsonEncoder().encode(body)
}

init(method: API.Method,
Expand All @@ -34,24 +34,32 @@ internal extension API {
}

public func execute(options: API.Options) throws -> U {
let url = ParseConfiguration.serverURL.appendingPathComponent(path.urlComponent)

guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let componentsURL = components.url else {
throw ParseError(code: .unknownError, message: "Invalid URL.")
}

let params = self.params?.getQueryItems()
let headers = API.getHeaders(options: options)
let url = ParseConfiguration.serverURL.appendingPathComponent(path.urlComponent)

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

var urlRequest = URLRequest(url: components.url!)
var urlRequest = URLRequest(url: componentsURL)
urlRequest.allHTTPHeaderFields = headers

if let body = data {
urlRequest.httpBody = body
}

urlRequest.httpMethod = method.rawValue

let responseData = try URLSession.shared.syncDataTask(with: urlRequest)
do {
pranjalsatija marked this conversation as resolved.
Show resolved Hide resolved
return try mapper(responseData)
} catch _ {
throw try getDecoder().decode(ParseError.self, from: responseData)
throw try ParseCoding.jsonDecoder().decode(ParseError.self, from: responseData)
}
}

Expand All @@ -63,27 +71,27 @@ internal extension API {

internal extension API.Command {
// MARK: Saving
static func saveCommand<T>(_ object: T) -> API.Command<T, T> where T: ObjectType {
static func saveCommand<T>(_ object: T) -> API.Command<T, T> where T: ParseObject {
if object.isSaved {
return updateCommand(object)
}
return createCommand(object)
}

// MARK: Saving - private
private static func createCommand<T>(_ object: T) -> API.Command<T, T> where T: ObjectType {
private static func createCommand<T>(_ object: T) -> API.Command<T, T> where T: ParseObject {
let mapper = { (data) -> T in
try getDecoder().decode(SaveResponse.self, from: data).apply(object)
try ParseCoding.jsonDecoder().decode(SaveResponse.self, from: data).apply(object)
}
return API.Command<T, T>(method: .POST,
path: object.endpoint,
body: object,
mapper: mapper)
}

private static func updateCommand<T>(_ object: T) -> API.Command<T, T> where T: ObjectType {
private static func updateCommand<T>(_ object: T) -> API.Command<T, T> where T: ParseObject {
let mapper = { (data: Data) -> T in
try getDecoder().decode(UpdateResponse.self, from: data).apply(object)
try ParseCoding.jsonDecoder().decode(UpdateResponse.self, from: data).apply(object)
}
return API.Command<T, T>(method: .PUT,
path: object.endpoint,
Expand All @@ -92,18 +100,18 @@ internal extension API.Command {
}

// MARK: Fetching
static func fetchCommand<T>(_ object: T) throws -> API.Command<T, T> where T: ObjectType {
static func fetchCommand<T>(_ object: T) throws -> API.Command<T, T> where T: ParseObject {
guard object.isSaved else {
throw ParseError(code: .unknownError, message: "Cannot Fetch an object without id")
}
return API.Command<T, T>(method: .GET,
path: object.endpoint) { (data) -> T in
try getDecoder().decode(T.self, from: data)
try ParseCoding.jsonDecoder().decode(T.self, from: data)
}
}
}

extension API.Command where T: ObjectType {
extension API.Command where T: ParseObject {

internal var data: Data? {
guard let body = body else { return nil }
Expand All @@ -124,7 +132,7 @@ extension API.Command where T: ObjectType {
}
let mapper = { (data: Data) -> [(T, ParseError?)] in
let decodingType = [BatchResponseItem<SaveOrUpdateResponse>].self
let responses = try getDecoder().decode(decodingType, from: data)
let responses = try ParseCoding.jsonDecoder().decode(decodingType, from: data)
return bodies.enumerated().map({ (object) -> (T, ParseError?) in
let response = responses[object.0]
if let success = response.success {
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/API/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public struct API {
headers["X-Parse-Client-Key"] = clientKey
}

if let token = CurrentUserInfo.currentSessionToken {
if let token = BaseParseUser.currentUserContainer?.sessionToken {
headers["X-Parse-Session-Token"] = token
}

Expand Down
6 changes: 3 additions & 3 deletions Sources/ParseSwift/API/BatchUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

import Foundation

typealias ParseObjectBatchCommand<T> = BatchCommand<T, T> where T: ObjectType
typealias ParseObjectBatchCommand<T> = BatchCommand<T, T> where T: ParseObject
typealias ParseObjectBatchResponse<T> = [(T, ParseError?)]
// swiftlint:disable line_length
typealias RESTBatchCommandType<T> = API.Command<ParseObjectBatchCommand<T>, ParseObjectBatchResponse<T>> where T: ObjectType
typealias RESTBatchCommandType<T> = API.Command<ParseObjectBatchCommand<T>, ParseObjectBatchResponse<T>> where T: ParseObject
// swiftlint:enable line_length

public struct BatchCommand<T, U>: Encodable where T: Encodable {
Expand Down Expand Up @@ -46,7 +46,7 @@ struct SaveOrUpdateResponse: Decodable {
return UpdateResponse(updatedAt: updatedAt)
}

func apply<T>(_ object: T) -> T where T: ObjectType {
func apply<T>(_ object: T) -> T where T: ParseObject {
if isCreate {
return asSaveResponse().apply(object)
} else {
Expand Down
4 changes: 2 additions & 2 deletions Sources/ParseSwift/API/Responses.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal struct SaveResponse: Decodable {
return createdAt
}

func apply<T>(_ object: T) -> T where T: ObjectType {
func apply<T>(_ object: T) -> T where T: ParseObject {
var object = object
object.objectId = objectId
object.createdAt = createdAt
Expand All @@ -27,7 +27,7 @@ internal struct SaveResponse: Decodable {
internal struct UpdateResponse: Decodable {
var updatedAt: Date

func apply<T>(_ object: T) -> T where T: ObjectType {
func apply<T>(_ object: T) -> T where T: ParseObject {
var object = object
object.updatedAt = updatedAt
return object
Expand Down
14 changes: 9 additions & 5 deletions Sources/ParseSwift/Asynchronous.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,22 @@ private func runAsync<T>(options: API.Options,
}
}

extension Saving {
// MARK: Saveable
extension Saveable {
public func save(options: API.Options = [], callback: @escaping (Self.SavingType?, Error?) -> Void) {
runAsync(options: options, function: self.save, callback: callback)
}
}

extension Fetching {
// MARK: Fetchable
extension Fetchable {
public func fetch(options: API.Options = [], callback: @escaping (Self.FetchingType?, Error?) -> Void) {
runAsync(options: options, function: self.fetch, callback: callback)
}
}

extension Querying {
// MARK: Queryable
extension Queryable {
public func find(options: API.Options = [], callback: @escaping ([ResultType]?, Error?) -> Void) {
runAsync(options: options, function: self.find, callback: callback)
}
Expand All @@ -46,15 +49,16 @@ extension Querying {
}
}

public extension ObjectType {
// MARK: Batch Support
public extension ParseObject {
static func saveAll(options: API.Options = [],
_ objects: Self...,
callback: @escaping ([(Self, ParseError?)]?, Error?) -> Void) {
objects.saveAll(options: options, callback: callback)
}
}

public extension Sequence where Element: ObjectType {
public extension Sequence where Element: ParseObject {
func saveAll(options: API.Options = [],
callback: @escaping ([(Element, ParseError?)]?, Error?) -> Void) {
runAsync(options: options, function: self.saveAll, callback: callback)
Expand Down
37 changes: 37 additions & 0 deletions Sources/ParseSwift/Coding/Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// File.swift
pranjalsatija marked this conversation as resolved.
Show resolved Hide resolved
//
pranjalsatija marked this conversation as resolved.
Show resolved Hide resolved
//
// Created by Pranjal Satija on 7/19/20.
//

import Foundation

// MARK: Date
internal extension Date {
func parseFormatted() -> String {
return ParseCoding.dateFormatter.string(from: self)
}

var parseRepresentation: [String: String] {
return ["__type": "Date", "iso": parseFormatted()]
}
}

// MARK: JSONEncoder
extension JSONEncoder {
func encodeAsString<T>(_ value: T) throws -> String where T: Encodable {
guard let string = String(data: try encode(value), encoding: .utf8) else {
throw ParseError(code: .unknownError, message: "Unable to encode object...")
}

return string
}
}

// MARK: ParseObject
internal extension ParseObject {
func getEncoder() -> ParseEncoder {
return ParseCoding.parseEncoder()
}
}
80 changes: 80 additions & 0 deletions Sources/ParseSwift/Coding/ParseCoding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// ParseObjectType.swift
pranjalsatija marked this conversation as resolved.
Show resolved Hide resolved
// ParseSwift
//
// Created by Florent Vilmart on 17-07-24.
// Copyright © 2017 Parse. All rights reserved.
//

import Foundation

// MARK: ParseCoding
internal enum ParseCoding {}

// MARK: Coders
extension ParseCoding {
private static let forbiddenKeys = Set(["createdAt", "updatedAt", "objectId", "className"])

static func jsonEncoder() -> JSONEncoder {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = dateEncodingStrategy
return encoder
}

static func jsonDecoder() -> JSONDecoder {
let encoder = JSONDecoder()
pranjalsatija marked this conversation as resolved.
Show resolved Hide resolved
encoder.dateDecodingStrategy = dateDecodingStrategy
return encoder
}

static func parseEncoder() -> ParseEncoder {
let encoder = ParseEncoder()
encoder.dateEncodingStrategy = dateEncodingStrategy
encoder.shouldEncodeKey = { (key, path) -> Bool in
if path.count == 0 // top level
&& Self.forbiddenKeys.contains(key) {
return false
}
return true
}

return encoder
}
}

// MARK: Dates
extension ParseCoding {
enum DateEncodingKeys: String, CodingKey {
case iso
case type = "__type"
}

static let dateFormatter: DateFormatter = {
var dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
return dateFormatter
}()

static let dateEncodingStrategy: JSONEncoder.DateEncodingStrategy = .custom({ (date, enc) in
var container = enc.container(keyedBy: DateEncodingKeys.self)
try container.encode("Date", forKey: .type)
let dateString = dateFormatter.string(from: date)
try container.encode(dateString, forKey: .iso)
})

static let dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .custom({ (dec) -> Date in
pranjalsatija marked this conversation as resolved.
Show resolved Hide resolved
do {
let container = try dec.singleValueContainer()
let decodedString = try container.decode(String.self)
return dateFormatter.date(from: decodedString)!
pranjalsatija marked this conversation as resolved.
Show resolved Hide resolved
} catch let error {
let container = try dec.container(keyedBy: DateEncodingKeys.self)
if let decoded = try container.decodeIfPresent(String.self, forKey: .iso) {
return dateFormatter.date(from: decoded)!
pranjalsatija marked this conversation as resolved.
Show resolved Hide resolved
}
}

throw ParseError(code: .unknownError, message: "unable to decode")
})
}
Loading