Skip to content

feat: add ParseObjectMutable protocol #270

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

Merged
merged 6 commits into from
Oct 31, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

### main

__Improvements__
- Added the ParseObjectMutable protocol to make emptyObject more developer friendly ([#270](https://github.com/parse-community/Parse-Swift/pull/270)), thanks to [Damian Van de Kauter](https://github.com/novemTeam).


[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.0.3...main)
* _Contributing to this repo? Add info about your change here to be included in the next release_

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ do {
}

//: Create your own value typed `ParseObject`.
struct GameScore: ParseObject {
struct GameScore: ParseObjectMutable {
//: Those are required for Object
var objectId: String?
var createdAt: Date?
Expand All @@ -39,20 +39,6 @@ struct GameScore: ParseObject {

//: Your own properties.
var score: Int = 0

/*:
It's recommended the developer adds the emptyObject computed property or similar.
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. 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
}
}

//: It's recommended to place custom initializers in an extension
Expand Down Expand Up @@ -110,11 +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. Using `emptyObject`
was initialized as immutable. Using `mutable`
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.mutable
changedScore.score = 200
changedScore.save { result in
switch result {
Expand Down Expand Up @@ -202,11 +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. Using `emptyObject`
was initialized as immutable. Using `mutable`
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?.mutable else {
fatalError()
}
changedScore.score = 200
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ npm start -- --appId applicationId --clientKey clientKey --masterKey masterKey -
initializeParseCustomObjectId()

//: Create your own value typed `ParseObject`.
struct GameScore: ParseObject {
struct GameScore: ParseObjectMutable {
//: Those are required for Object
var objectId: String?
var createdAt: Date?
Expand All @@ -30,20 +30,6 @@ struct GameScore: ParseObject {

//: Your own properties.
var score: Int = 0

/*:
It's recommended the developer adds the emptyObject computed property or similar.
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. 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
}
}

//: It's recommended to place custom initializers in an extension
Expand Down Expand Up @@ -82,11 +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. Using `emptyObject`
was initialized as immutable. Using `mutable`
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.mutable
changedScore.score = 200
changedScore.save { result in
switch result {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import ParseSwift
PlaygroundPage.current.needsIndefiniteExecution = true
initializeParse()

struct User: ParseUser {
struct User: ParseUserMutable {
//: These are required for `ParseObject`.
var objectId: String?
var createdAt: Date?
Expand All @@ -32,20 +32,6 @@ struct User: ParseUser {
var score: GameScore?
var targetScore: GameScore?
var allScores: [GameScore]?

/*:
It's recommended the developer adds the emptyObject computed property or similar.
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. 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
}
}

//: It's recommended to place custom initializers in an extension
Expand Down Expand Up @@ -117,10 +103,10 @@ 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
Using `mutable` 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?.mutable
currentUser?.customKey = "myCustom"
currentUser?.score = GameScore(score: 12)
currentUser?.targetScore = GameScore(score: 100)
Expand Down Expand Up @@ -224,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?.mutable
currentUser2?.username = "bye"
currentUser2?.password = "world"
currentUser2?.signup { result in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import ParseSwift
PlaygroundPage.current.needsIndefiniteExecution = true
initializeParse()

struct Installation: ParseInstallation {
struct Installation: ParseInstallationMutable {
//: These are required for `ParseObject`.
var objectId: String?
var createdAt: Date?
Expand All @@ -35,20 +35,6 @@ struct Installation: ParseInstallation {

//: Your custom keys
var customKey: String?

/*:
It's recommended the developer adds the emptyObject computed property or similar.
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. 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
}
}

/*: Save your first `customKey` value to your `ParseInstallation`.
Expand All @@ -71,11 +57,11 @@ currentInstallation?.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. Using `emptyObject` allows you to only
returns to main queue. Using `mutable` allows you to only
send the updated keys to the parse server as opposed to the
whole object.
*/
currentInstallation = currentInstallation?.emptyObject
currentInstallation = currentInstallation?.mutable
currentInstallation?.customKey = "updatedValue"
currentInstallation?.save { results in

Expand Down
10 changes: 10 additions & 0 deletions ParseSwift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,10 @@
91F346C3269B88F7005727B6 /* ParseCloudViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91F346C2269B88F7005727B6 /* ParseCloudViewModelTests.swift */; };
91F346C4269B88F7005727B6 /* ParseCloudViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91F346C2269B88F7005727B6 /* ParseCloudViewModelTests.swift */; };
91F346C5269B88F7005727B6 /* ParseCloudViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91F346C2269B88F7005727B6 /* ParseCloudViewModelTests.swift */; };
CD106A00272D481800939151 /* ParseObjectMutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD1069FF272D481800939151 /* ParseObjectMutable.swift */; };
CD106A01272D481800939151 /* ParseObjectMutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD1069FF272D481800939151 /* ParseObjectMutable.swift */; };
CD106A02272D481800939151 /* ParseObjectMutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD1069FF272D481800939151 /* ParseObjectMutable.swift */; };
CD106A03272D481800939151 /* ParseObjectMutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD1069FF272D481800939151 /* ParseObjectMutable.swift */; };
F971F4F624DE381A006CB79B /* ParseEncoderExtraTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F971F4F524DE381A006CB79B /* ParseEncoderExtraTests.swift */; };
F97B45CE24D9C6F200F4A88B /* ParseCoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B424D9C6F200F4A88B /* ParseCoding.swift */; };
F97B45CF24D9C6F200F4A88B /* ParseCoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F97B45B424D9C6F200F4A88B /* ParseCoding.swift */; };
Expand Down Expand Up @@ -961,6 +965,7 @@
91F346B8269B766C005727B6 /* CloudViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudViewModel.swift; sourceTree = "<group>"; };
91F346BD269B77B5005727B6 /* CloudObservable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudObservable.swift; sourceTree = "<group>"; };
91F346C2269B88F7005727B6 /* ParseCloudViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCloudViewModelTests.swift; sourceTree = "<group>"; };
CD1069FF272D481800939151 /* ParseObjectMutable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseObjectMutable.swift; sourceTree = "<group>"; };
F971F4F524DE381A006CB79B /* ParseEncoderExtraTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseEncoderExtraTests.swift; sourceTree = "<group>"; };
F97B45B424D9C6F200F4A88B /* ParseCoding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseCoding.swift; sourceTree = "<group>"; };
F97B45B524D9C6F200F4A88B /* AnyDecodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyDecodable.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1287,6 +1292,7 @@
F97B45C824D9C6F200F4A88B /* Queryable.swift */,
F97B45C724D9C6F200F4A88B /* Savable.swift */,
70647E9B259E3A9A004C1004 /* ParseType.swift */,
CD1069FF272D481800939151 /* ParseObjectMutable.swift */,
91BB8FCE2690BA70005A6BA5 /* QueryObservable.swift */,
91F346BD269B77B5005727B6 /* CloudObservable.swift */,
);
Expand Down Expand Up @@ -2084,6 +2090,7 @@
703B090226BD9652005A112F /* ParseAnalytics+async.swift in Sources */,
703B093F26BF47AC005A112F /* ParseApple+async.swift in Sources */,
F97B45E624D9C6F200F4A88B /* Query.swift in Sources */,
CD106A00272D481800939151 /* ParseObjectMutable.swift in Sources */,
703B093526BF43D9005A112F /* ParseAnonymous+async.swift in Sources */,
705D950825BE4C08003EF6F8 /* SubscriptionCallback.swift in Sources */,
70C5509225B4A99100B5DBC2 /* AddRelation.swift in Sources */,
Expand Down Expand Up @@ -2293,6 +2300,7 @@
703B090326BD9652005A112F /* ParseAnalytics+async.swift in Sources */,
703B094026BF47AC005A112F /* ParseApple+async.swift in Sources */,
F97B45E724D9C6F200F4A88B /* Query.swift in Sources */,
CD106A01272D481800939151 /* ParseObjectMutable.swift in Sources */,
703B093626BF43D9005A112F /* ParseAnonymous+async.swift in Sources */,
705D950925BE4C08003EF6F8 /* SubscriptionCallback.swift in Sources */,
70C5509325B4A99100B5DBC2 /* AddRelation.swift in Sources */,
Expand Down Expand Up @@ -2597,6 +2605,7 @@
703B090526BD9652005A112F /* ParseAnalytics+async.swift in Sources */,
703B094226BF47AC005A112F /* ParseApple+async.swift in Sources */,
F97B466724D9C88600F4A88B /* SecureStorage.swift in Sources */,
CD106A03272D481800939151 /* ParseObjectMutable.swift in Sources */,
703B093826BF43D9005A112F /* ParseAnonymous+async.swift in Sources */,
705D950B25BE4C08003EF6F8 /* SubscriptionCallback.swift in Sources */,
70C5509525B4A99100B5DBC2 /* AddRelation.swift in Sources */,
Expand Down Expand Up @@ -2720,6 +2729,7 @@
703B090426BD9652005A112F /* ParseAnalytics+async.swift in Sources */,
703B094126BF47AC005A112F /* ParseApple+async.swift in Sources */,
F97B466624D9C88600F4A88B /* SecureStorage.swift in Sources */,
CD106A02272D481800939151 /* ParseObjectMutable.swift in Sources */,
703B093726BF43D9005A112F /* ParseAnonymous+async.swift in Sources */,
705D950A25BE4C08003EF6F8 /* SubscriptionCallback.swift in Sources */,
70C5509425B4A99100B5DBC2 /* AddRelation.swift in Sources */,
Expand Down
13 changes: 3 additions & 10 deletions Sources/ParseSwift/Objects/ParseObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,12 @@ import Foundation
If you are using value types the the compiler will assist you with conforming to `ParseObject` protocol. If you
are thinking of using reference types, see the warning.

