Skip to content

feat: improve ParseRelation by adding more methods #294

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 4 commits into from
Dec 2, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ coverage:
status:
patch:
default:
target: auto
target: 74
changes: false
project:
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,31 @@ struct Role<RoleUser: ParseUser>: ParseRole {
}
}

//: Create your own value typed `ParseObject`.
struct GameScore: ParseObject, ParseObjectMutable {
//: These are required by ParseObject
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var ACL: ParseACL?

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

//: It's recommended to place custom initializers in an extension
//: to preserve the convenience initializer.
extension GameScore {

init(score: Int) {
self.score = score
}

init(objectId: String?) {
self.objectId = objectId
}
}

//: Roles can provide additional access/security to your apps.

//: This variable will store the saved role.
Expand Down Expand Up @@ -218,15 +243,71 @@ do {
print(error)
}

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

//: Example: relation.add(<#T##users: [ParseUser]##[ParseUser]#>)
//: Example: relation.remove(<#T##key: String##String#>, objects: <#T##[ParseObject]#>)
//: Add new child relationships.
[score1, score2].saveAll { result in
switch result {
case .success(let savedScores):
//: Make an array of all scores that were properly saved.
let scores = savedScores.compactMap { try? $0.get() }
do {
let newRelations = try relation.add("scores", objects: scores)
newRelations.save { result in
switch result {
case .success(let saved):
print("The relation saved successfully: \(saved)")
print("Check \"scores\" field in your \"_User\" class in Parse Dashboard.")

case .failure(let error):
print("Error saving role: \(error)")
}
}
} catch {
print(error)
}
case .failure(let error):
print("Couldn't save scores. \(error)")
}
}

//: Using this relation, you can create many-to-many relationships with other `ParseObjecs`,
//: similar to `users` and `roles`.
let specificRelation = User.current!.relation("scores", child: score1)
//: You can also do
// let specificRelation = User.current!.relation("scores", className: "GameScore")
do {
try specificRelation.query(score1).find { result in
switch result {
case .success(let scores):
print("Found related scores: \(scores)")
case .failure(let error):
print("Error finding scores: \(error)")
}
}
} catch {
print(error)
}

do {
//: You can also leverage the child to find scores related to the parent.
try score1.relation.query("scores", parent: User.current!).find { result in
switch result {
case .success(let scores):
print("Found related scores: \(scores)")
case .failure(let error):
print("Error finding scores: \(error)")
}
}
} catch {
print(error)
}

//: Example: try relation.remove(<#T##key: String##String#>, objects: <#T##[ParseObject]#>)

PlaygroundPage.current.finishExecution()
//: [Next](@next)
84 changes: 16 additions & 68 deletions Sources/ParseSwift/Types/ParseOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import Foundation
*/
public struct ParseOperation<T>: Savable where T: ParseObject {

var target: T?
var target: T
var operations = [String: Encodable]()

public init(target: T) {
Expand All @@ -34,13 +34,10 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
*/
public func set<W>(_ key: (String, WritableKeyPath<T, W>),
value: W) throws -> Self where W: Encodable {
guard let target = self.target else {
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
}
var mutableOperation = self
if !target[keyPath: key.1].isEqual(value) {
mutableOperation.operations[key.0] = value
mutableOperation.target?[keyPath: key.1] = value
mutableOperation.target[keyPath: key.1] = value
}
return mutableOperation
}
Expand All @@ -54,12 +51,9 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
*/
public func forceSet<W>(_ key: (String, WritableKeyPath<T, W>),
value: W) throws -> Self where W: Encodable {
guard self.target != nil else {
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
}
var mutableOperation = self
mutableOperation.operations[key.0] = value
mutableOperation.target?[keyPath: key.1] = value
mutableOperation.target[keyPath: key.1] = value
return mutableOperation
}

Expand Down Expand Up @@ -100,14 +94,11 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
*/
public func addUnique<V>(_ key: (String, WritableKeyPath<T, [V]>),
objects: [V]) throws -> Self where V: Encodable, V: Hashable {
guard let target = self.target else {
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
}
var mutableOperation = self
mutableOperation.operations[key.0] = AddUnique(objects: objects)
var values = target[keyPath: key.1]
values.append(contentsOf: objects)
mutableOperation.target?[keyPath: key.1] = Array(Set<V>(values))
mutableOperation.target[keyPath: key.1] = Array(Set<V>(values))
return mutableOperation
}

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

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

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

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

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

Expand Down Expand Up @@ -262,17 +238,14 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
*/
public func remove<V>(_ key: (String, WritableKeyPath<T, [V]>),
objects: [V]) throws -> Self where V: Encodable, V: Hashable {
guard let target = self.target else {
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
}
var mutableOperation = self
mutableOperation.operations[key.0] = Remove(objects: objects)
let values = target[keyPath: key.1]
var set = Set<V>(values)
objects.forEach {
set.remove($0)
}
mutableOperation.target?[keyPath: key.1] = Array(set)
mutableOperation.target[keyPath: key.1] = Array(set)
return mutableOperation
}

Expand All @@ -286,17 +259,14 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
*/
public func remove<V>(_ key: (String, WritableKeyPath<T, [V]?>),
objects: [V]) throws -> Self where V: Encodable, V: Hashable {
guard let target = self.target else {
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
}
var mutableOperation = self
mutableOperation.operations[key.0] = Remove(objects: objects)
let values = target[keyPath: key.1]
var set = Set<V>(values ?? [])
objects.forEach {
set.remove($0)
}
mutableOperation.target?[keyPath: key.1] = Array(set)
mutableOperation.target[keyPath: key.1] = Array(set)
return mutableOperation
}

Expand All @@ -309,9 +279,6 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
- returns: The updated operations.
*/
public func removeRelation<W>(_ key: String, objects: [W]) throws -> Self where W: ParseObject {
guard self.target != nil else {
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
}
var mutableOperation = self
mutableOperation.operations[key] = try RemoveRelation(objects: objects)
return mutableOperation
Expand All @@ -327,17 +294,14 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
*/
public func removeRelation<V>(_ key: (String, WritableKeyPath<T, [V]>),
objects: [V]) throws -> Self where V: ParseObject {
guard let target = self.target else {
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
}
var mutableOperation = self
mutableOperation.operations[key.0] = try RemoveRelation(objects: objects)
let values = target[keyPath: key.1]
var set = Set<V>(values)
objects.forEach {
set.remove($0)
}
mutableOperation.target?[keyPath: key.1] = Array(set)
mutableOperation.target[keyPath: key.1] = Array(set)
return mutableOperation
}

Expand All @@ -351,17 +315,14 @@ public struct ParseOperation<T>: Savable where T: ParseObject {
*/
public func removeRelation<V>(_ key: (String, WritableKeyPath<T, [V]?>),
objects: [V]) throws -> Self where V: ParseObject {
guard let target = self.target else {
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
}
var mutableOperation = self
mutableOperation.operations[key.0] = try RemoveRelation(objects: objects)
let values = target[keyPath: key.1]
var set = Set<V>(values ?? [])
objects.forEach {
set.remove($0)
}
mutableOperation.target?[keyPath: key.1] = Array(set)
mutableOperation.target[keyPath: key.1] = Array(set)
return mutableOperation
}

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

Expand All @@ -410,9 +371,6 @@ extension ParseOperation {
- returns: Returns saved `ParseObject`.
*/
public func save(options: API.Options = []) throws -> T {
guard let target = self.target else {
throw ParseError(code: .unknownError, message: "Target shouldn't be nil.")
}
if !target.isSaved {
throw ParseError(code: .missingObjectId, message: "ParseObject isn't saved.")
}
Expand All @@ -433,13 +391,6 @@ extension ParseOperation {
callbackQueue: DispatchQueue = .main,
completion: @escaping (Result<T, ParseError>) -> Void
) {
guard let target = self.target else {
callbackQueue.async {
let error = ParseError(code: .unknownError, message: "Target shouldn't be nil.")
completion(.failure(error))
}
return
}
if !target.isSaved {
callbackQueue.async {
let error = ParseError(code: .missingObjectId, message: "ParseObject isn't saved.")
Expand All @@ -461,11 +412,8 @@ extension ParseOperation {
}

func saveCommand() throws -> API.NonParseBodyCommand<ParseOperation<T>, T> {
guard let target = self.target else {
throw ParseError(code: .unknownError, message: "Target shouldn't be nil")
}
return API.NonParseBodyCommand(method: .PUT, path: target.endpoint, body: self) {
try ParseCoding.jsonDecoder().decode(UpdateResponse.self, from: $0).apply(to: target)
API.NonParseBodyCommand(method: .PUT, path: target.endpoint, body: self) {
try ParseCoding.jsonDecoder().decode(UpdateResponse.self, from: $0).apply(to: self.target)
}
}
}
Expand Down
Loading