diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 10e277f1a..d86255679 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -278,6 +278,7 @@ 918CED592684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 918CED582684C74000CFDC83 /* ParseLiveQuery+combine.swift */; }; 918CED5E268618C600CFDC83 /* ParseLiveQueryCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 918CED5D268618C600CFDC83 /* ParseLiveQueryCombineTests.swift */; }; 9194657824F16E330070296B /* ParseACLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9194657724F16E330070296B /* ParseACLTests.swift */; }; + 919823652B3A134000E9591A /* ParsePointerable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 919823642B3A134000E9591A /* ParsePointerable.swift */; }; 91B40651267A66ED00B129CD /* ParseErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91B40650267A66ED00B129CD /* ParseErrorTests.swift */; }; 91B79AC326EE3A4E00073F2C /* API+NonParseBodyCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91B79AC226EE3A4E00073F2C /* API+NonParseBodyCommand.swift */; }; 91B79AC826EE3C5D00073F2C /* API+BatchCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91B79AC726EE3C5D00073F2C /* API+BatchCommand.swift */; }; @@ -625,6 +626,7 @@ 918CED582684C74000CFDC83 /* ParseLiveQuery+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseLiveQuery+combine.swift"; sourceTree = ""; }; 918CED5D268618C600CFDC83 /* ParseLiveQueryCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseLiveQueryCombineTests.swift; sourceTree = ""; }; 9194657724F16E330070296B /* ParseACLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseACLTests.swift; sourceTree = ""; }; + 919823642B3A134000E9591A /* ParsePointerable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParsePointerable.swift; sourceTree = ""; }; 91B40650267A66ED00B129CD /* ParseErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseErrorTests.swift; sourceTree = ""; }; 91B79AC226EE3A4E00073F2C /* API+NonParseBodyCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "API+NonParseBodyCommand.swift"; sourceTree = ""; }; 91B79AC726EE3C5D00073F2C /* API+BatchCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "API+BatchCommand.swift"; sourceTree = ""; }; @@ -942,6 +944,7 @@ 705025EF2851542D008D6624 /* ParsePushFirebasePayloadable.swift */, 705025CB284CE4C2008D6624 /* ParsePushPayloadable.swift */, 70A98D812794AB3C009B58F2 /* ParseQueryScorable.swift */, + 919823642B3A134000E9591A /* ParsePointerable.swift */, 916E206F29D8C83100C21EC6 /* ParseRelationOperationable.swift */, 70CE0ABB285F8FF900DAEA86 /* ParseTypeable.swift */, F97B45C824D9C6F200F4A88B /* Queryable.swift */, @@ -1654,6 +1657,7 @@ F97B45F624D9C6F200F4A88B /* ParseError.swift in Sources */, 7045769D26BD934000F86F71 /* ParseFile+async.swift in Sources */, F97B463324D9C74400F4A88B /* URLSession.swift in Sources */, + 919823652B3A134000E9591A /* ParsePointerable.swift in Sources */, F97B464E24D9C78B00F4A88B /* ParseOperationAdd.swift in Sources */, 70D41D8028B520E200613510 /* ParseKeychainAccessGroup.swift in Sources */, 70385E762858E1000084D306 /* ParseHookFunctionable.swift in Sources */, diff --git a/Sources/ParseSwift/Protocols/ParsePointerable.swift b/Sources/ParseSwift/Protocols/ParsePointerable.swift new file mode 100644 index 000000000..45512ee7c --- /dev/null +++ b/Sources/ParseSwift/Protocols/ParsePointerable.swift @@ -0,0 +1,97 @@ +// +// ParsePointerable.swift +// ParseSwift +// +// Created by Corey Baker on 12/25/23. +// Copyright © 2023 Network Reconnaissance Lab. All rights reserved. +// + +import Foundation + +protocol ParsePointer: Encodable { + + var __type: String { get } // swiftlint:disable:this identifier_name + + var className: String { get } + + var objectId: String { get set } +} + +extension ParsePointer { + /** + 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. + */ + func hasSameObjectId(as other: any ParsePointer) -> Bool { + return other.className == className && other.objectId == objectId + } +} + +protocol ParsePointerObject: ParsePointer, ParseTypeable, Fetchable, Hashable { + associatedtype Object: ParseObject +} + +extension ParsePointerObject { + + /** + Determines if a `ParseObject` and `Pointer`have the same `objectId`. + - parameter as: `ParseObject` to compare. + - returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful. + */ + func hasSameObjectId(as other: Object) -> Bool { + return other.className == className && other.objectId == objectId + } + + /** + Determines if two `Pointer`'s have the same `objectId`. + - parameter as: `Pointer` to compare. + - returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful. + */ + func hasSameObjectId(as other: Self) -> Bool { + return other.className == className && other.objectId == objectId + } + + /** + Fetches the `ParseObject` *asynchronously* and executes the given callback block. + - parameter includeKeys: The name(s) of the key(s) to include. Use `["*"]` to include + all keys. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default + value of .main. + - parameter completion: The block to execute when completed. + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. + */ + func fetch(includeKeys: [String]? = nil, + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void) { + Task { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + + let method = API.Method.GET + let path = API.Endpoint.object(className: className, objectId: objectId) + let params: [String: String]? = { + guard let includeKeys = includeKeys else { + return nil + } + return ["include": "\(Set(includeKeys))"] + }() + let mapper = { (data) -> Object in + try ParseCoding.jsonDecoder().decode(Object.self, from: data) + } + await API.NonParseBodyCommand(method: method, path: path, params: params, mapper: mapper) + .execute(options: options, + callbackQueue: callbackQueue, + completion: completion) + } + } +} + +// MARK: Batch Support +extension Sequence where Element: ParsePointerObject { + +} diff --git a/Sources/ParseSwift/Types/Pointer.swift b/Sources/ParseSwift/Types/Pointer.swift index 9930ecd49..ca4458103 100644 --- a/Sources/ParseSwift/Types/Pointer.swift +++ b/Sources/ParseSwift/Types/Pointer.swift @@ -1,25 +1,5 @@ import Foundation -protocol ParsePointer: Encodable { - - var __type: String { get } // swiftlint:disable:this identifier_name - - var className: String { get } - - var objectId: String { get set } -} - -extension ParsePointer { - /** - 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. - */ - func hasSameObjectId(as other: ParsePointer) -> Bool { - return other.className == className && other.objectId == objectId - } -} - private func getObjectId(target: Objectable) throws -> String { guard let objectId = target.objectId else { throw ParseError(code: .missingObjectId, message: "Cannot set a pointer to an unsaved object") @@ -28,8 +8,9 @@ private func getObjectId(target: Objectable) throws -> String { } /// A Pointer referencing a ParseObject. -public struct Pointer: ParsePointer, ParseTypeable, Fetchable, Hashable { +public struct Pointer: ParsePointerObject { + typealias Object = T internal let __type: String = "Pointer" // swiftlint:disable:this identifier_name /** @@ -76,65 +57,6 @@ public struct Pointer: ParsePointer, ParseTypeable, Fetchable, H } } -public extension Pointer { - - /** - Determines if a `ParseObject` and `Pointer`have the same `objectId`. - - parameter as: `ParseObject` to compare. - - returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful. - */ - func hasSameObjectId(as other: T) -> Bool { - return other.className == className && other.objectId == objectId - } - - /** - Determines if two `Pointer`'s have the same `objectId`. - - parameter as: `Pointer` to compare. - - returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful. - */ - func hasSameObjectId(as other: Self) -> Bool { - return other.className == className && other.objectId == objectId - } - - /** - Fetches the `ParseObject` *asynchronously* and executes the given callback block. - - parameter includeKeys: The name(s) of the key(s) to include. Use `["*"]` to include - all keys. - - parameter options: A set of header options sent to the server. Defaults to an empty set. - - parameter callbackQueue: The queue to return to after completion. Default - value of .main. - - parameter completion: The block to execute when completed. - It should have the following argument signature: `(Result)`. - - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer - desires a different policy, it should be inserted in `options`. - */ - func fetch(includeKeys: [String]? = nil, - options: API.Options = [], - callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { - Task { - var options = options - options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - - let method = API.Method.GET - let path = API.Endpoint.object(className: className, objectId: objectId) - let params: [String: String]? = { - guard let includeKeys = includeKeys else { - return nil - } - return ["include": "\(Set(includeKeys))"] - }() - let mapper = { (data) -> T in - try ParseCoding.jsonDecoder().decode(T.self, from: data) - } - await API.NonParseBodyCommand(method: method, path: path, params: params, mapper: mapper) - .execute(options: options, - callbackQueue: callbackQueue, - completion: completion) - } - } -} - internal struct PointerType: ParsePointer, Codable { var __type: String = "Pointer" // swiftlint:disable:this identifier_name var className: String