Skip to content

Commit ba3d60e

Browse files
authored
Allow mixed custom objectId environment (#222)
* Allow mixed custom objectId environment * Update change log * nit on warning * nit * Update .codecov.yml
1 parent f584819 commit ba3d60e

13 files changed

+942
-58
lines changed

.codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ coverage:
44
status:
55
patch:
66
default:
7-
target: 49
7+
target: auto
88
changes: false
99
project:
1010
default:

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
# Parse-Swift Changelog
22

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

7+
### 1.9.7
8+
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.9.6...1.9.7)
9+
10+
__Improvements__
11+
- Properly allow a mixed custom objectId environment without compromising safety checks using .save(). If a developer wants to ignore the objectId checks, they need to specify isIgnoreCustomObjectIdConfig = true each time ([#222](https://github.com/parse-community/Parse-Swift/pull/222)), thanks to [Corey Baker](https://github.com/cbaker6).
12+
713
### 1.9.6
814
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.9.5...1.9.6)
915

Sources/ParseSwift/API/API+Commands.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,9 @@ internal extension API.Command {
344344
}
345345

346346
// MARK: Saving ParseObjects
347-
static func save<T>(_ object: T) throws -> API.Command<T, T> where T: ParseObject {
348-
if ParseSwift.configuration.allowCustomObjectId && object.objectId == nil {
347+
static func save<T>(_ object: T,
348+
isIgnoreCustomObjectIdConfig: Bool) throws -> API.Command<T, T> where T: ParseObject {
349+
if ParseSwift.configuration.allowCustomObjectId && object.objectId == nil && !isIgnoreCustomObjectIdConfig {
349350
throw ParseError(code: .missingObjectId, message: "objectId must not be nil")
350351
}
351352
if object.isSaved {

Sources/ParseSwift/Objects/ParseInstallation+combine.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ public extension ParseInstallation {
4242
- returns: A publisher that eventually produces a single value and then finishes or fails.
4343
- important: If an object saved has the same objectId as current, it will automatically update the current.
4444
*/
45-
func savePublisher(options: API.Options = []) -> Future<Self, ParseError> {
45+
func savePublisher(isIgnoreCustomObjectIdConfig: Bool = false,
46+
options: API.Options = []) -> Future<Self, ParseError> {
4647
Future { promise in
47-
self.save(options: options,
48+
self.save(isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig,
49+
options: options,
4850
completion: promise)
4951
}
5052
}
@@ -102,10 +104,12 @@ public extension Sequence where Element: ParseInstallation {
102104
*/
103105
func saveAllPublisher(batchLimit limit: Int? = nil,
104106
transaction: Bool = false,
107+
isIgnoreCustomObjectIdConfig: Bool = false,
105108
options: API.Options = []) -> Future<[(Result<Self.Element, ParseError>)], ParseError> {
106109
Future { promise in
107110
self.saveAll(batchLimit: limit,
108111
transaction: transaction,
112+
isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig,
109113
options: options,
110114
completion: promise)
111115
}

Sources/ParseSwift/Objects/ParseInstallation.swift

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,32 @@ extension ParseInstallation {
426426
- important: If an object saved has the same objectId as current, it will automatically update the current.
427427
*/
428428
public func save(options: API.Options = []) throws -> Self {
429+
try save(isIgnoreCustomObjectIdConfig: false,
430+
options: options)
431+
}
432+
433+
/**
434+
Saves the `ParseInstallation` *synchronously* and throws an error if there's an issue.
435+
436+
- parameter isIgnoreCustomObjectIdConfig: Ignore checking for `objectId`
437+
when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed
438+
`objectId` environments. Defaults to false.
439+
- parameter options: A set of header options sent to the server. Defaults to an empty set.
440+
- throws: An error of type `ParseError`.
441+
- returns: Returns saved `ParseInstallation`.
442+
- important: If an object saved has the same objectId as current, it will automatically update the current.
443+
- warning: If you are using `ParseConfiguration.allowCustomObjectId = true`
444+
and plan to generate all of your `objectId`'s on the client-side then you should leave
445+
`isIgnoreCustomObjectIdConfig = false`. Setting
446+
`ParseConfiguration.allowCustomObjectId = true` and
447+
`isIgnoreCustomObjectIdConfig = true` means the client will generate `objectId`'s
448+
and the server will generate an `objectId` only when the client does not provide one. This can
449+
increase the probability of colliding `objectId`'s as the client and server `objectId`'s may be generated using
450+
different algorithms. This can also lead to overwriting of `ParseObject`'s by accident as the
451+
client-side checks are disabled. Developers are responsible for handling such cases.
452+
*/
453+
public func save(isIgnoreCustomObjectIdConfig: Bool,
454+
options: API.Options = []) throws -> Self {
429455
var options = options
430456
options.insert(.cachePolicy(.reloadIgnoringLocalCacheData))
431457
var childObjects: [String: PointerType]?
@@ -445,7 +471,7 @@ extension ParseInstallation {
445471
throw error
446472
}
447473

448-
let result: Self = try saveCommand()
474+
let result: Self = try saveCommand(isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig)
449475
.execute(options: options,
450476
callbackQueue: .main,
451477
childObjects: childObjects,
@@ -457,13 +483,26 @@ extension ParseInstallation {
457483
/**
458484
Saves the `ParseInstallation` *asynchronously* and executes the given callback block.
459485

486+
- parameter isIgnoreCustomObjectIdConfig: Ignore checking for `objectId`
487+
when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed
488+
`objectId` environments. Defaults to false.
460489
- parameter options: A set of header options sent to the server. Defaults to an empty set.
461490
- parameter callbackQueue: The queue to return to after completion. Default value of .main.
462491
- parameter completion: The block to execute.
463492
It should have the following argument signature: `(Result<Self, ParseError>)`.
464493
- important: If an object saved has the same objectId as current, it will automatically update the current.
494+
- warning: If you are using `ParseConfiguration.allowCustomObjectId = true`
495+
and plan to generate all of your `objectId`'s on the client-side then you should leave
496+
`isIgnoreCustomObjectIdConfig = false`. Setting
497+
`ParseConfiguration.allowCustomObjectId = true` and
498+
`isIgnoreCustomObjectIdConfig = true` means the client will generate `objectId`'s
499+
and the server will generate an `objectId` only when the client does not provide one. This can
500+
increase the probability of colliding `objectId`'s as the client and server `objectId`'s may be generated using
501+
different algorithms. This can also lead to overwriting of `ParseObject`'s by accident as the
502+
client-side checks are disabled. Developers are responsible for handling such cases.
465503
*/
466504
public func save(
505+
isIgnoreCustomObjectIdConfig: Bool = false,
467506
options: API.Options = [],
468507
callbackQueue: DispatchQueue = .main,
469508
completion: @escaping (Result<Self, ParseError>) -> Void
@@ -473,7 +512,7 @@ extension ParseInstallation {
473512
self.ensureDeepSave(options: options) { (savedChildObjects, savedChildFiles, error) in
474513
guard let parseError = error else {
475514
do {
476-
try self.saveCommand()
515+
try self.saveCommand(isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig)
477516
.executeAsync(options: options,
478517
callbackQueue: callbackQueue,
479518
childObjects: savedChildObjects,
@@ -515,8 +554,8 @@ extension ParseInstallation {
515554
}
516555
}
517556

518-
func saveCommand() throws -> API.Command<Self, Self> {
519-
if ParseSwift.configuration.allowCustomObjectId && objectId == nil {
557+
func saveCommand(isIgnoreCustomObjectIdConfig: Bool = false) throws -> API.Command<Self, Self> {
558+
if ParseSwift.configuration.allowCustomObjectId && objectId == nil && !isIgnoreCustomObjectIdConfig {
520559
throw ParseError(code: .missingObjectId, message: "objectId must not be nil")
521560
}
522561
if isSaved {
@@ -643,6 +682,9 @@ public extension Sequence where Element: ParseInstallation {
643682
- parameter batchLimit: The maximum number of objects to send in each batch. If the items to be batched.
644683
is greater than the `batchLimit`, the objects will be sent to the server in waves up to the `batchLimit`.
645684
Defaults to 50.
685+
- parameter isIgnoreCustomObjectIdConfig: Ignore checking for `objectId`
686+
when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed
687+
`objectId` environments. Defaults to false.
646688
- parameter options: A set of header options sent to the server. Defaults to an empty set.
647689
- parameter transaction: Treat as an all-or-nothing operation. If some operation failure occurs that
648690
prevents the transaction from completing, then none of the objects are committed to the Parse Server database.
@@ -653,9 +695,19 @@ public extension Sequence where Element: ParseInstallation {
653695
- warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the
654696
objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else
655697
the transactions can fail.
698+
- warning: If you are using `ParseConfiguration.allowCustomObjectId = true`
699+
and plan to generate all of your `objectId`'s on the client-side then you should leave
700+
`isIgnoreCustomObjectIdConfig = false`. Setting
701+
`ParseConfiguration.allowCustomObjectId = true` and
702+
`isIgnoreCustomObjectIdConfig = true` means the client will generate `objectId`'s
703+
and the server will generate an `objectId` only when the client does not provide one. This can
704+
increase the probability of colliding `objectId`'s as the client and server `objectId`'s may be generated using
705+
different algorithms. This can also lead to overwriting of `ParseObject`'s by accident as the
706+
client-side checks are disabled. Developers are responsible for handling such cases.
656707
*/
657708
func saveAll(batchLimit limit: Int? = nil, // swiftlint:disable:this function_body_length
658709
transaction: Bool = false,
710+
isIgnoreCustomObjectIdConfig: Bool = false,
659711
options: API.Options = []) throws -> [(Result<Self.Element, ParseError>)] {
660712
var options = options
661713
options.insert(.cachePolicy(.reloadIgnoringLocalCacheData))
@@ -703,7 +755,9 @@ public extension Sequence where Element: ParseInstallation {
703755
}
704756

705757
var returnBatch = [(Result<Self.Element, ParseError>)]()
706-
let commands = try map { try $0.saveCommand() }
758+
let commands = try map {
759+
try $0.saveCommand(isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig)
760+
}
707761
let batchLimit: Int!
708762
if transaction {
709763
batchLimit = commands.count
@@ -731,6 +785,9 @@ public extension Sequence where Element: ParseInstallation {
731785
Defaults to 50.
732786
- parameter transaction: Treat as an all-or-nothing operation. If some operation failure occurs that
733787
prevents the transaction from completing, then none of the objects are committed to the Parse Server database.
788+
- parameter isIgnoreCustomObjectIdConfig: Ignore checking for `objectId`
789+
when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed
790+
`objectId` environments. Defaults to false.
734791
- parameter options: A set of header options sent to the server. Defaults to an empty set.
735792
- parameter callbackQueue: The queue to return to after completion. Default value of .main.
736793
- parameter completion: The block to execute.
@@ -739,10 +796,20 @@ public extension Sequence where Element: ParseInstallation {
739796
- warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the
740797
objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else
741798
the transactions can fail.
799+
- warning: If you are using `ParseConfiguration.allowCustomObjectId = true`
800+
and plan to generate all of your `objectId`'s on the client-side then you should leave
801+
`isIgnoreCustomObjectIdConfig = false`. Setting
802+
`ParseConfiguration.allowCustomObjectId = true` and
803+
`isIgnoreCustomObjectIdConfig = true` means the client will generate `objectId`'s
804+
and the server will generate an `objectId` only when the client does not provide one. This can
805+
increase the probability of colliding `objectId`'s as the client and server `objectId`'s may be generated using
806+
different algorithms. This can also lead to overwriting of `ParseObject`'s by accident as the
807+
client-side checks are disabled. Developers are responsible for handling such cases.
742808
*/
743809
func saveAll( // swiftlint:disable:this function_body_length cyclomatic_complexity
744810
batchLimit limit: Int? = nil,
745811
transaction: Bool = false,
812+
isIgnoreCustomObjectIdConfig: Bool = false,
746813
options: API.Options = [],
747814
callbackQueue: DispatchQueue = .main,
748815
completion: @escaping (Result<[(Result<Element, ParseError>)], ParseError>) -> Void
@@ -805,7 +872,9 @@ public extension Sequence where Element: ParseInstallation {
805872

806873
do {
807874
var returnBatch = [(Result<Self.Element, ParseError>)]()
808-
let commands = try map { try $0.saveCommand() }
875+
let commands = try map {
876+
try $0.saveCommand(isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig)
877+
}
809878
let batchLimit: Int!
810879
if transaction {
811880
batchLimit = commands.count

Sources/ParseSwift/Objects/ParseObject+combine.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@ public extension ParseObject {
3939
- returns: A publisher that eventually produces a single value and then finishes or fails.
4040
- important: If an object saved has the same objectId as current, it will automatically update the current.
4141
*/
42-
func savePublisher(options: API.Options = []) -> Future<Self, ParseError> {
42+
func savePublisher(isIgnoreCustomObjectIdConfig: Bool = false,
43+
options: API.Options = []) -> Future<Self, ParseError> {
4344
Future { promise in
44-
self.save(options: options,
45+
self.save(isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig,
46+
options: options,
4547
completion: promise)
4648
}
4749
}
@@ -89,19 +91,33 @@ public extension Sequence where Element: ParseObject {
8991
Defaults to 50.
9092
- parameter transaction: Treat as an all-or-nothing operation. If some operation failure occurs that
9193
prevents the transaction from completing, then none of the objects are committed to the Parse Server database.
94+
- parameter isIgnoreCustomObjectIdConfig: Ignore checking for `objectId`
95+
when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed
96+
`objectId` environments. Defaults to false.
9297
- parameter options: A set of header options sent to the server. Defaults to an empty set.
9398
- returns: A publisher that eventually produces a single value and then finishes or fails.
9499
- important: If an object saved has the same objectId as current, it will automatically update the current.
95100
- warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the
96101
objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else
97102
the transactions can fail.
103+
- warning: If you are using `ParseConfiguration.allowCustomObjectId = true`
104+
and plan to generate all of your `objectId`'s on the client-side then you should leave
105+
`isIgnoreCustomObjectIdConfig = false`. Setting
106+
`ParseConfiguration.allowCustomObjectId = true` and
107+
`isIgnoreCustomObjectIdConfig = true` means the client will generate `objectId`'s
108+
and the server will generate an `objectId` only when the client does not provide one. This can
109+
increase the probability of colliiding `objectId`'s as the client and server `objectId`'s may be generated using
110+
different algorithms. This can also lead to overwriting of `ParseObject`'s by accident as the
111+
client-side checks are disabled. Developers are responsible for handling such cases.
98112
*/
99113
func saveAllPublisher(batchLimit limit: Int? = nil,
100114
transaction: Bool = false,
115+
isIgnoreCustomObjectIdConfig: Bool = false,
101116
options: API.Options = []) -> Future<[(Result<Self.Element, ParseError>)], ParseError> {
102117
Future { promise in
103118
self.saveAll(batchLimit: limit,
104119
transaction: transaction,
120+
isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig,
105121
options: options,
106122
completion: promise)
107123
}

0 commit comments

Comments
 (0)