Skip to content

Commit 1352a83

Browse files
authored
feat: improve ParseRelation by adding more methods (#294)
* feat: improve ParseRelation by adding more methods * Fix Swift 5.2 build * Add relation using property key * codecov
1 parent 79b760f commit 1352a83

File tree

8 files changed

+389
-115
lines changed

8 files changed

+389
-115
lines changed

.codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ coverage:
66
status:
77
patch:
88
default:
9-
target: auto
9+
target: 74
1010
changes: false
1111
project:
1212
default:

ParseSwift.playground/Pages/12 - Roles and Relations.xcplaygroundpage/Contents.swift

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,31 @@ struct Role<RoleUser: ParseUser>: ParseRole {
4747
}
4848
}
4949

50+
//: Create your own value typed `ParseObject`.
51+
struct GameScore: ParseObject, ParseObjectMutable {
52+
//: These are required by ParseObject
53+
var objectId: String?
54+
var createdAt: Date?
55+
var updatedAt: Date?
56+
var ACL: ParseACL?
57+
58+
//: Your own properties.
59+
var score: Int = 0
60+
}
61+
62+
//: It's recommended to place custom initializers in an extension
63+
//: to preserve the convenience initializer.
64+
extension GameScore {
65+
66+
init(score: Int) {
67+
self.score = score
68+
}
69+
70+
init(objectId: String?) {
71+
self.objectId = objectId
72+
}
73+
}
74+
5075
//: Roles can provide additional access/security to your apps.
5176

5277
//: This variable will store the saved role.
@@ -218,15 +243,71 @@ do {
218243
print(error)
219244
}
220245

246+
//: Using this relation, you can create one-to-many relationships with other `ParseObjecs`,
247+
//: similar to `users` and `roles`.
221248
//: All `ParseObject`s have a `ParseRelation` attribute that be used on instances.
222249
//: For example, the User has:
223-
let relation = User.current!.relation
250+
var relation = User.current!.relation
251+
let score1 = GameScore(score: 53)
252+
let score2 = GameScore(score: 57)
224253

225-
//: Example: relation.add(<#T##users: [ParseUser]##[ParseUser]#>)
226-
//: Example: relation.remove(<#T##key: String##String#>, objects: <#T##[ParseObject]#>)
254+
//: Add new child relationships.
255+
[score1, score2].saveAll { result in
256+
switch result {
257+
case .success(let savedScores):
258+
//: Make an array of all scores that were properly saved.
259+
let scores = savedScores.compactMap { try? $0.get() }
260+
do {
261+
let newRelations = try relation.add("scores", objects: scores)
262+
newRelations.save { result in
263+
switch result {
264+
case .success(let saved):
265+
print("The relation saved successfully: \(saved)")
266+
print("Check \"scores\" field in your \"_User\" class in Parse Dashboard.")
267+
268+
case .failure(let error):
269+
print("Error saving role: \(error)")
270+
}
271+
}
272+
} catch {
273+
print(error)
274+
}
275+
case .failure(let error):
276+
print("Couldn't save scores. \(error)")
277+
}
278+
}
227279

228-
//: Using this relation, you can create many-to-many relationships with other `ParseObjecs`,
229-
//: similar to `users` and `roles`.
280+
let specificRelation = User.current!.relation("scores", child: score1)
281+
//: You can also do
282+
// let specificRelation = User.current!.relation("scores", className: "GameScore")
283+
do {
284+
try specificRelation.query(score1).find { result in
285+
switch result {
286+
case .success(let scores):
287+
print("Found related scores: \(scores)")
288+
case .failure(let error):
289+
print("Error finding scores: \(error)")
290+
}
291+
}
292+
} catch {
293+
print(error)
294+
}
295+
296+
do {
297+
//: You can also leverage the child to find scores related to the parent.
298+
try score1.relation.query("scores", parent: User.current!).find { result in
299+
switch result {
300+
case .success(let scores):
301+
print("Found related scores: \(scores)")
302+
case .failure(let error):
303+
print("Error finding scores: \(error)")
304+
}
305+
}
306+
} catch {
307+
print(error)
308+
}
309+
310+
//: Example: try relation.remove(<#T##key: String##String#>, objects: <#T##[ParseObject]#>)
230311

231312
PlaygroundPage.current.finishExecution()
232313
//: [Next](@next)

Sources/ParseSwift/Types/ParseOperation.swift