It's recommended the developer adds the emptyObject computed property or similar.
It's recommended the developer uses the ParseObjectMutable protocol.
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. 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. You should add the following properties in your `ParseObject`'s:

var emptyObject: Self {
var object = Self()
object.objectId = objectId
object.createdAt = createdAt
return object
}

to update objects.

- important: It is recommended that all added properties be optional properties so they can eventually be used as
Parse `Pointer`'s. If a developer really wants to have a required key, they should require it on the server-side or
create methods to check the respective properties on the client-side before saving objects. See
Expand Down
76 changes: 76 additions & 0 deletions Sources/ParseSwift/Protocols/ParseObjectMutable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// ParseObjectMutable.swift
// ParseSwift
//
// Created by Damian Van de Kauter on 30/10/2021.
// Copyright © 2021 Parse Community. All rights reserved.
//

import Foundation

/**
The **ParseObjectMutable** protocol adds 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. 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.

**Example use case:**
~~~~
var user = User.current?.mutable
user?.customKey = "newValue"

do {
try await user?.save()
} catch {
// Handle error
}
~~~~

- warning: Using the **ParseObjectMutable** protocol requires the developer to use an **init()** without declaring custom properties.
You can still use non-optional properties if you add them to your **init()** method and give them a default value.
If you need a custom **init()**, you can't use the **ParseObjectMutable** protocol.
*/
public protocol ParseObjectMutable: ParseObject {
init()

var mutable: Self { get }
}

public extension ParseObjectMutable {

/**
An empty version of the respective object that allows you to update a
a subset of the fields of an object as oppose to updating every field of an object.
- note: You need to use this to create a mutable copy of your **ParseObject**.
*/
var mutable: Self {
var object = self
object.objectId = objectId
object.createdAt = createdAt
return object
}
}

/**
The **ParseInstallationMutable** protocol adds 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. 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.

It combines the **ParseInstallation** protocol and the **ParseObjectMutable** protocol.
*/
public protocol ParseInstallationMutable: ParseInstallation, ParseObjectMutable {
init()
}

/**
The **ParseUserMutable** protocol adds 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. 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.

It combines the **ParseUser** protocol and the **ParseObjectMutable** protocol.
*/
public protocol ParseUserMutable: ParseUser, ParseObjectMutable {
init()
}
Loading