From 98b1ed19ab290ecdd59a13febcb609a955fbe16c Mon Sep 17 00:00:00 2001 From: Corey Date: Thu, 23 Sep 2021 09:09:42 -0400 Subject: [PATCH 1/8] Add emptyObject method to ParseObject --- Sources/ParseSwift/Objects/ParseObject.swift | 14 ++++ Sources/ParseSwift/Protocols/Objectable.swift | 5 ++ Tests/ParseSwiftTests/IOS13Tests.swift | 1 + .../ParseEncoderExtraTests.swift | 3 + .../ParseSwiftTests/ParseLiveQueryTests.swift | 2 + .../ParseObjectBatchTests.swift | 3 + .../ParseSwiftTests/ParseObjectCombine.swift | 1 + .../ParseObjectCustomObjectIdTests.swift | 4 ++ Tests/ParseSwiftTests/ParseObjectTests.swift | 68 ++++++++++++++++++- .../ParseOperationCombineTests.swift | 1 + .../ParseSwiftTests/ParseOperationTests.swift | 7 ++ .../ParsePointerCombineTests.swift | 3 + Tests/ParseSwiftTests/ParsePointerTests.swift | 4 ++ .../ParseQueryCombineTests.swift | 2 + Tests/ParseSwiftTests/ParseQueryTests.swift | 3 + .../ParseQueryViewModelTests.swift | 1 + .../ParseSwiftTests/ParseRelationTests.swift | 7 ++ Tests/ParseSwiftTests/ParseRoleTests.swift | 8 +++ 18 files changed, 136 insertions(+), 1 deletion(-) diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index f1a07b7fc..da693d48d 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -55,6 +55,20 @@ extension ParseObject { public func toPointer() throws -> Pointer { return try Pointer(self) } + + /** + Gets an empy version of the respective object, This can be used when you only need to update a + a subset of the fields of an object as oppose to updating every field of an object. + - note: Using an empty object and updating a subset of the fields reduces the amount of data sent between + client and server when using `save` and `saveAll` to update an object. + - returns: Self + */ + public func emptyObject() -> Self { + var object = Self() + object.objectId = objectId + object.createdAt = createdAt + return object + } } // MARK: Batch Support diff --git a/Sources/ParseSwift/Protocols/Objectable.swift b/Sources/ParseSwift/Protocols/Objectable.swift index b1a8e4b1e..c304e114e 100644 --- a/Sources/ParseSwift/Protocols/Objectable.swift +++ b/Sources/ParseSwift/Protocols/Objectable.swift @@ -35,6 +35,11 @@ public protocol Objectable: ParseType, Decodable { The ACL for this object. */ var ACL: ParseACL? { get set } + + /** + Default initializer of this `ParseObject`. + */ + init() } extension Objectable { diff --git a/Tests/ParseSwiftTests/IOS13Tests.swift b/Tests/ParseSwiftTests/IOS13Tests.swift index 627032f7c..041280579 100644 --- a/Tests/ParseSwiftTests/IOS13Tests.swift +++ b/Tests/ParseSwiftTests/IOS13Tests.swift @@ -47,6 +47,7 @@ class IOS13Tests: XCTestCase { // swiftlint:disable:this type_body_length var levels: [Level]? //custom initializers + init() {} init (objectId: String?) { self.objectId = objectId } diff --git a/Tests/ParseSwiftTests/ParseEncoderTests/ParseEncoderExtraTests.swift b/Tests/ParseSwiftTests/ParseEncoderTests/ParseEncoderExtraTests.swift index 07eee4b7b..82596cfd3 100644 --- a/Tests/ParseSwiftTests/ParseEncoderTests/ParseEncoderExtraTests.swift +++ b/Tests/ParseSwiftTests/ParseEncoderTests/ParseEncoderExtraTests.swift @@ -21,6 +21,9 @@ class ParseEncoderTests: XCTestCase { var score: Int //: a custom initializer + init() { + self.score = 5 + } init(score: Int) { self.score = score } diff --git a/Tests/ParseSwiftTests/ParseLiveQueryTests.swift b/Tests/ParseSwiftTests/ParseLiveQueryTests.swift index 9e4bc9a11..f569da582 100644 --- a/Tests/ParseSwiftTests/ParseLiveQueryTests.swift +++ b/Tests/ParseSwiftTests/ParseLiveQueryTests.swift @@ -23,6 +23,8 @@ class ParseLiveQueryTests: XCTestCase { var score: Int = 0 //custom initializer + init() {} + init(score: Int) { self.score = score } diff --git a/Tests/ParseSwiftTests/ParseObjectBatchTests.swift b/Tests/ParseSwiftTests/ParseObjectBatchTests.swift index 7990b9c8b..4bbf08065 100644 --- a/Tests/ParseSwiftTests/ParseObjectBatchTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectBatchTests.swift @@ -23,6 +23,9 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le var score: Int = 0 //custom initializers + init() { + self.score = 5 + } init(score: Int) { self.score = score } diff --git a/Tests/ParseSwiftTests/ParseObjectCombine.swift b/Tests/ParseSwiftTests/ParseObjectCombine.swift index 54c6b6e63..e67eec57a 100644 --- a/Tests/ParseSwiftTests/ParseObjectCombine.swift +++ b/Tests/ParseSwiftTests/ParseObjectCombine.swift @@ -28,6 +28,7 @@ class ParseObjectCombineTests: XCTestCase { // swiftlint:disable:this type_body_ var player: String? //custom initializers + init() {} init (objectId: String?) { self.objectId = objectId } diff --git a/Tests/ParseSwiftTests/ParseObjectCustomObjectIdTests.swift b/Tests/ParseSwiftTests/ParseObjectCustomObjectIdTests.swift index 5399d3e8a..80ee964db 100644 --- a/Tests/ParseSwiftTests/ParseObjectCustomObjectIdTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectCustomObjectIdTests.swift @@ -37,6 +37,7 @@ class ParseObjectCustomObjectIdTests: XCTestCase { // swiftlint:disable:this typ var levels: [Level]? //custom initializers + init() {} init (objectId: String?) { self.objectId = objectId } @@ -64,6 +65,9 @@ class ParseObjectCustomObjectIdTests: XCTestCase { // swiftlint:disable:this typ var profilePicture: ParseFile? //: a custom initializer + init() { + self.score = GameScore() + } init(score: GameScore) { self.score = score } diff --git a/Tests/ParseSwiftTests/ParseObjectTests.swift b/Tests/ParseSwiftTests/ParseObjectTests.swift index ee3d7be41..ed84dfcaa 100644 --- a/Tests/ParseSwiftTests/ParseObjectTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectTests.swift @@ -42,7 +42,9 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length var nextLevel: Level? //custom initializers - init (objectId: String?) { + init() {} + + init(objectId: String?) { self.objectId = objectId } init(score: Int) { @@ -69,6 +71,10 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length var profilePicture: ParseFile? //: a custom initializer + init() { + self.score = GameScore() + } + init(score: GameScore) { self.score = score } @@ -102,6 +108,10 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length var game: GameClass? //: a custom initializer + required init() { + self.score = 5 + } + init(score: Int) { self.score = score } @@ -154,6 +164,10 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length var name = "Hello" //: a custom initializer + required init() { + self.score = GameScoreClass() + } + init(score: GameScoreClass) { self.score = score } @@ -226,6 +240,15 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length wait(for: [expectation2], timeout: 20.0) } + func testEmptyObject() throws { + var score = GameScore(score: 19, name: "fire") + score.objectId = "yolo" + score.createdAt = Date() + let empty = score.emptyObject() + XCTAssertTrue(score.hasSameObjectId(as: empty)) + XCTAssertEqual(score.createdAt, empty.createdAt) + } + func testFetchCommand() { var score = GameScore(score: 10) let className = score.className @@ -546,6 +569,49 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length let decoded = try XCTUnwrap(String(data: encoded, encoding: .utf8)) XCTAssertEqual(decoded, expected) } + + func testUpdateCommandEmptyObject() throws { + var score = GameScore(score: 10) + let className = score.className + let objectId = "yarr" + score.objectId = objectId + score.createdAt = Date() + score.updatedAt = score.createdAt + + let command = try score.emptyObject().saveCommand() + XCTAssertNotNil(command) + XCTAssertEqual(command.path.urlComponent, "/classes/\(className)/\(objectId)") + XCTAssertEqual(command.method, API.Method.PUT) + XCTAssertNil(command.params) + + guard let body = command.body else { + XCTFail("Should be able to unwrap") + return + } + + let expected = "{}" + let encoded = try ParseCoding.parseEncoder() + .encode(body, collectChildren: false, + objectsSavedBeforeThisOne: nil, + filesSavedBeforeThisOne: nil).encoded + let decoded = try XCTUnwrap(String(data: encoded, encoding: .utf8)) + XCTAssertEqual(decoded, expected) + + var empty = score.emptyObject() + empty.player = "Jennifer" + let command2 = try empty.saveCommand() + guard let body2 = command2.body else { + XCTFail("Should be able to unwrap") + return + } + let expected2 = "{\"player\":\"Jennifer\"}" + let encoded2 = try ParseCoding.parseEncoder() + .encode(body2, collectChildren: false, + objectsSavedBeforeThisOne: nil, + filesSavedBeforeThisOne: nil).encoded + let decoded2 = try XCTUnwrap(String(data: encoded2, encoding: .utf8)) + XCTAssertEqual(decoded2, expected2) + } #endif func testSave() { // swiftlint:disable:this function_body_length diff --git a/Tests/ParseSwiftTests/ParseOperationCombineTests.swift b/Tests/ParseSwiftTests/ParseOperationCombineTests.swift index d0e224890..bb41e3643 100644 --- a/Tests/ParseSwiftTests/ParseOperationCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseOperationCombineTests.swift @@ -28,6 +28,7 @@ class ParseOperationCombineTests: XCTestCase { // swiftlint:disable:this type_bo var player: String? //custom initializers + init() {} init (objectId: String?) { self.objectId = objectId } diff --git a/Tests/ParseSwiftTests/ParseOperationTests.swift b/Tests/ParseSwiftTests/ParseOperationTests.swift index dfa6ad30c..f4fb13cfd 100644 --- a/Tests/ParseSwiftTests/ParseOperationTests.swift +++ b/Tests/ParseSwiftTests/ParseOperationTests.swift @@ -25,6 +25,10 @@ class ParseOperationTests: XCTestCase { var previous: [Level]? //custom initializers + init() { + self.score = 5 + } + init(score: Int) { self.score = score } @@ -42,6 +46,9 @@ class ParseOperationTests: XCTestCase { var members = [String]() //custom initializers + init() { + self.level = 5 + } init(level: Int) { self.level = level } diff --git a/Tests/ParseSwiftTests/ParsePointerCombineTests.swift b/Tests/ParseSwiftTests/ParsePointerCombineTests.swift index cdb1540ab..8bff44f90 100644 --- a/Tests/ParseSwiftTests/ParsePointerCombineTests.swift +++ b/Tests/ParseSwiftTests/ParsePointerCombineTests.swift @@ -27,6 +27,9 @@ class ParsePointerCombineTests: XCTestCase { var score: Int //: a custom initializer + init() { + self.score = 5 + } init(score: Int) { self.score = score } diff --git a/Tests/ParseSwiftTests/ParsePointerTests.swift b/Tests/ParseSwiftTests/ParsePointerTests.swift index 6bbe1f5f6..337bb8d05 100644 --- a/Tests/ParseSwiftTests/ParsePointerTests.swift +++ b/Tests/ParseSwiftTests/ParsePointerTests.swift @@ -25,6 +25,10 @@ class ParsePointerTests: XCTestCase { var others: [Pointer]? //: a custom initializer + init() { + self.score = 5 + } + init(score: Int) { self.score = score } diff --git a/Tests/ParseSwiftTests/ParseQueryCombineTests.swift b/Tests/ParseSwiftTests/ParseQueryCombineTests.swift index 49e1593f4..a72ad6415 100644 --- a/Tests/ParseSwiftTests/ParseQueryCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseQueryCombineTests.swift @@ -28,6 +28,8 @@ class ParseQueryCombineTests: XCTestCase { // swiftlint:disable:this type_body_l var player: String? //custom initializers + init() {} + init (objectId: String?) { self.objectId = objectId } diff --git a/Tests/ParseSwiftTests/ParseQueryTests.swift b/Tests/ParseSwiftTests/ParseQueryTests.swift index ac8e63f42..832d20106 100644 --- a/Tests/ParseSwiftTests/ParseQueryTests.swift +++ b/Tests/ParseSwiftTests/ParseQueryTests.swift @@ -23,6 +23,9 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length var score: Int //: a custom initializer + init() { + self.score = 5 + } init(score: Int) { self.score = score } diff --git a/Tests/ParseSwiftTests/ParseQueryViewModelTests.swift b/Tests/ParseSwiftTests/ParseQueryViewModelTests.swift index e1976e479..f4d1d44f0 100644 --- a/Tests/ParseSwiftTests/ParseQueryViewModelTests.swift +++ b/Tests/ParseSwiftTests/ParseQueryViewModelTests.swift @@ -24,6 +24,7 @@ class ParseQueryViewModelTests: XCTestCase { var score: Int = 0 //custom initializer + init() {} init(score: Int) { self.score = score } diff --git a/Tests/ParseSwiftTests/ParseRelationTests.swift b/Tests/ParseSwiftTests/ParseRelationTests.swift index 41097fdce..2cd3016b3 100644 --- a/Tests/ParseSwiftTests/ParseRelationTests.swift +++ b/Tests/ParseSwiftTests/ParseRelationTests.swift @@ -25,6 +25,9 @@ class ParseRelationTests: XCTestCase { var levels: [String]? //custom initializers + init() { + self.score = 5 + } init(score: Int) { self.score = score } @@ -42,6 +45,10 @@ class ParseRelationTests: XCTestCase { var members = [String]() //custom initializers + init() { + self.level = 5 + } + init(level: Int) { self.level = level } diff --git a/Tests/ParseSwiftTests/ParseRoleTests.swift b/Tests/ParseSwiftTests/ParseRoleTests.swift index 14dddc19f..f8395dda1 100644 --- a/Tests/ParseSwiftTests/ParseRoleTests.swift +++ b/Tests/ParseSwiftTests/ParseRoleTests.swift @@ -24,6 +24,10 @@ class ParseRoleTests: XCTestCase { var levels: [String]? //custom initializers + init() { + self.score = 5 + } + init(score: Int) { self.score = score } @@ -76,6 +80,10 @@ class ParseRoleTests: XCTestCase { var members = [String]() //custom initializers + init() { + self.level = 5 + } + init(level: Int) { self.level = level } From be1382bff63034e104a2a5fd86a2262262ea1557 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Thu, 23 Sep 2021 10:55:59 -0400 Subject: [PATCH 2/8] Update playground examples --- CHANGELOG.md | 9 +++++- .../Contents.swift | 23 +++++++++++---- .../Contents.swift | 4 +++ .../Contents.swift | 4 +++ .../Contents.swift | 4 +++ .../Contents.swift | 10 +++++-- .../Contents.swift | 4 +++ .../Contents.swift | 4 +++ .../Contents.swift | 10 ++++--- .../Contents.swift | 28 +++++++++++++------ .../5 - ACL.xcplaygroundpage/Contents.swift | 10 ++++++- .../Contents.swift | 14 ++++++---- .../Contents.swift | 7 ++++- .../Contents.swift | 14 ++++++++++ .../9 - Files.xcplaygroundpage/Contents.swift | 6 +++- Sources/ParseSwift/Objects/ParseObject.swift | 7 ++++- Sources/ParseSwift/ParseConstants.swift | 2 +- Sources/ParseSwift/Protocols/Objectable.swift | 5 ---- 18 files changed, 130 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 358c52e9f..0e73dd21e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,21 @@ # Parse-Swift Changelog ### main -[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.9.10...main) +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.10.0...main) * _Contributing to this repo? Add info about your change here to be included in the next release_ +### 1.10.0 +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.9.9...1.10.0) + +__Improvements__ +- (Breaking Change) Provide ParseObject method emptyObject that makes it easy to send only modified keys to the server. This change "might" be breaking depending on your implementation as it requires ParseObjects to now have an empty initializer, init() ([#243](https://github.com/parse-community/Parse-Swift/pull/243)), thanks to [Corey Baker](https://github.com/cbaker6). + __Fixes__ - ParseUser shouldn't send email if it hasn't been modified or else email verification is resent ([#241](https://github.com/parse-community/Parse-Swift/pull/241)), thanks to [Corey Baker](https://github.com/cbaker6). ### 1.9.10 [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.9.9...1.9.10) + __Fixes__ - ParseInstallation can't be retreived from Keychain after the first fun ([#236](https://github.com/parse-community/Parse-Swift/pull/236)), thanks to [Corey Baker](https://github.com/cbaker6). diff --git a/ParseSwift.playground/Pages/1 - Your first Object.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/1 - Your first Object.xcplaygroundpage/Contents.swift index e95c47028..a18465adf 100644 --- a/ParseSwift.playground/Pages/1 - Your first Object.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/1 - Your first Object.xcplaygroundpage/Contents.swift @@ -39,8 +39,12 @@ struct GameScore: ParseObject { //: Your own properties. var score: Int = 0 +} + +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { - //: Custom initializer. init(score: Int) { self.score = score } @@ -62,6 +66,11 @@ struct GameData: ParseObject { //: `ParseBytes` needs to be a part of the original schema //: or else you will need your masterKey to force an upgrade. var bytes: ParseBytes? +} + +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameData { init (bytes: ParseBytes?, polygon: ParsePolygon) { self.bytes = bytes @@ -87,9 +96,11 @@ score.save { result in assert(savedScore.score == 10) /*: To modify, need to make it a var as the value type - was initialized as immutable. + was initialized as immutable. Using `emptyObject` + allows you to only send the updated keys to the + parse server as opposed to the whole object. */ - var changedScore = savedScore + var changedScore = savedScore.emptyObject() changedScore.score = 200 changedScore.save { result in switch result { @@ -177,9 +188,11 @@ assert(savedScore?.updatedAt != nil) assert(savedScore?.score == 10) /*: To modify, need to make it a var as the value type - was initialized as immutable. + was initialized as immutable. Using `emptyObject` + allows you to only send the updated keys to the + parse server as opposed to the whole object. */ -guard var changedScore = savedScore else { +guard var changedScore = savedScore?.emptyObject() else { fatalError() } changedScore.score = 200 diff --git a/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift index 5e4ba6b0d..a3357652a 100644 --- a/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift @@ -94,7 +94,11 @@ struct GameScore: ParseObject { //: Your own properties. var score: Int = 0 +} +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { //: Custom initializer. init(score: Int) { self.score = score diff --git a/ParseSwift.playground/Pages/11 - LiveQuery.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/11 - LiveQuery.xcplaygroundpage/Contents.swift index 0322e9fee..115050a78 100644 --- a/ParseSwift.playground/Pages/11 - LiveQuery.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/11 - LiveQuery.xcplaygroundpage/Contents.swift @@ -21,7 +21,11 @@ struct GameScore: ParseObject { var score: Int = 0 var location: ParseGeoPoint? var name: String? +} +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { //: Custom initializer. init(name: String, score: Int) { self.name = name diff --git a/ParseSwift.playground/Pages/13 - Operations.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/13 - Operations.xcplaygroundpage/Contents.swift index 1c17e44f2..c10a70c9a 100644 --- a/ParseSwift.playground/Pages/13 - Operations.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/13 - Operations.xcplaygroundpage/Contents.swift @@ -22,7 +22,11 @@ struct GameScore: ParseObject { //: Your own properties. var score: Int = 0 +} +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { //: Custom initializer. init(score: Int) { self.score = score diff --git a/ParseSwift.playground/Pages/15 - Custom ObjectId.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/15 - Custom ObjectId.xcplaygroundpage/Contents.swift index 316ffc2f5..424650624 100644 --- a/ParseSwift.playground/Pages/15 - Custom ObjectId.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/15 - Custom ObjectId.xcplaygroundpage/Contents.swift @@ -30,7 +30,11 @@ struct GameScore: ParseObject { //: Your own properties. var score: Int = 0 +} +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { //: Custom initializer. init(objectId: String, score: Int) { self.objectId = objectId @@ -64,9 +68,11 @@ score.save { result in print("Saved score: \(savedScore)") /*: To modify, need to make it a var as the value type - was initialized as immutable. + was initialized as immutable. Using `emptyObject` + allows you to only send the updated keys to the + parse server as opposed to the whole object. */ - var changedScore = savedScore + var changedScore = savedScore.emptyObject() changedScore.score = 200 changedScore.save { result in switch result { diff --git a/ParseSwift.playground/Pages/17 - SwiftUI - Finding Objects.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/17 - SwiftUI - Finding Objects.xcplaygroundpage/Contents.swift index 5c5759382..67502511d 100644 --- a/ParseSwift.playground/Pages/17 - SwiftUI - Finding Objects.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/17 - SwiftUI - Finding Objects.xcplaygroundpage/Contents.swift @@ -39,7 +39,11 @@ struct GameScore: ParseObject, Identifiable { var location: ParseGeoPoint? var name: String? var myFiles: [ParseFile]? +} +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { //: Custom initializer. init(name: String, score: Int) { self.name = name diff --git a/ParseSwift.playground/Pages/18 - SwiftUI - Finding Objects With Custom ViewModel.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/18 - SwiftUI - Finding Objects With Custom ViewModel.xcplaygroundpage/Contents.swift index f13d0ec7a..329e3fb2b 100644 --- a/ParseSwift.playground/Pages/18 - SwiftUI - Finding Objects With Custom ViewModel.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/18 - SwiftUI - Finding Objects With Custom ViewModel.xcplaygroundpage/Contents.swift @@ -39,7 +39,11 @@ struct GameScore: ParseObject, Identifiable { var score: Int = 0 var location: ParseGeoPoint? var name: String? +} +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { //: Custom initializer. init(name: String, score: Int) { self.name = name diff --git a/ParseSwift.playground/Pages/19 - SwiftUI - LiveQuery.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/19 - SwiftUI - LiveQuery.xcplaygroundpage/Contents.swift index 6c24f73c6..ab24881d9 100644 --- a/ParseSwift.playground/Pages/19 - SwiftUI - LiveQuery.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/19 - SwiftUI - LiveQuery.xcplaygroundpage/Contents.swift @@ -29,7 +29,11 @@ struct GameScore: ParseObject { var score: Int = 0 var location: ParseGeoPoint? var name: String? +} +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { //: Custom initializer. init(name: String, score: Int) { self.name = name @@ -75,9 +79,7 @@ struct ContentView: View { Text("Not subscribed to a query") } - Spacer() - - Text("Update GameScore in Parse Dashboard to see changes here") + Text("Update GameScore in Parse Dashboard to see changes here:") Button(action: { try? query.unsubscribe() @@ -88,8 +90,8 @@ struct ContentView: View { .foregroundColor(.white) .padding() .cornerRadius(20.0) - .frame(width: 300, height: 50) }) + Spacer() } } } diff --git a/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift index d9f3c9670..d89be2780 100644 --- a/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift @@ -32,7 +32,11 @@ struct User: ParseUser { var score: GameScore? var targetScore: GameScore? var allScores: [GameScore]? +} +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension User { //: Custom init for signup. init(username: String, password: String, email: String) { self.username = username @@ -51,7 +55,11 @@ struct GameScore: ParseObject { //: Your own properties. var score: Int? = 0 +} +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { //: Custom initializer. init(score: Int) { self.score = score @@ -95,12 +103,15 @@ User.login(username: "hello", password: "world") { result in Asynchrounously - Performs work on background queue and returns to specified callbackQueue. If no callbackQueue is specified it returns to main queue. + Using `emptyObject` allows you to only send the updated keys to the + parse server as opposed to the whole object. */ -User.current?.customKey = "myCustom" -User.current?.score = GameScore(score: 12) -User.current?.targetScore = GameScore(score: 100) -User.current?.allScores = [GameScore(score: 5), GameScore(score: 8)] -User.current?.save { result in +var currentUser = User.current?.emptyObject() +currentUser?.customKey = "myCustom" +currentUser?.score = GameScore(score: 12) +currentUser?.targetScore = GameScore(score: 100) +currentUser?.allScores = [GameScore(score: 5), GameScore(score: 8)] +currentUser?.save { result in switch result { case .success(let updatedUser): @@ -199,9 +210,10 @@ User.anonymous.login { result in } //: Convert the anonymous user to a real new user. -User.current?.username = "bye" -User.current?.password = "world" -User.current?.signup { result in +var currentUser2 = User.current?.emptyObject() +currentUser2?.username = "bye" +currentUser2?.password = "world" +currentUser2?.signup { result in switch result { case .success(let user): diff --git a/ParseSwift.playground/Pages/5 - ACL.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/5 - ACL.xcplaygroundpage/Contents.swift index c7c358434..7a303842e 100644 --- a/ParseSwift.playground/Pages/5 - ACL.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/5 - ACL.xcplaygroundpage/Contents.swift @@ -33,7 +33,15 @@ struct GameScore: ParseObject { //: Your own properties var score: Int - //: a custom initializer + init() { + self.score = 0 + } +} + +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { + //: Custom initializer. init(score: Int) { self.score = score } diff --git a/ParseSwift.playground/Pages/6 - Installation.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/6 - Installation.xcplaygroundpage/Contents.swift index bf44ff8aa..a3d7b1576 100644 --- a/ParseSwift.playground/Pages/6 - Installation.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/6 - Installation.xcplaygroundpage/Contents.swift @@ -42,8 +42,9 @@ struct Installation: ParseInstallation { designated callbackQueue. If no callbackQueue is specified it returns to main queue. */ -Installation.current?.customKey = "myCustomInstallationKey2" -Installation.current?.save { results in +var currentInstallation = Installation.current +currentInstallation?.customKey = "myCustomInstallationKey2" +currentInstallation?.save { results in switch results { case .success(let updatedInstallation): @@ -56,10 +57,13 @@ Installation.current?.save { results in /*: Update your `ParseInstallation` `customKey` value. Performs work on background queue and returns to designated on designated callbackQueue. If no callbackQueue is specified it - returns to main queue. + returns to main queue. Using `emptyObject` allows you to only + send the updated keys to the parse server as opposed to the + whole object. */ -Installation.current?.customKey = "updatedValue" -Installation.current?.save { results in +currentInstallation = currentInstallation?.emptyObject() +currentInstallation?.customKey = "updatedValue" +currentInstallation?.save { results in switch results { case .success(let updatedInstallation): diff --git a/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift index a96639bdc..be7f9da72 100644 --- a/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift @@ -21,10 +21,15 @@ struct GameScore: ParseObject { var updatedAt: Date? var ACL: ParseACL? var location: ParseGeoPoint? + //: Your own properties var score: Int? +} - //: A custom initializer. +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { + //: Custom initializer. init(score: Int) { self.score = score } diff --git a/ParseSwift.playground/Pages/8 - Pointers.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/8 - Pointers.xcplaygroundpage/Contents.swift index a792853ff..30cd238fc 100644 --- a/ParseSwift.playground/Pages/8 - Pointers.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/8 - Pointers.xcplaygroundpage/Contents.swift @@ -24,6 +24,11 @@ struct Book: ParseObject { //: Your own properties. var title: String? +} + +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension Book { init(title: String) { self.title = title @@ -42,6 +47,15 @@ struct Author: ParseObject { var book: Book var otherBooks: [Book]? + init() { + self.name = "hello" + self.book = Book() + } +} + +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension Author { init(name: String, book: Book) { self.name = name self.book = book diff --git a/ParseSwift.playground/Pages/9 - Files.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/9 - Files.xcplaygroundpage/Contents.swift index 5fa5d11b0..d1231d133 100644 --- a/ParseSwift.playground/Pages/9 - Files.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/9 - Files.xcplaygroundpage/Contents.swift @@ -25,8 +25,12 @@ struct GameScore: ParseObject { var score: Int = 0 var profilePicture: ParseFile? var myData: ParseFile? +} - //custom initializer +//: It's recommended to place custom initializers in an extension +//: to preserve the convenience initializer. +extension GameScore { + //: Custom initializer. init(score: Int) { self.score = score } diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index da693d48d..bf099b819 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -32,7 +32,12 @@ public protocol ParseObject: Objectable, Deletable, Hashable, CustomDebugStringConvertible, - CustomStringConvertible {} + CustomStringConvertible { + /** + Default initializer of this `ParseObject`. + */ + init() +} // MARK: Default Implementations extension ParseObject { diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index 7d8d20b60..4f6b6e419 100644 --- a/Sources/ParseSwift/ParseConstants.swift +++ b/Sources/ParseSwift/ParseConstants.swift @@ -10,7 +10,7 @@ import Foundation enum ParseConstants { static let sdk = "swift" - static let version = "1.9.9" + static let version = "1.9.11" static let fileManagementDirectory = "parse/" static let fileManagementPrivateDocumentsDirectory = "Private Documents/" static let fileManagementLibraryDirectory = "Library/" diff --git a/Sources/ParseSwift/Protocols/Objectable.swift b/Sources/ParseSwift/Protocols/Objectable.swift index c304e114e..b1a8e4b1e 100644 --- a/Sources/ParseSwift/Protocols/Objectable.swift +++ b/Sources/ParseSwift/Protocols/Objectable.swift @@ -35,11 +35,6 @@ public protocol Objectable: ParseType, Decodable { The ACL for this object. */ var ACL: ParseACL? { get set } - - /** - Default initializer of this `ParseObject`. - */ - init() } extension Objectable { From 80a743486e16defd526b3be36e9140e1f970ee46 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Thu, 23 Sep 2021 11:52:12 -0400 Subject: [PATCH 3/8] Fix bug in User playground example that prevented signup due to CLP's --- .../Pages/4 - User - Continued.xcplaygroundpage/Contents.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift index d89be2780..2ad90e280 100644 --- a/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift @@ -156,7 +156,7 @@ do { //: you should create an instance of your user first. var newUser = User(username: "parse", password: "aPassword*", email: "parse@parse.com") //: Add any other additional information. -newUser.targetScore = .init(score: 40) +newUser.customKey = "mind" newUser.signup { result in switch result { From cc2e8efeafee9e09a367accc9772e814ba2ccfe7 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Thu, 23 Sep 2021 12:12:51 -0400 Subject: [PATCH 4/8] Nits --- Sources/ParseSwift/Objects/ParseObject.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index bf099b819..843e3282c 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -34,7 +34,7 @@ public protocol ParseObject: Objectable, CustomDebugStringConvertible, CustomStringConvertible { /** - Default initializer of this `ParseObject`. + Default initializer of this object. */ init() } @@ -54,7 +54,7 @@ extension ParseObject { } /** - Gets a Pointer referencing this Object. + Gets a Pointer referencing this object. - returns: Pointer */ public func toPointer() throws -> Pointer { @@ -62,10 +62,10 @@ extension ParseObject { } /** - Gets an empy version of the respective object, This can be used when you only need to update a + Gets an empty version of the respective object. This can be used when you only need to update a a subset of the fields of an object as oppose to updating every field of an object. - note: Using an empty object and updating a subset of the fields reduces the amount of data sent between - client and server when using `save` and `saveAll` to update an object. + client and server when using `save` and `saveAll` to update objects. - returns: Self */ public func emptyObject() -> Self { From 601e56c7f3fd7d39ae7fdcfc5ec14a6e62eccabc Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Thu, 23 Sep 2021 12:14:24 -0400 Subject: [PATCH 5/8] more nits --- Sources/ParseSwift/Objects/ParseObject.swift | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index 843e3282c..8ead1b0a1 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -44,9 +44,7 @@ extension ParseObject { /** Determines if two objects have the same objectId. - - parameter as: Object to compare. - - returns: Returns a `true` if the other object has the same `objectId` or `false` if unsuccessful. */ public func hasSameObjectId(as other: T) -> Bool { @@ -54,19 +52,19 @@ extension ParseObject { } /** - Gets a Pointer referencing this object. - - returns: Pointer + Gets a Pointer referencing this object. + - returns: Pointer */ public func toPointer() throws -> Pointer { return try Pointer(self) } /** - Gets an empty version of the respective object. This can be used when you only need to update a + Gets an empty version of the respective object. This can be used when you only need to update a a subset of the fields of an object as oppose to updating every field of an object. - note: Using an empty object and updating a subset of the fields reduces the amount of data sent between client and server when using `save` and `saveAll` to update objects. - - returns: Self + - returns: Self */ public func emptyObject() -> Self { var object = Self() From fd761cf06020c64cb6a50e28eca21aa192d4b19f Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Thu, 23 Sep 2021 12:30:17 -0400 Subject: [PATCH 6/8] Turn emptyObject into computed property --- .../Contents.swift | 4 +-- .../Contents.swift | 2 +- .../Contents.swift | 4 +-- .../Contents.swift | 2 +- Sources/ParseSwift/Objects/ParseObject.swift | 33 +++++++++---------- Tests/ParseSwiftTests/ParseObjectTests.swift | 6 ++-- 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/ParseSwift.playground/Pages/1 - Your first Object.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/1 - Your first Object.xcplaygroundpage/Contents.swift index a18465adf..c3aea56e2 100644 --- a/ParseSwift.playground/Pages/1 - Your first Object.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/1 - Your first Object.xcplaygroundpage/Contents.swift @@ -100,7 +100,7 @@ score.save { result in allows you to only send the updated keys to the parse server as opposed to the whole object. */ - var changedScore = savedScore.emptyObject() + var changedScore = savedScore.emptyObject changedScore.score = 200 changedScore.save { result in switch result { @@ -192,7 +192,7 @@ assert(savedScore?.score == 10) allows you to only send the updated keys to the parse server as opposed to the whole object. */ -guard var changedScore = savedScore?.emptyObject() else { +guard var changedScore = savedScore?.emptyObject else { fatalError() } changedScore.score = 200 diff --git a/ParseSwift.playground/Pages/15 - Custom ObjectId.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/15 - Custom ObjectId.xcplaygroundpage/Contents.swift index 424650624..18258b7d6 100644 --- a/ParseSwift.playground/Pages/15 - Custom ObjectId.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/15 - Custom ObjectId.xcplaygroundpage/Contents.swift @@ -72,7 +72,7 @@ score.save { result in allows you to only send the updated keys to the parse server as opposed to the whole object. */ - var changedScore = savedScore.emptyObject() + var changedScore = savedScore.emptyObject changedScore.score = 200 changedScore.save { result in switch result { diff --git a/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift index 2ad90e280..027558318 100644 --- a/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift @@ -106,7 +106,7 @@ User.login(username: "hello", password: "world") { result in Using `emptyObject` allows you to only send the updated keys to the parse server as opposed to the whole object. */ -var currentUser = User.current?.emptyObject() +var currentUser = User.current?.emptyObject currentUser?.customKey = "myCustom" currentUser?.score = GameScore(score: 12) currentUser?.targetScore = GameScore(score: 100) @@ -210,7 +210,7 @@ User.anonymous.login { result in } //: Convert the anonymous user to a real new user. -var currentUser2 = User.current?.emptyObject() +var currentUser2 = User.current?.emptyObject currentUser2?.username = "bye" currentUser2?.password = "world" currentUser2?.signup { result in diff --git a/ParseSwift.playground/Pages/6 - Installation.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/6 - Installation.xcplaygroundpage/Contents.swift index a3d7b1576..ea5bb4ab6 100644 --- a/ParseSwift.playground/Pages/6 - Installation.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/6 - Installation.xcplaygroundpage/Contents.swift @@ -61,7 +61,7 @@ currentInstallation?.save { results in send the updated keys to the parse server as opposed to the whole object. */ -currentInstallation = currentInstallation?.emptyObject() +currentInstallation = currentInstallation?.emptyObject currentInstallation?.customKey = "updatedValue" currentInstallation?.save { results in diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index 8ead1b0a1..1ab0e483d 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -40,14 +40,27 @@ public protocol ParseObject: Objectable, } // MARK: Default Implementations -extension ParseObject { +public extension ParseObject { + + /** + Gets an empty version of the respective object. This can be used when you only need to update a + a subset of the fields of an object as oppose to updating every field of an object. + - note: Using an empty object and updating a subset of the fields reduces the amount of data sent between + client and server when using `save` and `saveAll` to update objects. + */ + var emptyObject: Self { + var object = Self() + object.objectId = objectId + object.createdAt = createdAt + return object + } /** Determines if two objects have the same objectId. - parameter as: Object to compare. - returns: Returns a `true` if the other object has the same `objectId` or `false` if unsuccessful. */ - public func hasSameObjectId(as other: T) -> Bool { + func hasSameObjectId(as other: T) -> Bool { return other.className == className && other.objectId == objectId && objectId != nil } @@ -55,23 +68,9 @@ extension ParseObject { Gets a Pointer referencing this object. - returns: Pointer */ - public func toPointer() throws -> Pointer { + func toPointer() throws -> Pointer { return try Pointer(self) } - - /** - Gets an empty version of the respective object. This can be used when you only need to update a - a subset of the fields of an object as oppose to updating every field of an object. - - note: Using an empty object and updating a subset of the fields reduces the amount of data sent between - client and server when using `save` and `saveAll` to update objects. - - returns: Self - */ - public func emptyObject() -> Self { - var object = Self() - object.objectId = objectId - object.createdAt = createdAt - return object - } } // MARK: Batch Support diff --git a/Tests/ParseSwiftTests/ParseObjectTests.swift b/Tests/ParseSwiftTests/ParseObjectTests.swift index ed84dfcaa..4b2adb58c 100644 --- a/Tests/ParseSwiftTests/ParseObjectTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectTests.swift @@ -244,7 +244,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length var score = GameScore(score: 19, name: "fire") score.objectId = "yolo" score.createdAt = Date() - let empty = score.emptyObject() + let empty = score.emptyObject XCTAssertTrue(score.hasSameObjectId(as: empty)) XCTAssertEqual(score.createdAt, empty.createdAt) } @@ -578,7 +578,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length score.createdAt = Date() score.updatedAt = score.createdAt - let command = try score.emptyObject().saveCommand() + let command = try score.emptyObject.saveCommand() XCTAssertNotNil(command) XCTAssertEqual(command.path.urlComponent, "/classes/\(className)/\(objectId)") XCTAssertEqual(command.method, API.Method.PUT) @@ -597,7 +597,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length let decoded = try XCTUnwrap(String(data: encoded, encoding: .utf8)) XCTAssertEqual(decoded, expected) - var empty = score.emptyObject() + var empty = score.emptyObject empty.player = "Jennifer" let command2 = try empty.saveCommand() guard let body2 = command2.body else { From 1b9ebba57da0cc9f9456c7b0f5d049b0977915df Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Thu, 23 Sep 2021 12:31:34 -0400 Subject: [PATCH 7/8] nit --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e73dd21e..123e6a08a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.9.9...1.10.0) __Improvements__ -- (Breaking Change) Provide ParseObject method emptyObject that makes it easy to send only modified keys to the server. This change "might" be breaking depending on your implementation as it requires ParseObjects to now have an empty initializer, init() ([#243](https://github.com/parse-community/Parse-Swift/pull/243)), thanks to [Corey Baker](https://github.com/cbaker6). +- (Breaking Change) Provide ParseObject property, emptyObject, that makes it easy to send only modified keys to the server. This change "might" be breaking depending on your implementation as it requires ParseObjects to now have an empty initializer, init() ([#243](https://github.com/parse-community/Parse-Swift/pull/243)), thanks to [Corey Baker](https://github.com/cbaker6). __Fixes__ - ParseUser shouldn't send email if it hasn't been modified or else email verification is resent ([#241](https://github.com/parse-community/Parse-Swift/pull/241)), thanks to [Corey Baker](https://github.com/cbaker6). From 547c9b63dedc8b98bea6bc10982506133e46b565 Mon Sep 17 00:00:00 2001 From: Corey Baker Date: Thu, 23 Sep 2021 13:16:45 -0400 Subject: [PATCH 8/8] Fix SDK version --- Sources/ParseSwift/ParseConstants.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index 4f6b6e419..aed7969fd 100644 --- a/Sources/ParseSwift/ParseConstants.swift +++ b/Sources/ParseSwift/ParseConstants.swift @@ -10,7 +10,7 @@ import Foundation enum ParseConstants { static let sdk = "swift" - static let version = "1.9.11" + static let version = "1.10.0" static let fileManagementDirectory = "parse/" static let fileManagementPrivateDocumentsDirectory = "Private Documents/" static let fileManagementLibraryDirectory = "Library/"