Lines changed: 16 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import Foundation
1818
*/
1919
public struct ParseOperation<T>: Savable where T: ParseObject {
2020

21-
var target: T?
21+
var target: T
2222
var operations = [String: Encodable]()
2323

2424
public init(target: T) {
@@ -34,13 +34,10 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
3434
*/
3535
public func set<W>(_ key: (String, WritableKeyPath<T, W>),
3636
value: W) throws -> Self where W: Encodable {
37-
guard let target = self.target else {
38-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
39-
}
4037
var mutableOperation = self
4138
if !target[keyPath: key.1].isEqual(value) {
4239
mutableOperation.operations[key.0] = value
43-
mutableOperation.target?[keyPath: key.1] = value
40+
mutableOperation.target[keyPath: key.1] = value
4441
}
4542
return mutableOperation
4643
}
@@ -54,12 +51,9 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
5451
*/
5552
public func forceSet<W>(_ key: (String, WritableKeyPath<T, W>),
5653
value: W) throws -> Self where W: Encodable {
57-
guard self.target != nil else {
58-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
59-
}
6054
var mutableOperation = self
6155
mutableOperation.operations[key.0] = value
62-
mutableOperation.target?[keyPath: key.1] = value
56+
mutableOperation.target[keyPath: key.1] = value
6357
return mutableOperation
6458
}
6559

@@ -100,14 +94,11 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
10094
*/
10195
public func addUnique<V>(_ key: (String, WritableKeyPath<T, [V]>),
10296
objects: [V]) throws -> Self where V: Encodable, V: Hashable {
103-
guard let target = self.target else {
104-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
105-
}
10697
var mutableOperation = self
10798
mutableOperation.operations[key.0] = AddUnique(objects: objects)
10899
var values = target[keyPath: key.1]
109100
values.append(contentsOf: objects)
110-
mutableOperation.target?[keyPath: key.1] = Array(Set<V>(values))
101+
mutableOperation.target[keyPath: key.1] = Array(Set<V>(values))
111102
return mutableOperation
112103
}
113104

@@ -121,14 +112,11 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
121112
*/
122113
public func addUnique<V>(_ key: (String, WritableKeyPath<T, [V]?>),
123114
objects: [V]) throws -> Self where V: Encodable, V: Hashable {
124-
guard let target = self.target else {
125-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
126-
}
127115
var mutableOperation = self
128116
mutableOperation.operations[key.0] = AddUnique(objects: objects)
129117
var values = target[keyPath: key.1] ?? []
130118
values.append(contentsOf: objects)
131-
mutableOperation.target?[keyPath: key.1] = Array(Set<V>(values))
119+
mutableOperation.target[keyPath: key.1] = Array(Set<V>(values))
132120
return mutableOperation
133121
}
134122

@@ -154,14 +142,11 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
154142
*/
155143
public func add<V>(_ key: (String, WritableKeyPath<T, [V]>),
156144
objects: [V]) throws -> Self where V: Encodable {
157-
guard let target = self.target else {
158-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
159-
}
160145
var mutableOperation = self
161146
mutableOperation.operations[key.0] = Add(objects: objects)
162147
var values = target[keyPath: key.1]
163148
values.append(contentsOf: objects)
164-
mutableOperation.target?[keyPath: key.1] = values
149+
mutableOperation.target[keyPath: key.1] = values
165150
return mutableOperation
166151
}
167152

@@ -174,14 +159,11 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
174159
*/
175160
public func add<V>(_ key: (String, WritableKeyPath<T, [V]?>),
176161
objects: [V]) throws -> Self where V: Encodable {
177-
guard let target = self.target else {
178-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
179-
}
180162
var mutableOperation = self
181163
mutableOperation.operations[key.0] = Add(objects: objects)
182164
var values = target[keyPath: key.1] ?? []
183165
values.append(contentsOf: objects)
184-
mutableOperation.target?[keyPath: key.1] = values
166+
mutableOperation.target[keyPath: key.1] = values
185167
return mutableOperation
186168
}
187169

@@ -207,14 +189,11 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
207189
*/
208190
public func addRelation<V>(_ key: (String, WritableKeyPath<T, [V]>),
209191
objects: [V]) throws -> Self where V: ParseObject {
210-
guard let target = self.target else {
211-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
212-
}
213192
var mutableOperation = self
214193
mutableOperation.operations[key.0] = try AddRelation(objects: objects)
215194
var values = target[keyPath: key.1]
216195
values.append(contentsOf: objects)
217-
mutableOperation.target?[keyPath: key.1] = values
196+
mutableOperation.target[keyPath: key.1] = values
218197
return mutableOperation
219198
}
220199

@@ -227,14 +206,11 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
227206
*/
228207
public func addRelation<V>(_ key: (String, WritableKeyPath<T, [V]?>),
229208
objects: [V]) throws -> Self where V: ParseObject {
230-
guard let target = self.target else {
231-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
232-
}
233209
var mutableOperation = self
234210
mutableOperation.operations[key.0] = try AddRelation(objects: objects)
235211
var values = target[keyPath: key.1] ?? []
236212
values.append(contentsOf: objects)
237-
mutableOperation.target?[keyPath: key.1] = values
213+
mutableOperation.target[keyPath: key.1] = values
238214
return mutableOperation
239215
}
240216

@@ -262,17 +238,14 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
262238
*/
263239
public func remove<V>(_ key: (String, WritableKeyPath<T, [V]>),
264240
objects: [V]) throws -> Self where V: Encodable, V: Hashable {
265-
guard let target = self.target else {
266-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
267-
}
268241
var mutableOperation = self
269242
mutableOperation.operations[key.0] = Remove(objects: objects)
270243
let values = target[keyPath: key.1]
271244
var set = Set<V>(values)
272245
objects.forEach {
273246
set.remove($0)
274247
}
275-
mutableOperation.target?[keyPath: key.1] = Array(set)
248+
mutableOperation.target[keyPath: key.1] = Array(set)
276249
return mutableOperation
277250
}
278251

@@ -286,17 +259,14 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
286259
*/
287260
public func remove<V>(_ key: (String, WritableKeyPath<T, [V]?>),
288261
objects: [V]) throws -> Self where V: Encodable, V: Hashable {
289-
guard let target = self.target else {
290-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
291-
}
292262
var mutableOperation = self
293263
mutableOperation.operations[key.0] = Remove(objects: objects)
294264
let values = target[keyPath: key.1]
295265
var set = Set<V>(values ?? [])
296266
objects.forEach {
297267
set.remove($0)
298268
}
299-
mutableOperation.target?[keyPath: key.1] = Array(set)
269+
mutableOperation.target[keyPath: key.1] = Array(set)
300270
return mutableOperation
301271
}
302272

@@ -309,9 +279,6 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
309279
- returns: The updated operations.
310280
*/
311281
public func removeRelation<W>(_ key: String, objects: [W]) throws -> Self where W: ParseObject {
312-
guard self.target != nil else {
313-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
314-
}
315282
var mutableOperation = self
316283
mutableOperation.operations[key] = try RemoveRelation(objects: objects)
317284
return mutableOperation
@@ -327,17 +294,14 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
327294
*/
328295
public func removeRelation<V>(_ key: (String, WritableKeyPath<T, [V]>),
329296
objects: [V]) throws -> Self where V: ParseObject {
330-
guard let target = self.target else {
331-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
332-
}
333297
var mutableOperation = self
334298
mutableOperation.operations[key.0] = try RemoveRelation(objects: objects)
335299
let values = target[keyPath: key.1]
336300
var set = Set<V>(values)
337301
objects.forEach {
338302
set.remove($0)
339303
}
340-
mutableOperation.target?[keyPath: key.1] = Array(set)
304+
mutableOperation.target[keyPath: key.1] = Array(set)
341305
return mutableOperation
342306
}
343307

@@ -351,17 +315,14 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
351315
*/
352316
public func removeRelation<V>(_ key: (String, WritableKeyPath<T, [V]?>),
353317
objects: [V]) throws -> Self where V: ParseObject {
354-
guard let target = self.target else {
355-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
356-
}
357318
var mutableOperation = self
358319
mutableOperation.operations[key.0] = try RemoveRelation(objects: objects)
359320
let values = target[keyPath: key.1]
360321
var set = Set<V>(values ?? [])
361322
objects.forEach {
362323
set.remove($0)
363324
}
364-
mutableOperation.target?[keyPath: key.1] = Array(set)
325+
mutableOperation.target[keyPath: key.1] = Array(set)
365326
return mutableOperation
366327
}
367328

@@ -385,7 +346,7 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
385346
public func unset<V>(_ key: (String, WritableKeyPath<T, V?>)) -> Self where V: Encodable {
386347
var mutableOperation = self
387348
mutableOperation.operations[key.0] = Delete()
388-
mutableOperation.target?[keyPath: key.1] = nil
349+
mutableOperation.target[keyPath: key.1] = nil
389350
return mutableOperation
390351
}
391352

@@ -410,9 +371,6 @@ extension ParseOperation {
410371
- returns: Returns saved `ParseObject`.
411372
*/
412373
public func save(options: API.Options = []) throws -> T {
413-
guard let target = self.target else {
414-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil.")
415-
}
416374
if !target.isSaved {
417375
throw ParseError(code: .missingObjectId, message: "ParseObject isn't saved.")
418376
}
@@ -433,13 +391,6 @@ extension ParseOperation {
433391
callbackQueue: DispatchQueue = .main,
434392
completion: @escaping (Result<T, ParseError>) -> Void
435393
) {
436-
guard let target = self.target else {
437-
callbackQueue.async {
438-
let error = ParseError(code: .unknownError, message: "Target shouldn't be nil.")
439-
completion(.failure(error))
440-
}
441-
return
442-
}
443394
if !target.isSaved {
444395
callbackQueue.async {
445396
let error = ParseError(code: .missingObjectId, message: "ParseObject isn't saved.")
@@ -461,11 +412,8 @@ extension ParseOperation {
461412
}
462413

463414
func saveCommand() throws -> API.NonParseBodyCommand<ParseOperation<T>, T> {
464-
guard let target = self.target else {
465-
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
466-
}
467-
return API.NonParseBodyCommand(method: .PUT, path: target.endpoint, body: self) {
468-
try ParseCoding.jsonDecoder().decode(UpdateResponse.self, from: $0).apply(to: target)
415+
API.NonParseBodyCommand(method: .PUT, path: target.endpoint, body: self) {
416+
try ParseCoding.jsonDecoder().decode(UpdateResponse.self, from: $0).apply(to: self.target)
469417
}
470418
}
471419
}

0 commit comments

Comments
 (0)