diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index d86255679..10ac86df0 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -23,6 +23,8 @@ 7003972A25A3B0140052CB31 /* ParseURLSessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7003972925A3B0130052CB31 /* ParseURLSessionDelegate.swift */; }; 7004C22025B63C7A005E0AD9 /* ParseRelation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7004C21F25B63C7A005E0AD9 /* ParseRelation.swift */; }; 7004C24D25B69207005E0AD9 /* ParseRoleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7004C22D25B69077005E0AD9 /* ParseRoleTests.swift */; }; + 700A8A662B4CC1E40087ADBE /* ParsePointerable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700A8A652B4CC1E40087ADBE /* ParsePointerable+async.swift */; }; + 700A8A682B4CC2700087ADBE /* ParsePointerable+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700A8A672B4CC2700087ADBE /* ParsePointerable+combine.swift */; }; 700AFE03289C3508006C1CD9 /* ParseQueryCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700AFE02289C3508006C1CD9 /* ParseQueryCacheTests.swift */; }; 70110D52250680140091CC1D /* ParseConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70110D51250680140091CC1D /* ParseConstants.swift */; }; 70110D572506CE890091CC1D /* BaseParseInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70110D562506CE890091CC1D /* BaseParseInstallation.swift */; }; @@ -365,6 +367,8 @@ 7003972925A3B0130052CB31 /* ParseURLSessionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseURLSessionDelegate.swift; sourceTree = ""; }; 7004C21F25B63C7A005E0AD9 /* ParseRelation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseRelation.swift; sourceTree = ""; }; 7004C22D25B69077005E0AD9 /* ParseRoleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseRoleTests.swift; sourceTree = ""; }; + 700A8A652B4CC1E40087ADBE /* ParsePointerable+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParsePointerable+async.swift"; sourceTree = ""; }; + 700A8A672B4CC2700087ADBE /* ParsePointerable+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParsePointerable+combine.swift"; sourceTree = ""; }; 700AFE02289C3508006C1CD9 /* ParseQueryCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseQueryCacheTests.swift; sourceTree = ""; }; 70110D51250680140091CC1D /* ParseConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConstants.swift; sourceTree = ""; }; 70110D562506CE890091CC1D /* BaseParseInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseParseInstallation.swift; sourceTree = ""; }; @@ -945,6 +949,8 @@ 705025CB284CE4C2008D6624 /* ParsePushPayloadable.swift */, 70A98D812794AB3C009B58F2 /* ParseQueryScorable.swift */, 919823642B3A134000E9591A /* ParsePointerable.swift */, + 700A8A652B4CC1E40087ADBE /* ParsePointerable+async.swift */, + 700A8A672B4CC2700087ADBE /* ParsePointerable+combine.swift */, 916E206F29D8C83100C21EC6 /* ParseRelationOperationable.swift */, 70CE0ABB285F8FF900DAEA86 /* ParseTypeable.swift */, F97B45C824D9C6F200F4A88B /* Queryable.swift */, @@ -1560,6 +1566,7 @@ 703B094E26BF47E3005A112F /* ParseTwitter+combine.swift in Sources */, 70386A3825D998D90048EC1B /* ParseLDAP.swift in Sources */, 709A14A02839CABD00BF85E5 /* ParseCLP.swift in Sources */, + 700A8A662B4CC1E40087ADBE /* ParsePointerable+async.swift in Sources */, 700395F225A171320052CB31 /* LiveQueryable.swift in Sources */, 70F03A252780BDF700E5AFB4 /* ParseGoogle+async.swift in Sources */, F97B45F224D9C6F200F4A88B /* Pointer.swift in Sources */, @@ -1606,6 +1613,7 @@ 7045769326BD8F8100F86F71 /* ParseInstallation+async.swift in Sources */, 7C55F9E72860CD6B002A352D /* ParseSpotify.swift in Sources */, 7034B9FF2A46391200395CBC /* ParseHookFunction.swift in Sources */, + 700A8A682B4CC2700087ADBE /* ParsePointerable+combine.swift in Sources */, 7003960925A184EF0052CB31 /* ParseLiveQuery.swift in Sources */, 7044C17525C4ECFF0011F6E7 /* ParseCloudable+combine.swift in Sources */, 705025B32845C302008D6624 /* ParsePushStatus.swift in Sources */, diff --git a/Sources/ParseSwift/Protocols/ParsePointerable+async.swift b/Sources/ParseSwift/Protocols/ParsePointerable+async.swift new file mode 100644 index 000000000..b6a43991c --- /dev/null +++ b/Sources/ParseSwift/Protocols/ParsePointerable+async.swift @@ -0,0 +1,36 @@ +// +// ParsePointerable+async.swift +// ParseSwift +// +// Created by Corey Baker on 1/8/24. +// Copyright © 2024 Network Reconnaissance Lab. All rights reserved. +// + +import Foundation + +// swiftlint:disable line_length + +// MARK: Batch Support +public extension Sequence where Element: ParsePointerObject { + + /** + Fetches a collection of objects *aynchronously* with the current data from the server and sets + an error if one occurs. + - parameter includeKeys: The name(s) of the key(s) to include that are + `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and + `includeAll` for `Query`. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: Returns an array of Result enums with the object if a fetch was successful or a + `ParseError` if it failed. + - throws: An error of type `ParseError`. + */ + @discardableResult func fetchAll(includeKeys: [String]? = nil, + options: API.Options = []) async throws -> [(Result)] { + try await withCheckedThrowingContinuation { continuation in + self.fetchAll(includeKeys: includeKeys, + options: options, + completion: continuation.resume) + } + } + +} diff --git a/Sources/ParseSwift/Protocols/ParsePointerable+combine.swift b/Sources/ParseSwift/Protocols/ParsePointerable+combine.swift new file mode 100644 index 000000000..1b6faff42 --- /dev/null +++ b/Sources/ParseSwift/Protocols/ParsePointerable+combine.swift @@ -0,0 +1,40 @@ +// +// ParsePointerable+combine.swift +// ParseSwift +// +// Created by Corey Baker on 1/8/24. +// Copyright © 2024 Network Reconnaissance Lab. All rights reserved. +// + +#if canImport(Combine) + +import Foundation +import Combine + +// swiftlint:disable line_length + +// MARK: Batch Support +public extension Sequence where Element: ParsePointerObject { + + /** + Fetches a collection of objects *aynchronously* with the current data from the server and sets + an error if one occurs. Publishes when complete. + - parameter includeKeys: The name(s) of the key(s) to include that are + `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and + `includeAll` for `Query`. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: A publisher that eventually produces an an array of Result enums with the object if a fetch was + successful or a `ParseError` if it failed. + */ + func fetchAllPublisher(includeKeys: [String]? = nil, + options: API.Options = []) -> Future<[(Result)], ParseError> { + Future { promise in + self.fetchAll(includeKeys: includeKeys, + options: options, + completion: promise) + } + } + +} + +#endif diff --git a/Sources/ParseSwift/Protocols/ParsePointerable.swift b/Sources/ParseSwift/Protocols/ParsePointerable.swift index 45512ee7c..6edcdd6e6 100644 --- a/Sources/ParseSwift/Protocols/ParsePointerable.swift +++ b/Sources/ParseSwift/Protocols/ParsePointerable.swift @@ -8,7 +8,7 @@ import Foundation -protocol ParsePointer: Encodable { +public protocol ParsePointer: Encodable { var __type: String { get } // swiftlint:disable:this identifier_name @@ -28,12 +28,22 @@ extension ParsePointer { } } -protocol ParsePointerObject: ParsePointer, ParseTypeable, Fetchable, Hashable { +public protocol ParsePointerObject: ParsePointer, ParseTypeable, Fetchable, Hashable { associatedtype Object: ParseObject } extension ParsePointerObject { + /** + Convert a Pointer to its respective `ParseObject`. + - returns: A `ParseObject` created from this Pointer. + */ + func toObject() -> Object { + var object = Object() + object.objectId = self.objectId + return object + } + /** Determines if a `ParseObject` and `Pointer`have the same `objectId`. - parameter as: `ParseObject` to compare. @@ -92,6 +102,33 @@ extension ParsePointerObject { } // MARK: Batch Support -extension Sequence where Element: ParsePointerObject { +public extension Sequence where Element: ParsePointerObject { + + /** + Fetches a collection of objects all at once *asynchronously* and executes the completion block when done. + - parameter includeKeys: The name(s) of the key(s) to include that are + `ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and + `includeAll` for `Query`. + - 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. + It should have the following argument signature: `(Result<[(Result)], ParseError>)`. + - warning: The order in which objects are returned are not guarenteed. You should not expect results in + any particular order. + */ + func fetchAll( + includeKeys: [String]? = nil, + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result<[(Result)], ParseError>) -> Void + ) { + let objects = Set(compactMap { $0.toObject() }) + objects.fetchAll( + includeKeys: includeKeys, + options: options, + callbackQueue: callbackQueue, + completion: completion + ) + } } diff --git a/Sources/ParseSwift/Types/Pointer.swift b/Sources/ParseSwift/Types/Pointer.swift index ca4458103..5e96cbbc8 100644 --- a/Sources/ParseSwift/Types/Pointer.swift +++ b/Sources/ParseSwift/Types/Pointer.swift @@ -10,8 +10,8 @@ private func getObjectId(target: Objectable) throws -> String { /// A Pointer referencing a ParseObject. public struct Pointer: ParsePointerObject { - typealias Object = T - internal let __type: String = "Pointer" // swiftlint:disable:this identifier_name + public typealias Object = T + public let __type: String = "Pointer" // swiftlint:disable:this identifier_name /** The class name of the object. diff --git a/Tests/ParseSwiftTests/BatchUtilsTests.swift b/Tests/ParseSwiftTests/BatchUtilsTests.swift index a15d449a4..260a49abc 100644 --- a/Tests/ParseSwiftTests/BatchUtilsTests.swift +++ b/Tests/ParseSwiftTests/BatchUtilsTests.swift @@ -11,13 +11,6 @@ import XCTest @testable import ParseSwift class BatchUtilsTests: XCTestCase { - override func setUp() async throws { - try await super.setUp() - } - - override func tearDown() async throws { - try await super.tearDown() - } func testSplitArrayLessSegments() throws { let array = [1, 2]