Skip to content
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

feat: Add fetchAll method for Parse pointers #141

Merged
merged 7 commits into from
Jan 9, 2024
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
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@
# Parse-Swift Changelog

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

### 5.9.0
[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)

__New features__
* 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).

### 5.8.2
[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)

Expand Down
16 changes: 15 additions & 1 deletion ParseSwift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -278,6 +280,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 */; };
Expand Down Expand Up @@ -364,6 +367,8 @@
7003972925A3B0130052CB31 /* ParseURLSessionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseURLSessionDelegate.swift; sourceTree = "<group>"; };
7004C21F25B63C7A005E0AD9 /* ParseRelation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseRelation.swift; sourceTree = "<group>"; };
7004C22D25B69077005E0AD9 /* ParseRoleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseRoleTests.swift; sourceTree = "<group>"; };
700A8A652B4CC1E40087ADBE /* ParsePointerable+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParsePointerable+async.swift"; sourceTree = "<group>"; };
700A8A672B4CC2700087ADBE /* ParsePointerable+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParsePointerable+combine.swift"; sourceTree = "<group>"; };
700AFE02289C3508006C1CD9 /* ParseQueryCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseQueryCacheTests.swift; sourceTree = "<group>"; };
70110D51250680140091CC1D /* ParseConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConstants.swift; sourceTree = "<group>"; };
70110D562506CE890091CC1D /* BaseParseInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseParseInstallation.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -625,6 +630,7 @@
918CED582684C74000CFDC83 /* ParseLiveQuery+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseLiveQuery+combine.swift"; sourceTree = "<group>"; };
918CED5D268618C600CFDC83 /* ParseLiveQueryCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseLiveQueryCombineTests.swift; sourceTree = "<group>"; };
9194657724F16E330070296B /* ParseACLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseACLTests.swift; sourceTree = "<group>"; };
919823642B3A134000E9591A /* ParsePointerable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParsePointerable.swift; sourceTree = "<group>"; };
91B40650267A66ED00B129CD /* ParseErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseErrorTests.swift; sourceTree = "<group>"; };
91B79AC226EE3A4E00073F2C /* API+NonParseBodyCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "API+NonParseBodyCommand.swift"; sourceTree = "<group>"; };
91B79AC726EE3C5D00073F2C /* API+BatchCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "API+BatchCommand.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -942,6 +948,9 @@
705025EF2851542D008D6624 /* ParsePushFirebasePayloadable.swift */,
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 */,
Expand Down Expand Up @@ -1410,7 +1419,7 @@
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1430;
LastUpgradeCheck = 1500;
LastUpgradeCheck = 1520;
ORGANIZATIONNAME = "Network Reconnaissance Lab";
TargetAttributes = {
4AB8B4F31F254AE10070F682 = {
Expand Down Expand Up @@ -1557,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 */,
Expand Down Expand Up @@ -1603,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 */,
Expand Down Expand Up @@ -1654,6 +1665,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 */,
Expand Down Expand Up @@ -1848,6 +1860,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
Expand Down Expand Up @@ -1917,6 +1930,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
LastUpgradeVersion = "1520"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
LastUpgradeVersion = "1520"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/ParseConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation

enum ParseConstants {
static let sdk = "swift"
static let version = "5.8.2"
static let version = "5.9.0"
static let fileManagementDirectory = "parse/"
static let fileManagementPrivateDocumentsDirectory = "Private Documents/"
static let fileManagementLibraryDirectory = "Library/"
Expand Down
38 changes: 38 additions & 0 deletions Sources/ParseSwift/Protocols/ParsePointerable+async.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// ParsePointerable+async.swift
// ParseSwift
//
// Created by Corey Baker on 1/8/24.
// Copyright © 2024 Network Reconnaissance Lab. All rights reserved.
//

import Foundation

// 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<Self.Element.Object, ParseError>)] {
try await withCheckedThrowingContinuation { continuation in
self.fetchAll(
includeKeys: includeKeys,
options: options,
completion: continuation.resume
)
}
}

}
41 changes: 41 additions & 0 deletions Sources/ParseSwift/Protocols/ParsePointerable+combine.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// 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

// 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<Self.Element.Object, ParseError>)], ParseError> {
Future { promise in
self.fetchAll(
includeKeys: includeKeys,
options: options,
completion: promise
)
}
}

}

#endif
134 changes: 134 additions & 0 deletions Sources/ParseSwift/Protocols/ParsePointerable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
//
// ParsePointerable.swift
// ParseSwift
//
// Created by Corey Baker on 12/25/23.
// Copyright © 2023 Network Reconnaissance Lab. All rights reserved.
//

import Foundation

public 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
}
}

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.
- 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<T, ParseError>)`.
- 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<Object, ParseError>) -> 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<NoBody, Object>(method: method, path: path, params: params, mapper: mapper)
.execute(options: options,
callbackQueue: callbackQueue,
completion: completion)
}
}
}

// MARK: Batch Support
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<Element.Object, ParseError>)], 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<Element.Object, ParseError>)], ParseError>) -> Void
) {
let objects = Set(compactMap { $0.toObject() })
objects.fetchAll(
includeKeys: includeKeys,
options: options,
callbackQueue: callbackQueue,
completion: completion
)
}

}
Loading