Skip to content

Commit 0cc07eb

Browse files
authored
feat: Add fetchAll method for Parse pointers (#141)
* feat: Add fetchAll method for Parse pointers * add fetchAll methods * Update project * add test cases * nit * nits
1 parent 7b3e48b commit 0cc07eb

File tree

12 files changed

+467
-93
lines changed

12 files changed

+467
-93
lines changed

CHANGELOG.md

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

44
### main
5-
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.8.2...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift)
5+
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.9.0...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift)
66
* _Contributing to this repo? Add info about your change here to be included in the next release_
77

8+
### 5.9.0
9+
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.8.2...5.9.0), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.9.0/documentation/parseswift)
10+
11+
__New features__
12+
* Add fetchAll method to array of Parse Pointer Object's ([#141](https://github.com/netreconlab/Parse-Swift/pull/141)), thanks to [Corey Baker](https://github.com/cbaker6).
13+
814
### 5.8.2
915
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.8.1...5.8.2), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.8.2/documentation/parseswift)
1016

ParseSwift.xcodeproj/project.pbxproj

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
7003972A25A3B0140052CB31 /* ParseURLSessionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7003972925A3B0130052CB31 /* ParseURLSessionDelegate.swift */; };
2424
7004C22025B63C7A005E0AD9 /* ParseRelation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7004C21F25B63C7A005E0AD9 /* ParseRelation.swift */; };
2525
7004C24D25B69207005E0AD9 /* ParseRoleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7004C22D25B69077005E0AD9 /* ParseRoleTests.swift */; };
26+
700A8A662B4CC1E40087ADBE /* ParsePointerable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700A8A652B4CC1E40087ADBE /* ParsePointerable+async.swift */; };
27+
700A8A682B4CC2700087ADBE /* ParsePointerable+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700A8A672B4CC2700087ADBE /* ParsePointerable+combine.swift */; };
2628
700AFE03289C3508006C1CD9 /* ParseQueryCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 700AFE02289C3508006C1CD9 /* ParseQueryCacheTests.swift */; };
2729
70110D52250680140091CC1D /* ParseConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70110D51250680140091CC1D /* ParseConstants.swift */; };
2830
70110D572506CE890091CC1D /* BaseParseInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70110D562506CE890091CC1D /* BaseParseInstallation.swift */; };
@@ -278,6 +280,7 @@
278280
918CED592684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 918CED582684C74000CFDC83 /* ParseLiveQuery+combine.swift */; };
279281
918CED5E268618C600CFDC83 /* ParseLiveQueryCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 918CED5D268618C600CFDC83 /* ParseLiveQueryCombineTests.swift */; };
280282
9194657824F16E330070296B /* ParseACLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9194657724F16E330070296B /* ParseACLTests.swift */; };
283+
919823652B3A134000E9591A /* ParsePointerable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 919823642B3A134000E9591A /* ParsePointerable.swift */; };
281284
91B40651267A66ED00B129CD /* ParseErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91B40650267A66ED00B129CD /* ParseErrorTests.swift */; };
282285
91B79AC326EE3A4E00073F2C /* API+NonParseBodyCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91B79AC226EE3A4E00073F2C /* API+NonParseBodyCommand.swift */; };
283286
91B79AC826EE3C5D00073F2C /* API+BatchCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91B79AC726EE3C5D00073F2C /* API+BatchCommand.swift */; };
@@ -364,6 +367,8 @@
364367
7003972925A3B0130052CB31 /* ParseURLSessionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseURLSessionDelegate.swift; sourceTree = "<group>"; };
365368
7004C21F25B63C7A005E0AD9 /* ParseRelation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseRelation.swift; sourceTree = "<group>"; };
366369
7004C22D25B69077005E0AD9 /* ParseRoleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseRoleTests.swift; sourceTree = "<group>"; };
370+
700A8A652B4CC1E40087ADBE /* ParsePointerable+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParsePointerable+async.swift"; sourceTree = "<group>"; };
371+
700A8A672B4CC2700087ADBE /* ParsePointerable+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParsePointerable+combine.swift"; sourceTree = "<group>"; };
367372
700AFE02289C3508006C1CD9 /* ParseQueryCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseQueryCacheTests.swift; sourceTree = "<group>"; };
368373
70110D51250680140091CC1D /* ParseConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConstants.swift; sourceTree = "<group>"; };
369374
70110D562506CE890091CC1D /* BaseParseInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseParseInstallation.swift; sourceTree = "<group>"; };
@@ -625,6 +630,7 @@
625630
918CED582684C74000CFDC83 /* ParseLiveQuery+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseLiveQuery+combine.swift"; sourceTree = "<group>"; };
626631
918CED5D268618C600CFDC83 /* ParseLiveQueryCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseLiveQueryCombineTests.swift; sourceTree = "<group>"; };
627632
9194657724F16E330070296B /* ParseACLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseACLTests.swift; sourceTree = "<group>"; };
633+
919823642B3A134000E9591A /* ParsePointerable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParsePointerable.swift; sourceTree = "<group>"; };
628634
91B40650267A66ED00B129CD /* ParseErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseErrorTests.swift; sourceTree = "<group>"; };
629635
91B79AC226EE3A4E00073F2C /* API+NonParseBodyCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "API+NonParseBodyCommand.swift"; sourceTree = "<group>"; };
630636
91B79AC726EE3C5D00073F2C /* API+BatchCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "API+BatchCommand.swift"; sourceTree = "<group>"; };
@@ -942,6 +948,9 @@
942948
705025EF2851542D008D6624 /* ParsePushFirebasePayloadable.swift */,
943949
705025CB284CE4C2008D6624 /* ParsePushPayloadable.swift */,
944950
70A98D812794AB3C009B58F2 /* ParseQueryScorable.swift */,
951+
919823642B3A134000E9591A /* ParsePointerable.swift */,
952+
700A8A652B4CC1E40087ADBE /* ParsePointerable+async.swift */,
953+
700A8A672B4CC2700087ADBE /* ParsePointerable+combine.swift */,
945954
916E206F29D8C83100C21EC6 /* ParseRelationOperationable.swift */,
946955
70CE0ABB285F8FF900DAEA86 /* ParseTypeable.swift */,
947956
F97B45C824D9C6F200F4A88B /* Queryable.swift */,
@@ -1410,7 +1419,7 @@
14101419
attributes = {
14111420
BuildIndependentTargetsInParallel = YES;
14121421
LastSwiftUpdateCheck = 1430;
1413-
LastUpgradeCheck = 1500;
1422+
LastUpgradeCheck = 1520;
14141423
ORGANIZATIONNAME = "Network Reconnaissance Lab";
14151424
TargetAttributes = {
14161425
4AB8B4F31F254AE10070F682 = {
@@ -1557,6 +1566,7 @@
15571566
703B094E26BF47E3005A112F /* ParseTwitter+combine.swift in Sources */,
15581567
70386A3825D998D90048EC1B /* ParseLDAP.swift in Sources */,
15591568
709A14A02839CABD00BF85E5 /* ParseCLP.swift in Sources */,
1569+
700A8A662B4CC1E40087ADBE /* ParsePointerable+async.swift in Sources */,
15601570
700395F225A171320052CB31 /* LiveQueryable.swift in Sources */,
15611571
70F03A252780BDF700E5AFB4 /* ParseGoogle+async.swift in Sources */,
15621572
F97B45F224D9C6F200F4A88B /* Pointer.swift in Sources */,
@@ -1603,6 +1613,7 @@
16031613
7045769326BD8F8100F86F71 /* ParseInstallation+async.swift in Sources */,
16041614
7C55F9E72860CD6B002A352D /* ParseSpotify.swift in Sources */,
16051615
7034B9FF2A46391200395CBC /* ParseHookFunction.swift in Sources */,
1616+
700A8A682B4CC2700087ADBE /* ParsePointerable+combine.swift in Sources */,
16061617
7003960925A184EF0052CB31 /* ParseLiveQuery.swift in Sources */,
16071618
7044C17525C4ECFF0011F6E7 /* ParseCloudable+combine.swift in Sources */,
16081619
705025B32845C302008D6624 /* ParsePushStatus.swift in Sources */,
@@ -1654,6 +1665,7 @@
16541665
F97B45F624D9C6F200F4A88B /* ParseError.swift in Sources */,
16551666
7045769D26BD934000F86F71 /* ParseFile+async.swift in Sources */,
16561667
F97B463324D9C74400F4A88B /* URLSession.swift in Sources */,
1668+
919823652B3A134000E9591A /* ParsePointerable.swift in Sources */,
16571669
F97B464E24D9C78B00F4A88B /* ParseOperationAdd.swift in Sources */,
16581670
70D41D8028B520E200613510 /* ParseKeychainAccessGroup.swift in Sources */,
16591671
70385E762858E1000084D306 /* ParseHookFunctionable.swift in Sources */,
@@ -1848,6 +1860,7 @@
18481860
isa = XCBuildConfiguration;
18491861
buildSettings = {
18501862
ALWAYS_SEARCH_USER_PATHS = NO;
1863+
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
18511864
CLANG_ANALYZER_NONNULL = YES;
18521865
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
18531866
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@@ -1917,6 +1930,7 @@
19171930
isa = XCBuildConfiguration;
19181931
buildSettings = {
19191932
ALWAYS_SEARCH_USER_PATHS = NO;
1933+
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
19201934
CLANG_ANALYZER_NONNULL = YES;
19211935
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
19221936
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";

ParseSwift.xcodeproj/xcshareddata/xcschemes/ParseSwift.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1500"
3+
LastUpgradeVersion = "1520"
44
version = "1.7">
55
<BuildAction
66
parallelizeBuildables = "YES"

ParseSwift.xcodeproj/xcshareddata/xcschemes/TestHost.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1500"
3+
LastUpgradeVersion = "1520"
44
version = "1.7">
55
<BuildAction
66
parallelizeBuildables = "YES"

Sources/ParseSwift/ParseConstants.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010

1111
enum ParseConstants {
1212
static let sdk = "swift"
13-
static let version = "5.8.2"
13+
static let version = "5.9.0"
1414
static let fileManagementDirectory = "parse/"
1515
static let fileManagementPrivateDocumentsDirectory = "Private Documents/"
1616
static let fileManagementLibraryDirectory = "Library/"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// ParsePointerable+async.swift
3+
// ParseSwift
4+
//
5+
// Created by Corey Baker on 1/8/24.
6+
// Copyright © 2024 Network Reconnaissance Lab. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
// MARK: Batch Support
12+
public extension Sequence where Element: ParsePointerObject {
13+
14+
/**
15+
Fetches a collection of objects *aynchronously* with the current data from the server and sets
16+
an error if one occurs.
17+
- parameter includeKeys: The name(s) of the key(s) to include that are
18+
`ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and
19+
`includeAll` for `Query`.
20+
- parameter options: A set of header options sent to the server. Defaults to an empty set.
21+
- returns: Returns an array of Result enums with the object if a fetch was successful or a
22+
`ParseError` if it failed.
23+
- throws: An error of type `ParseError`.
24+
*/
25+
@discardableResult func fetchAll(
26+
includeKeys: [String]? = nil,
27+
options: API.Options = []
28+
) async throws -> [(Result<Self.Element.Object, ParseError>)] {
29+
try await withCheckedThrowingContinuation { continuation in
30+
self.fetchAll(
31+
includeKeys: includeKeys,
32+
options: options,
33+
completion: continuation.resume
34+
)
35+
}
36+
}
37+
38+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// ParsePointerable+combine.swift
3+
// ParseSwift
4+
//
5+
// Created by Corey Baker on 1/8/24.
6+
// Copyright © 2024 Network Reconnaissance Lab. All rights reserved.
7+
//
8+
9+
#if canImport(Combine)
10+
11+
import Foundation
12+
import Combine
13+
14+
// MARK: Batch Support
15+
public extension Sequence where Element: ParsePointerObject {
16+
17+
/**
18+
Fetches a collection of objects *aynchronously* with the current data from the server and sets
19+
an error if one occurs. Publishes when complete.
20+
- parameter includeKeys: The name(s) of the key(s) to include that are
21+
`ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and
22+
`includeAll` for `Query`.
23+
- parameter options: A set of header options sent to the server. Defaults to an empty set.
24+
- returns: A publisher that eventually produces an an array of Result enums with the object if a fetch was
25+
successful or a `ParseError` if it failed.
26+
*/
27+
func fetchAllPublisher(
28+
includeKeys: [String]? = nil,
29+
options: API.Options = []) -> Future<[(Result<Self.Element.Object, ParseError>)], ParseError> {
30+
Future { promise in
31+
self.fetchAll(
32+
includeKeys: includeKeys,
33+
options: options,
34+
completion: promise
35+
)
36+
}
37+
}
38+
39+
}
40+
41+
#endif
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
//
2+
// ParsePointerable.swift
3+
// ParseSwift
4+
//
5+
// Created by Corey Baker on 12/25/23.
6+
// Copyright © 2023 Network Reconnaissance Lab. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public protocol ParsePointer: Encodable {
12+
13+
var __type: String { get } // swiftlint:disable:this identifier_name
14+
15+
var className: String { get }
16+
17+
var objectId: String { get set }
18+
}
19+
20+
extension ParsePointer {
21+
/**
22+
Determines if two objects have the same objectId.
23+
- parameter as: Object to compare.
24+
- returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful.
25+
*/
26+
func hasSameObjectId(as other: any ParsePointer) -> Bool {
27+
return other.className == className && other.objectId == objectId
28+
}
29+
}
30+
31+
public protocol ParsePointerObject: ParsePointer, ParseTypeable, Fetchable, Hashable {
32+
associatedtype Object: ParseObject
33+
}
34+
35+
extension ParsePointerObject {
36+
37+
/**
38+
Convert a Pointer to its respective `ParseObject`.
39+
- returns: A `ParseObject` created from this Pointer.
40+
*/
41+
func toObject() -> Object {
42+
var object = Object()
43+
object.objectId = self.objectId
44+
return object
45+
}
46+
47+
/**
48+
Determines if a `ParseObject` and `Pointer`have the same `objectId`.
49+
- parameter as: `ParseObject` to compare.
50+
- returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful.
51+
*/
52+
func hasSameObjectId(as other: Object) -> Bool {
53+
return other.className == className && other.objectId == objectId
54+
}
55+
56+
/**
57+
Determines if two `Pointer`'s have the same `objectId`.
58+
- parameter as: `Pointer` to compare.
59+
- returns: Returns a **true** if the other object has the same `objectId` or **false** if unsuccessful.
60+
*/
61+
func hasSameObjectId(as other: Self) -> Bool {
62+
return other.className == className && other.objectId == objectId
63+
}
64+
65+
/**
66+
Fetches the `ParseObject` *asynchronously* and executes the given callback block.
67+
- parameter includeKeys: The name(s) of the key(s) to include. Use `["*"]` to include
68+
all keys.
69+
- parameter options: A set of header options sent to the server. Defaults to an empty set.
70+
- parameter callbackQueue: The queue to return to after completion. Default
71+
value of .main.
72+
- parameter completion: The block to execute when completed.
73+
It should have the following argument signature: `(Result<T, ParseError>)`.
74+
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
75+
desires a different policy, it should be inserted in `options`.
76+
*/
77+
func fetch(includeKeys: [String]? = nil,
78+
options: API.Options = [],
79+
callbackQueue: DispatchQueue = .main,
80+
completion: @escaping (Result<Object, ParseError>) -> Void) {
81+
Task {
82+
var options = options
83+
options.insert(.cachePolicy(.reloadIgnoringLocalCacheData))
84+
85+
let method = API.Method.GET
86+
let path = API.Endpoint.object(className: className, objectId: objectId)
87+
let params: [String: String]? = {
88+
guard let includeKeys = includeKeys else {
89+
return nil
90+
}
91+
return ["include": "\(Set(includeKeys))"]
92+
}()
93+
let mapper = { (data) -> Object in
94+
try ParseCoding.jsonDecoder().decode(Object.self, from: data)
95+
}
96+
await API.NonParseBodyCommand<NoBody, Object>(method: method, path: path, params: params, mapper: mapper)
97+
.execute(options: options,
98+
callbackQueue: callbackQueue,
99+
completion: completion)
100+
}
101+
}
102+
}
103+
104+
// MARK: Batch Support
105+
public extension Sequence where Element: ParsePointerObject {
106+
107+
/**
108+
Fetches a collection of objects all at once *asynchronously* and executes the completion block when done.
109+
- parameter includeKeys: The name(s) of the key(s) to include that are
110+
`ParseObject`s. Use `["*"]` to include all keys one level deep. This is similar to `include` and
111+
`includeAll` for `Query`.
112+
- parameter options: A set of header options sent to the server. Defaults to an empty set.
113+
- parameter callbackQueue: The queue to return to after completion. Default value of .main.
114+
- parameter completion: The block to execute.
115+
It should have the following argument signature: `(Result<[(Result<Element.Object, ParseError>)], ParseError>)`.
116+
- warning: The order in which objects are returned are not guarenteed. You should not expect results in
117+
any particular order.
118+
*/
119+
func fetchAll(
120+
includeKeys: [String]? = nil,
121+
options: API.Options = [],
122+
callbackQueue: DispatchQueue = .main,
123+
completion: @escaping (Result<[(Result<Element.Object, ParseError>)], ParseError>) -> Void
124+
) {
125+
let objects = Set(compactMap { $0.toObject() })
126+
objects.fetchAll(
127+
includeKeys: includeKeys,
128+
options: options,
129+
callbackQueue: callbackQueue,
130+
completion: completion
131+
)
132+
}
133+
134+
}

0 commit comments

Comments
 (0)