diff --git a/.codecov.yml b/.codecov.yml index 7009a7cb2..59006cb1a 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -6,7 +6,7 @@ coverage: status: patch: default: - target: auto + target: 71 changes: false project: default: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a20febd58..f5e6de7be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,17 +6,15 @@ on: branches: '*' env: CI_XCODE_VER: '/Applications/Xcode_11.7.app/Contents/Developer' - CI_XCODE_13: '/Applications/Xcode_13.1.app/Contents/Developer' + CI_XCODE_13: '/Applications/Xcode_13.2.app/Contents/Developer' jobs: xcode-test-ios: runs-on: macos-latest steps: - uses: actions/checkout@v2 - - name: Version - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -version - env: - DEVELOPER_DIR: ${{ env.CI_XCODE_13 }} + #- name: Use multiple cores + # run: defaults write com.apple.dt.XCBuild EnableSwiftBuildSystemIntegration 1 - name: Build-Test run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace Parse.xcworkspace -scheme ParseSwift\ \(iOS\) -destination platform\=iOS\ Simulator,name\=iPhone\ 12\ Pro\ Max -derivedDataPath DerivedData -test-iterations 10 -retry-tests-on-failure clean test | xcpretty env: @@ -48,6 +46,8 @@ jobs: security default-keychain -s temporary security unlock-keychain -p "" temporary security set-keychain-settings -lut 7200 temporary + #- name: Use multiple cores + # run: defaults write com.apple.dt.XCBuild EnableSwiftBuildSystemIntegration 1 - name: Build-Test run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace Parse.xcworkspace -scheme ParseSwift\ \(macOS\) -destination platform\=macOS -derivedDataPath DerivedData -test-iterations 10 -retry-tests-on-failure clean test | xcpretty env: @@ -73,6 +73,8 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v2 + #- name: Use multiple cores + # run: defaults write com.apple.dt.XCBuild EnableSwiftBuildSystemIntegration 1 - name: Build run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace Parse.xcworkspace -scheme ParseSwift\ \(tvOS\) -destination platform\=tvOS\ Simulator,name\=Apple\ TV -derivedDataPath DerivedData -test-iterations 10 -retry-tests-on-failure clean test | xcpretty env: @@ -98,6 +100,8 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v2 + #- name: Use multiple cores + # run: defaults write com.apple.dt.XCBuild EnableSwiftBuildSystemIntegration 1 - name: Build run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -target ParseSwift\ \(watchOS\) | xcpretty env: @@ -119,6 +123,8 @@ jobs: security default-keychain -s temporary security unlock-keychain -p "" temporary security set-keychain-settings -lut 7200 temporary + #- name: Use multiple cores + # run: defaults write com.apple.dt.XCBuild EnableSwiftBuildSystemIntegration 1 - name: Build and Test run: swift test --enable-code-coverage -v env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 61a27f4ab..ad95bb35a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,8 @@ jobs: - uses: actions/checkout@v2 - name: Get release version run: echo "TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV +# - name: Use multiple cores +# run: defaults write com.apple.dt.XCBuild EnableSwiftBuildSystemIntegration 1 - name: Update Framework Version run: ./Scripts/update_build env: @@ -41,6 +43,8 @@ jobs: bundle install - name: Get release version run: echo "TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV + # - name: Use multiple cores + # run: defaults write com.apple.dt.XCBuild EnableSwiftBuildSystemIntegration 1 - name: Create Jazzy Docs run: ./Scripts/jazzy.sh env: diff --git a/CHANGELOG.md b/CHANGELOG.md index 3037e1e9c..aa333f4ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,24 @@ ### main -[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.3.1...main) +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.4.0...main) * _Contributing to this repo? Add info about your change here to be included in the next release_ +### 2.4.0 +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.3.1...2.4.0) + +__Improvements__ +- Added additional methods to ParseRelation to make it easier to create and query relations ([#294](https://github.com/parse-community/Parse-Swift/pull/294)), thanks to [Corey Baker](https://github.com/cbaker6). +- Enable async/await for iOS13, tvOS13, watchOS6, and macOS10_15. All async/await methods are @MainActor's. Requires Xcode 13.2 or above to use async/await. Not compatible with Xcode 13.0/1, will need to upgrade to 13.2+. Still works with Xcode 11/12 ([#278](https://github.com/parse-community/Parse-Swift/pull/278)), thanks to [Corey Baker](https://github.com/cbaker6). + +__Fixes__ +- When transactions are enabled errors are now thrown from the client if the amount of objects in a transaction exceeds the batch size. An error will also be thrown if a developer attempts to save objects in a transation that has unsaved children ([#295](https://github.com/parse-community/Parse-Swift/pull/294)), thanks to [Corey Baker](https://github.com/cbaker6). + ### 2.3.1 [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.3.0...2.3.1) __Fixes__ -- Fixed an issue querying an object didn't dispatch to the proper queue which can cause app crashes ([#293](https://github.com/parse-community/Parse-Swift/pull/293)), thanks to [Corey Baker](https://github.com/cbaker6). +- Fixed an issue where querying an object didn't dispatch to the proper queue which can cause app crashes ([#293](https://github.com/parse-community/Parse-Swift/pull/293)), thanks to [Corey Baker](https://github.com/cbaker6). ### 2.3.0 [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.2.6...2.3.0) diff --git a/ParseSwift.playground/Pages/2 - Finding Objects.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/2 - Finding Objects.xcplaygroundpage/Contents.swift index cdaa88742..9d205f228 100644 --- a/ParseSwift.playground/Pages/2 - Finding Objects.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/2 - Finding Objects.xcplaygroundpage/Contents.swift @@ -76,19 +76,19 @@ results.forEach { score in //: Query highest score using async/await #if swift(>=5.5) && canImport(_Concurrency) import _Concurrency -if #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) { - let highestScoresQuery = GameScore.query("isHighest" == true) - Task { - do { - let highestScores = try await highestScoresQuery.find() - highestScores.forEach { score in - print("Found highest score: \(score)") - } - } catch { - print("Error: \(error)") + +let highestScoresQuery = GameScore.query("isHighest" == true) +Task { + do { + let highestScores = try await highestScoresQuery.find() + highestScores.forEach { score in + print("Found highest score: \(score)") } + } catch { + print("Error: \(error)") } } + #endif //: Query first asynchronously (preferred way) - Performs work on background diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index a5e31b8c7..44882defb 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -1412,11 +1412,11 @@ 706436A227341F36007C6461 /* Extensions */ = { isa = PBXGroup; children = ( - F97B462C24D9C74400F4A88B /* URLSession.swift */, - 9116F66E26A35D600082F6D6 /* URLCache.swift */, - 706436A327341F6E007C6461 /* Encodable.swift */, 70386A0525D9718C0048EC1B /* Data.swift */, 706436A827341FD0007C6461 /* Date.swift */, + 706436A327341F6E007C6461 /* Encodable.swift */, + 9116F66E26A35D600082F6D6 /* URLCache.swift */, + F97B462C24D9C74400F4A88B /* URLSession.swift */, ); path = Extensions; sourceTree = ""; @@ -1464,8 +1464,8 @@ isa = PBXGroup; children = ( 707A3C1025B0A8E8000D215C /* ParseAnonymous.swift */, - 703B092F26BF42C2005A112F /* ParseAnonymous+combine.swift */, 703B093426BF43D9005A112F /* ParseAnonymous+async.swift */, + 703B092F26BF42C2005A112F /* ParseAnonymous+combine.swift */, ); path = Internal; sourceTree = ""; diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple+async.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple+async.swift index 38eda48f2..31ed42885 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple+async.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseApple/ParseApple+async.swift @@ -9,7 +9,7 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseApple { // MARK: Async/Await @@ -19,7 +19,7 @@ public extension ParseApple { - parameter identityToken: The `identityToken` from `ASAuthorizationAppleIDCredential`. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func login(user: String, identityToken: Data, @@ -37,7 +37,7 @@ public extension ParseApple { - parameter authData: Dictionary containing key/values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func login(authData: [String: String], options: API.Options = []) async throws -> AuthenticatedUser { @@ -49,7 +49,7 @@ public extension ParseApple { } } -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseApple { /** @@ -58,7 +58,7 @@ public extension ParseApple { - parameter identityToken: The `identityToken` from `ASAuthorizationAppleIDCredential`. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func link(user: String, identityToken: Data, @@ -76,7 +76,7 @@ public extension ParseApple { - parameter authData: Dictionary containing key/values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func link(authData: [String: String], options: API.Options = []) async throws -> AuthenticatedUser { diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook+async.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook+async.swift index cece2b44f..5c8b60ede 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook+async.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseFacebook/ParseFacebook+async.swift @@ -9,7 +9,7 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseFacebook { // MARK: Async/Await @@ -20,7 +20,7 @@ public extension ParseFacebook { - parameter expiresIn: Optional expiration in seconds for Facebook login. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func login(userId: String, authenticationToken: String, @@ -42,7 +42,7 @@ public extension ParseFacebook { - parameter expiresIn: Optional expiration in seconds for Facebook login. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func login(userId: String, accessToken: String, @@ -61,7 +61,7 @@ public extension ParseFacebook { Login a `ParseUser` *asynchronously* using Facebook authentication for graph API login. - parameter authData: Dictionary containing key/values. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func login(authData: [String: String], options: API.Options = []) async throws -> AuthenticatedUser { @@ -73,7 +73,7 @@ public extension ParseFacebook { } } -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseFacebook { /** Link the *current* `ParseUser` *asynchronously* using Facebook authentication for limited login. @@ -82,7 +82,7 @@ public extension ParseFacebook { - parameter expiresIn: Optional expiration in seconds for Facebook login. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func link(userId: String, authenticationToken: String, @@ -104,7 +104,7 @@ public extension ParseFacebook { - parameter expiresIn: Optional expiration in seconds for Facebook login. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func link(userId: String, accessToken: String, @@ -124,7 +124,7 @@ public extension ParseFacebook { - parameter authData: Dictionary containing key/values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func link(authData: [String: String], options: API.Options = []) async throws -> AuthenticatedUser { diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP+async.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP+async.swift index 565599cfa..ca92dd425 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP+async.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseLDAP/ParseLDAP+async.swift @@ -9,7 +9,7 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseLDAP { // MARK: Async/Await /** @@ -18,7 +18,7 @@ public extension ParseLDAP { - parameter password: The password of the user. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func login(id: String, password: String, @@ -36,7 +36,7 @@ public extension ParseLDAP { - parameter authData: Dictionary containing key/values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func login(authData: [String: String], options: API.Options = []) async throws -> AuthenticatedUser { @@ -48,7 +48,7 @@ public extension ParseLDAP { } } -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseLDAP { /** Link the *current* `ParseUser` *asynchronously* using LDAP authentication. @@ -56,7 +56,7 @@ public extension ParseLDAP { - parameter password: The password of the user. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func link(id: String, password: String, @@ -74,7 +74,7 @@ public extension ParseLDAP { - parameter authData: Dictionary containing key/values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func link(authData: [String: String], options: API.Options = []) async throws -> AuthenticatedUser { diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter+async.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter+async.swift index d38b9d225..3a1414d82 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter+async.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter+async.swift @@ -9,7 +9,7 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseTwitter { // MARK: Async/Await @@ -23,7 +23,7 @@ public extension ParseTwitter { - parameter authTokenSecret: The Twitter `authSecretToken` obtained from Twitter. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func login(userId: String, screenName: String? = nil, @@ -49,7 +49,7 @@ public extension ParseTwitter { - parameter authData: Dictionary containing key/values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func login(authData: [String: String], options: API.Options = []) async throws -> AuthenticatedUser { @@ -61,7 +61,7 @@ public extension ParseTwitter { } } -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseTwitter { /** @@ -74,7 +74,7 @@ public extension ParseTwitter { - parameter authTokenSecret: The Twitter `authSecretToken` obtained from Twitter. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func link(userId: String, screenName: String? = nil, @@ -100,7 +100,7 @@ public extension ParseTwitter { - parameter authData: Dictionary containing key/values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func link(authData: [String: String], options: API.Options = []) async throws -> AuthenticatedUser { diff --git a/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift b/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift index 8831ccae0..59165f22b 100644 --- a/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift +++ b/Sources/ParseSwift/Authentication/3rd Party/ParseTwitter/ParseTwitter.swift @@ -137,7 +137,7 @@ public extension ParseTwitter { public extension ParseTwitter { /** - Link the *current* `ParseUser` *asynchronously* using Twitter authentication. Publishes when complete. + Link the *current* `ParseUser` *asynchronously* using Twitter authentication. - parameter user: The `userId` from `Twitter`. - parameter screenName: The `user screenName` from `Twitter`. - parameter consumerKey: The `consumerKey` from `Twitter`. @@ -145,7 +145,7 @@ public extension ParseTwitter { - parameter authToken: The Twitter `authToken` obtained from Twitter. - parameter authTokenSecret: The Twitter `authSecretToken` obtained from Twitter. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. + - parameter completion: The block to execute. */ func link(userId: String, screenName: String? = nil, diff --git a/Sources/ParseSwift/Authentication/Internal/ParseAnonymous+async.swift b/Sources/ParseSwift/Authentication/Internal/ParseAnonymous+async.swift index 409341e4e..c32547e43 100644 --- a/Sources/ParseSwift/Authentication/Internal/ParseAnonymous+async.swift +++ b/Sources/ParseSwift/Authentication/Internal/ParseAnonymous+async.swift @@ -9,7 +9,7 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseAnonymous { // MARK: Async/Await @@ -17,7 +17,7 @@ public extension ParseAnonymous { Login a `ParseUser` *asynchronously* using the respective authentication type. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func login(options: API.Options = []) async throws -> AuthenticatedUser { try await withCheckedThrowingContinuation { continuation in @@ -31,7 +31,7 @@ public extension ParseAnonymous { - parameter authData: The authData for the respective authentication type. This will be ignored. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func login(authData: [String: String], options: API.Options = []) async throws -> AuthenticatedUser { @@ -43,7 +43,7 @@ public extension ParseAnonymous { } } -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseAnonymous { func link(authData: [String: String], diff --git a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication+async.swift b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication+async.swift index 166d82f9f..14e6f7cbf 100644 --- a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication+async.swift +++ b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication+async.swift @@ -9,7 +9,7 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseAuthentication { // MARK: Convenience Implementations - Async/Await @@ -30,14 +30,14 @@ public extension ParseAuthentication { } } -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseUser { // MARK: 3rd Party Authentication - Login Async/Await /** Makes an *asynchronous* request to log in a user with specified credentials. - Publishes an instance of the successfully logged in `ParseUser`. + Returns an instance of the successfully logged in `ParseUser`. This also caches the user locally so that calls to *current* will use the latest logged in user. - parameter type: The authentication type. @@ -73,7 +73,7 @@ public extension ParseUser { /** Makes an *asynchronous* request to link a user with specified credentials. The user should already be logged in. - Publishes an instance of the successfully linked `ParseUser`. + Returns an instance of the successfully linked `ParseUser`. This also caches the user locally so that calls to *current* will use the latest logged in user. - parameter type: The authentication type. diff --git a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift index 90113c555..cecc98bcc 100644 --- a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift +++ b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift @@ -145,7 +145,7 @@ public protocol ParseAuthentication: Codable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter returns: An instance of the linked `AuthenticatedUser`. */ - @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) + @MainActor func link(authData: [String: String], options: API.Options) async throws -> AuthenticatedUser @@ -155,7 +155,7 @@ public protocol ParseAuthentication: Codable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter returns: An instance of the unlinked `AuthenticatedUser`. */ - @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) + @MainActor func unlink(_ user: AuthenticatedUser, options: API.Options) async throws -> AuthenticatedUser @@ -165,7 +165,7 @@ public protocol ParseAuthentication: Codable { - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter returns: An instance of the unlinked `AuthenticatedUser`. */ - @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) + @MainActor func unlink(options: API.Options) async throws -> AuthenticatedUser #endif } @@ -435,7 +435,7 @@ public extension ParseUser { internal func linkCommand() throws -> API.Command { var mutableSelf = self.anonymous.strip(self) if let current = Self.current { - guard current.hasSameObjectId(as: self) else { + guard current.hasSameObjectId(as: mutableSelf) else { let error = ParseError(code: .unknownError, message: "Can't signup a user with a different objectId than the current user") throw error @@ -456,8 +456,9 @@ public extension ParseUser { } internal func linkCommand(body: SignupLoginBody) -> API.Command { - var body = body + let originalAuthData = Self.current?.authData Self.current?.anonymous.strip() + var body = body if var currentAuthData = Self.current?.authData { if let bodyAuthData = body.authData { bodyAuthData.forEach { (key, value) in @@ -470,6 +471,7 @@ public extension ParseUser { return API.Command(method: .PUT, path: endpoint, body: body) { (data) -> Self in + Self.current?.authData = originalAuthData let user = try ParseCoding.jsonDecoder().decode(UpdateSessionTokenResponse.self, from: data) Self.current?.updatedAt = user.updatedAt Self.current?.authData = body.authData @@ -478,7 +480,7 @@ public extension ParseUser { } if let sessionToken = user.sessionToken { Self.currentContainer = .init(currentUser: current, - sessionToken: sessionToken) + sessionToken: sessionToken) } Self.saveCurrentContainerToKeychain() return current diff --git a/Sources/ParseSwift/Extensions/URLSession.swift b/Sources/ParseSwift/Extensions/URLSession.swift index 952e13c83..37ed8d962 100755 --- a/Sources/ParseSwift/Extensions/URLSession.swift +++ b/Sources/ParseSwift/Extensions/URLSession.swift @@ -20,8 +20,8 @@ internal extension URLSession { configuration.requestCachePolicy = ParseSwift.configuration.requestCachePolicy configuration.httpAdditionalHeaders = ParseSwift.configuration.httpAdditionalHeaders return URLSession(configuration: configuration, - delegate: ParseSwift.sessionDelegate, - delegateQueue: nil) + delegate: ParseSwift.sessionDelegate, + delegateQueue: nil) } else { let session = URLSession.shared session.configuration.urlCache = URLCache.parse diff --git a/Sources/ParseSwift/LiveQuery/ParseLiveQuery+async.swift b/Sources/ParseSwift/LiveQuery/ParseLiveQuery+async.swift index 6af99557b..56c7f3681 100644 --- a/Sources/ParseSwift/LiveQuery/ParseLiveQuery+async.swift +++ b/Sources/ParseSwift/LiveQuery/ParseLiveQuery+async.swift @@ -9,15 +9,15 @@ #if swift(>=5.5) && canImport(_Concurrency) && !os(Linux) && !os(Android) && !os(Windows) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor extension ParseLiveQuery { // MARK: Async/Await /** - Manually establish a connection to the `ParseLiveQuery` Server. Publishes when established. + Manually establish a connection to the `ParseLiveQuery` Server. - parameter isUserWantsToConnect: Specifies if the user is calling this function. Defaults to `true`. - returns: An instance of the logged in `ParseUser`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ public func open(isUserWantsToConnect: Bool = true) async throws { let _: Void = try await withCheckedThrowingContinuation { continuation in @@ -32,9 +32,9 @@ extension ParseLiveQuery { } /** - Sends a ping frame from the client side. Publishes when a pong is received from the + Sends a ping frame from the client side. Returns when a pong is received from the server endpoint. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ public func sendPing() async throws { let _: Void = try await withCheckedThrowingContinuation { continuation in diff --git a/Sources/ParseSwift/Objects/ParseInstallation+async.swift b/Sources/ParseSwift/Objects/ParseInstallation+async.swift index 0b5fbcd19..796540311 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation+async.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation+async.swift @@ -9,19 +9,19 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseInstallation { // MARK: Async/Await /** Fetches the `ParseInstallation` *aynchronously* with the current data from the server - and sets an error if one occurs. Publishes when complete. + 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. 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 saved `ParseInstallation`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. - important: If an object fetched has the same objectId as current, it will automatically update the current. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. @@ -42,7 +42,7 @@ public extension ParseInstallation { `objectId` environments. Defaults to false. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns saved `ParseInstallation`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. - important: If an object saved has the same objectId as current, it will automatically update the current. */ func save(isIgnoreCustomObjectIdConfig: Bool = false, @@ -59,7 +59,7 @@ public extension ParseInstallation { - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns saved `ParseInstallation`. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. - important: If an object deleted has the same objectId as current, it will automatically update the current. */ func delete(options: API.Options = []) async throws { @@ -69,7 +69,7 @@ public extension ParseInstallation { } } -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension Sequence where Element: ParseInstallation { /** Fetches a collection of installations *aynchronously* with the current data from the server and sets @@ -78,8 +78,9 @@ public extension Sequence where Element: ParseInstallation { `ParseObject`s. Use `["*"]` to include all keys. 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 saved `ParseInstallation`. - - throws: `ParseError`. + - returns: Returns an array of Result enums with the object if a save was successful or a + `ParseError` if it failed. + - throws: An error of type `ParseError`.. - important: If an object fetched has the same objectId as current, it will automatically update the current. */ func fetchAll(includeKeys: [String]? = nil, @@ -102,12 +103,24 @@ public extension Sequence where Element: ParseInstallation { when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed `objectId` environments. Defaults to false. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: - returns: Returns saved `ParseInstallation`. - - throws: `ParseError`. + - returns: Returns an array of Result enums with the object if a save was successful or a + `ParseError` if it failed. + - throws: An error of type `ParseError`.. - important: If an object saved has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else the transactions can fail. + - warning: If you are using `ParseConfiguration.allowCustomObjectId = true` + and plan to generate all of your `objectId`'s on the client-side then you should leave + `isIgnoreCustomObjectIdConfig = false`. Setting + `ParseConfiguration.allowCustomObjectId = true` and + `isIgnoreCustomObjectIdConfig = true` means the client will generate `objectId`'s + and the server will generate an `objectId` only when the client does not provide one. This can + increase the probability of colliiding `objectId`'s as the client and server `objectId`'s may be generated using + different algorithms. This can also lead to overwriting of `ParseObject`'s by accident as the + client-side checks are disabled. Developers are responsible for handling such cases. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func saveAll(batchLimit limit: Int? = nil, transaction: Bool = ParseSwift.configuration.useTransactions, @@ -130,8 +143,8 @@ public extension Sequence where Element: ParseInstallation { - parameter transaction: Treat as an all-or-nothing operation. If some operation failure occurs that prevents the transaction from completing, then none of the objects are committed to the Parse Server database. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: Returns saved `ParseInstallation`. - - throws: `ParseError`. + - returns: Each element in the array is `nil` if the delete successful or a `ParseError` if it failed. + - throws: An error of type `ParseError`.. - important: If an object deleted has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else diff --git a/Sources/ParseSwift/Objects/ParseInstallation+combine.swift b/Sources/ParseSwift/Objects/ParseInstallation+combine.swift index 5919a4b84..f430b6bf6 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation+combine.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation+combine.swift @@ -72,7 +72,8 @@ public extension Sequence where Element: ParseInstallation { `ParseObject`s. Use `["*"]` to include all keys. 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 a single value and then finishes or fails. + - 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. - important: If an object fetched has the same objectId as current, it will automatically update the current. */ func fetchAllPublisher(includeKeys: [String]? = nil, @@ -92,11 +93,23 @@ public extension Sequence where Element: ParseInstallation { - parameter transaction: Treat as an all-or-nothing operation. If some operation failure occurs that prevents the transaction from completing, then none of the objects are committed to the Parse Server database. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. + - returns: A publisher that eventually produces an an array of Result enums with the object if a save was + successful or a `ParseError` if it failed. - important: If an object saved has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else the transactions can fail. + - warning: If you are using `ParseConfiguration.allowCustomObjectId = true` + and plan to generate all of your `objectId`'s on the client-side then you should leave + `isIgnoreCustomObjectIdConfig = false`. Setting + `ParseConfiguration.allowCustomObjectId = true` and + `isIgnoreCustomObjectIdConfig = true` means the client will generate `objectId`'s + and the server will generate an `objectId` only when the client does not provide one. This can + increase the probability of colliiding `objectId`'s as the client and server `objectId`'s may be generated using + different algorithms. This can also lead to overwriting of `ParseObject`'s by accident as the + client-side checks are disabled. Developers are responsible for handling such cases. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func saveAllPublisher(batchLimit limit: Int? = nil, transaction: Bool = ParseSwift.configuration.useTransactions, @@ -119,7 +132,8 @@ public extension Sequence where Element: ParseInstallation { - parameter transaction: Treat as an all-or-nothing operation. If some operation failure occurs that prevents the transaction from completing, then none of the objects are committed to the Parse Server database. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. + - returns: A publisher that eventually produces an an array of Result enums with `nil` if a delete was + successful or a `ParseError` if it failed. - important: If an object deleted has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else diff --git a/Sources/ParseSwift/Objects/ParseInstallation.swift b/Sources/ParseSwift/Objects/ParseInstallation.swift index a44c81bc4..dddebbeba 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation.swift @@ -393,23 +393,21 @@ extension ParseInstallation { try fetchCommand(include: includeKeys) .executeAsync(options: options, callbackQueue: callbackQueue) { result in - callbackQueue.async { - if case .success(let foundResult) = result { - do { - try Self.updateKeychainIfNeeded([foundResult]) - completion(.success(foundResult)) - } catch { - let returnError: ParseError! - if let parseError = error as? ParseError { - returnError = parseError - } else { - returnError = ParseError(code: .unknownError, message: error.localizedDescription) - } - completion(.failure(returnError)) + if case .success(let foundResult) = result { + do { + try Self.updateKeychainIfNeeded([foundResult]) + completion(.success(foundResult)) + } catch { + let returnError: ParseError! + if let parseError = error as? ParseError { + returnError = parseError + } else { + returnError = ParseError(code: .unknownError, message: error.localizedDescription) } - } else { - completion(result) + completion(.failure(returnError)) } + } else { + completion(result) } } } catch { @@ -548,24 +546,22 @@ extension ParseInstallation { callbackQueue: callbackQueue, childObjects: savedChildObjects, childFiles: savedChildFiles) { result in - callbackQueue.async { - if case .success(let foundResults) = result { - do { - try Self.updateKeychainIfNeeded([foundResults]) - completion(.success(foundResults)) - } catch { - let returnError: ParseError! - if let parseError = error as? ParseError { - returnError = parseError - } else { - returnError = ParseError(code: .unknownError, - message: error.localizedDescription) - } - completion(.failure(returnError)) + if case .success(let foundResults) = result { + do { + try Self.updateKeychainIfNeeded([foundResults]) + completion(.success(foundResults)) + } catch { + let returnError: ParseError! + if let parseError = error as? ParseError { + returnError = parseError + } else { + returnError = ParseError(code: .unknownError, + message: error.localizedDescription) } - } else { - completion(result) + completion(.failure(returnError)) } + } else { + completion(result) } } } catch { @@ -729,7 +725,7 @@ public extension Sequence where Element: ParseInstallation { prevents the transaction from completing, then none of the objects are committed to the Parse Server database. - returns: Returns a Result enum with the object if a save was successful or a `ParseError` if it failed. - - throws: `ParseError` + - throws: An error of type `ParseError`. - important: If an object saved has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else @@ -931,10 +927,8 @@ public extension Sequence where Element: ParseInstallation { case .success(let saved): returnBatch.append(contentsOf: saved) if completed == (batches.count - 1) { - callbackQueue.async { - try? Self.Element.updateKeychainIfNeeded(returnBatch.compactMap {try? $0.get()}) - completion(.success(returnBatch)) - } + try? Self.Element.updateKeychainIfNeeded(returnBatch.compactMap {try? $0.get()}) + completion(.success(returnBatch)) } completed += 1 case .failure(let error): @@ -965,7 +959,7 @@ public extension Sequence where Element: ParseInstallation { - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns a Result enum with the object if a fetch was successful or a `ParseError` if it failed. - - throws: `ParseError` + - throws: An error of type `ParseError`. - important: If an object fetched has the same objectId as current, it will automatically update the current. - warning: The order in which installations are returned are not guarenteed. You shouldn't expect results in any particular order. @@ -1043,10 +1037,8 @@ public extension Sequence where Element: ParseInstallation { message: "objectId \"\(uniqueObjectId)\" was not found in className \"\(Self.Element.className)\""))) } } - callbackQueue.async { - try? Self.Element.updateKeychainIfNeeded(fetchedObjects) - completion(.success(fetchedObjectsToReturn)) - } + try? Self.Element.updateKeychainIfNeeded(fetchedObjects) + completion(.success(fetchedObjectsToReturn)) case .failure(let error): callbackQueue.async { completion(.failure(error)) @@ -1078,7 +1070,7 @@ public extension Sequence where Element: ParseInstallation { 2. A non-aggregate Parse.Error. This indicates a serious error that caused the delete operation to be aborted partway through (for instance, a connection failure in the middle of the delete). - - throws: `ParseError` + - throws: An error of type `ParseError`. - important: If an object deleted has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else diff --git a/Sources/ParseSwift/Objects/ParseObject+async.swift b/Sources/ParseSwift/Objects/ParseObject+async.swift index 0f73d525f..ee707a899 100644 --- a/Sources/ParseSwift/Objects/ParseObject+async.swift +++ b/Sources/ParseSwift/Objects/ParseObject+async.swift @@ -9,7 +9,7 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseObject { // MARK: Async/Await @@ -19,8 +19,8 @@ public extension ParseObject { `ParseObject`s. Use `["*"]` to include all keys. 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 a single value and then finishes or fails. - - throws: `ParseError`. + - returns: Returns the fetched `ParseObject`. + - throws: An error of type `ParseError`.. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. */ @@ -39,8 +39,8 @@ public extension ParseObject { when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed `objectId` environments. Defaults to false. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: Returns the saved `ParseObject`. + - throws: An error of type `ParseError`.. - important: If an object saved has the same objectId as current, it will automatically update the current. */ func save(isIgnoreCustomObjectIdConfig: Bool = false, @@ -56,8 +56,7 @@ public extension ParseObject { Deletes the `ParseObject` *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. - important: If an object deleted has the same objectId as current, it will automatically update the current. */ func delete(options: API.Options = []) async throws { @@ -68,7 +67,7 @@ public extension ParseObject { } } -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension Sequence where Element: ParseObject { /** Fetches a collection of objects *aynchronously* with the current data from the server and sets @@ -77,8 +76,9 @@ public extension Sequence where Element: ParseObject { `ParseObject`s. Use `["*"]` to include all keys. 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 a single value and then finishes or fails. - - throws: `ParseError`. + - 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`.. - important: If an object fetched has the same objectId as current, it will automatically update the current. */ func fetchAll(includeKeys: [String]? = nil, @@ -101,12 +101,23 @@ public extension Sequence where Element: ParseObject { when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed `objectId` environments. Defaults to false. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: Returns saved `ParseInstallation`. - - throws: `ParseError`. - - important: If an object saved has the same objectId as current, it will automatically update the current. + - returns: Returns an array of Result enums with the object if a save was successful or a + `ParseError` if it failed. + - throws: An error of type `ParseError`.. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else the transactions can fail. + - warning: If you are using `ParseConfiguration.allowCustomObjectId = true` + and plan to generate all of your `objectId`'s on the client-side then you should leave + `isIgnoreCustomObjectIdConfig = false`. Setting + `ParseConfiguration.allowCustomObjectId = true` and + `isIgnoreCustomObjectIdConfig = true` means the client will generate `objectId`'s + and the server will generate an `objectId` only when the client does not provide one. This can + increase the probability of colliiding `objectId`'s as the client and server `objectId`'s may be generated using + different algorithms. This can also lead to overwriting of `ParseObject`'s by accident as the + client-side checks are disabled. Developers are responsible for handling such cases. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func saveAll(batchLimit limit: Int? = nil, transaction: Bool = ParseSwift.configuration.useTransactions, @@ -129,8 +140,8 @@ public extension Sequence where Element: ParseObject { - parameter transaction: Treat as an all-or-nothing operation. If some operation failure occurs that prevents the transaction from completing, then none of the objects are committed to the Parse Server database. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: - returns: Returns saved `ParseInstallation`. - - throws: `ParseError`. + - returns: Returns `nil` if the delete successful or a `ParseError` if it failed. + - throws: An error of type `ParseError`.. - important: If an object deleted has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else diff --git a/Sources/ParseSwift/Objects/ParseObject+combine.swift b/Sources/ParseSwift/Objects/ParseObject+combine.swift index 3f66b7e7a..22bb6921e 100644 --- a/Sources/ParseSwift/Objects/ParseObject+combine.swift +++ b/Sources/ParseSwift/Objects/ParseObject+combine.swift @@ -71,7 +71,8 @@ public extension Sequence where Element: ParseObject { `ParseObject`s. Use `["*"]` to include all keys. 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 a single value and then finishes or fails. + - 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. - important: If an object fetched has the same objectId as current, it will automatically update the current. */ func fetchAllPublisher(includeKeys: [String]? = nil, @@ -94,8 +95,8 @@ public extension Sequence where Element: ParseObject { when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed `objectId` environments. Defaults to false. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - important: If an object saved has the same objectId as current, it will automatically update the current. + - returns: A publisher that eventually produces an an array of Result enums with the object if a save was + successful or a `ParseError` if it failed. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else the transactions can fail. @@ -130,7 +131,8 @@ public extension Sequence where Element: ParseObject { - parameter transaction: Treat as an all-or-nothing operation. If some operation failure occurs that prevents the transaction from completing, then none of the objects are committed to the Parse Server database. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. + - returns: A publisher that eventually produces an an array of Result enums with `nil` if a delete was + successful or a `ParseError` if it failed. - important: If an object deleted has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index e647c4a1a..70d1e25fe 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -110,9 +110,9 @@ transactions for this call. when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed `objectId` environments. Defaults to false. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - - returns: Returns a Result enum with the object if a save was successful or a `ParseError` if it failed. - - throws: `ParseError` + - returns: Returns an array of Result enums with the object if a save was successful or a + `ParseError` if it failed. + - throws: An error of type `ParseError`. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else the transactions can fail. @@ -311,15 +311,11 @@ transactions for this call. case .success(let saved): returnBatch.append(contentsOf: saved) if completed == (batches.count - 1) { - callbackQueue.async { - completion(.success(returnBatch)) - } + completion(.success(returnBatch)) } completed += 1 case .failure(let error): - callbackQueue.async { - completion(.failure(error)) - } + completion(.failure(error)) return } } @@ -342,9 +338,9 @@ transactions for this call. `ParseObject`s. Use `["*"]` to include all keys. 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 a Result enum with the object if a fetch was successful or a `ParseError` if it failed. - - throws: `ParseError` + - 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`. - warning: The order in which objects are returned are not guarenteed. You shouldn't expect results in any particular order. */ @@ -417,9 +413,7 @@ transactions for this call. message: "objectId \"\(uniqueObjectId)\" was not found in className \"\(Self.Element.className)\""))) } } - callbackQueue.async { - completion(.success(fetchedObjectsToReturn)) - } + completion(.success(fetchedObjectsToReturn)) case .failure(let error): callbackQueue.async { completion(.failure(error)) @@ -451,7 +445,7 @@ transactions for this call. 2. A non-aggregate Parse.Error. This indicates a serious error that caused the delete operation to be aborted partway through (for instance, a connection failure in the middle of the delete). - - throws: `ParseError` + - throws: An error of type `ParseError`. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else the transactions can fail. @@ -580,6 +574,7 @@ extension ParseObject { `includeAll` for `Query`. - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of `ParseError` type. + - returns: Returns the fetched `ParseObject`. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. */ @@ -614,9 +609,7 @@ extension ParseObject { try fetchCommand(include: includeKeys) .executeAsync(options: options, callbackQueue: callbackQueue) { result in - callbackQueue.async { completion(result) - } } } catch { callbackQueue.async { @@ -732,9 +725,7 @@ extension ParseObject { callbackQueue: callbackQueue, childObjects: savedChildObjects, childFiles: savedChildFiles) { result in - callbackQueue.async { completion(result) - } } } catch { callbackQueue.async { diff --git a/Sources/ParseSwift/Objects/ParseRole.swift b/Sources/ParseSwift/Objects/ParseRole.swift index 0d79601ca..876cec669 100644 --- a/Sources/ParseSwift/Objects/ParseRole.swift +++ b/Sources/ParseSwift/Objects/ParseRole.swift @@ -44,7 +44,7 @@ public extension ParseRole { /** Create a `ParseRole` with a name. The `ParseACL` will still need to be initialized before saving. - parameter name: The name of the Role to create. - - throws: `ParseError` if the name has invalid characters. + - throws: An error of type `ParseError`. if the name has invalid characters. */ init(name: String) throws { try Self.checkName(name) @@ -56,7 +56,7 @@ public extension ParseRole { - parameter name: The name of the Role to create. - parameter acl: The `ParseACL` for this role. Roles must have an ACL. A `ParseRole` is a local representation of a role persisted to the Parse Server. - - throws: `ParseError` if the name has invalid characters. + - throws: An error of type `ParseError`. if the name has invalid characters. */ init(name: String, acl: ParseACL) throws { try Self.checkName(name) diff --git a/Sources/ParseSwift/Objects/ParseUser+async.swift b/Sources/ParseSwift/Objects/ParseUser+async.swift index 411d1a536..241c36f0a 100644 --- a/Sources/ParseSwift/Objects/ParseUser+async.swift +++ b/Sources/ParseSwift/Objects/ParseUser+async.swift @@ -9,12 +9,12 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseUser { // MARK: Async/Await /** - Signs up the user *asynchronously* and publishes value. + Signs up the user *asynchronously*. This will also enforce that the username isn't already taken. @@ -22,10 +22,9 @@ public extension ParseUser { - parameter username: The username of the user. - parameter password: The password of the user. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: Returns the signed in `ParseUser`. + - throws: An error of type `ParseError`.. */ - @MainActor static func signup(username: String, password: String, options: API.Options = []) async throws -> Self { @@ -38,16 +37,15 @@ public extension ParseUser { } /** - Signs up the user *asynchronously* and publishes value. + Signs up the user *asynchronously*. This will also enforce that the username isn't already taken. - warning: Make sure that password and username are set before calling this method. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: Returns the signed in `ParseUser`. + - throws: An error of type `ParseError`.. */ - @MainActor func signup(options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in self.signup(options: options, @@ -57,16 +55,15 @@ public extension ParseUser { /** Makes an *asynchronous* request to log in a user with specified credentials. - Publishes an instance of the successfully logged in `ParseUser`. + Returns an instance of the successfully logged in `ParseUser`. This also caches the user locally so that calls to *current* will use the latest logged in user. - parameter username: The username of the user. - parameter password: The password of the user. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: Returns the logged in `ParseUser`. + - throws: An error of type `ParseError`.. */ - @MainActor static func login(username: String, password: String, options: API.Options = []) async throws -> Self { @@ -80,16 +77,15 @@ public extension ParseUser { /** Logs in a `ParseUser` *asynchronously* with a session token. - Publishes an instance of the successfully logged in `ParseUser`. + Returns an instance of the successfully logged in `ParseUser`. If successful, this saves the session to the keychain, so you can retrieve the currently logged in user using *current*. - parameter sessionToken: The sessionToken of the user to login. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: Returns the logged in `ParseUser`. + - throws: An error of type `ParseError`.. */ - @MainActor func become(sessionToken: String, options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in @@ -103,10 +99,8 @@ public extension ParseUser { This will also remove the session from the Keychain, log out of linked services and all future calls to `current` will return `nil`. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ - @MainActor static func logout(options: API.Options = []) async throws { _ = try await withCheckedThrowingContinuation { continuation in Self.logout(options: options, completion: continuation.resume) @@ -118,10 +112,8 @@ public extension ParseUser { associated with the user account. This email allows the user to securely reset their password on the web. - parameter email: The email address associated with the user that forgot their password. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ - @MainActor static func passwordReset(email: String, options: API.Options = []) async throws { _ = try await withCheckedThrowingContinuation { continuation in @@ -134,10 +126,8 @@ public extension ParseUser { associated with the user account. - parameter email: The email address associated with the user. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ - @MainActor static func verificationEmail(email: String, options: API.Options = []) async throws { _ = try await withCheckedThrowingContinuation { continuation in @@ -151,13 +141,12 @@ public extension ParseUser { `ParseObject`s. Use `["*"]` to include all keys. 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 a single value and then finishes or fails. - - throws: `ParseError`. + - returns: Returns the fetched `ParseUser`. + - throws: An error of type `ParseError`.. - important: If an object fetched has the same objectId as current, it will automatically update the current. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. */ - @MainActor func fetch(includeKeys: [String]? = nil, options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in @@ -173,11 +162,10 @@ public extension ParseUser { when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed `objectId` environments. Defaults to false. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: Returns the saved `ParseUser`. + - throws: An error of type `ParseError`.. - important: If an object saved has the same objectId as current, it will automatically update the current. */ - @MainActor func save(isIgnoreCustomObjectIdConfig: Bool = false, options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in @@ -191,11 +179,9 @@ public extension ParseUser { Deletes the `ParseUser` *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. - important: If an object deleted has the same objectId as current, it will automatically update the current. */ - @MainActor func delete(options: API.Options = []) async throws { _ = try await withCheckedThrowingContinuation { continuation in self.delete(options: options, completion: continuation.resume) @@ -203,7 +189,7 @@ public extension ParseUser { } } -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension Sequence where Element: ParseUser { /** Fetches a collection of users *aynchronously* with the current data from the server and sets @@ -212,11 +198,11 @@ public extension Sequence where Element: ParseUser { `ParseObject`s. Use `["*"]` to include all keys. 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 a single value and then finishes or fails. - - throws: `ParseError`. + - 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`.. - important: If an object fetched has the same objectId as current, it will automatically update the current. */ - @MainActor func fetchAll(includeKeys: [String]? = nil, options: API.Options = []) async throws -> [(Result)] { try await withCheckedThrowingContinuation { continuation in @@ -237,14 +223,25 @@ public extension Sequence where Element: ParseUser { when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed `objectId` environments. Defaults to false. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: Returns an array of Result enums with the object if a save was successful or a + `ParseError` if it failed. + - throws: An error of type `ParseError`.. - important: If an object saved has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else the transactions can fail. + - warning: If you are using `ParseConfiguration.allowCustomObjectId = true` + and plan to generate all of your `objectId`'s on the client-side then you should leave + `isIgnoreCustomObjectIdConfig = false`. Setting + `ParseConfiguration.allowCustomObjectId = true` and + `isIgnoreCustomObjectIdConfig = true` means the client will generate `objectId`'s + and the server will generate an `objectId` only when the client does not provide one. This can + increase the probability of colliiding `objectId`'s as the client and server `objectId`'s may be generated using + different algorithms. This can also lead to overwriting of `ParseObject`'s by accident as the + client-side checks are disabled. Developers are responsible for handling such cases. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ - @MainActor func saveAll(batchLimit limit: Int? = nil, transaction: Bool = ParseSwift.configuration.useTransactions, isIgnoreCustomObjectIdConfig: Bool = false, @@ -259,21 +256,20 @@ public extension Sequence where Element: ParseUser { } /** - Deletes a collection of users *asynchronously* and publishes when complete. + Deletes a collection of users *asynchronously*. - parameter batchLimit: The maximum number of objects to send in each batch. If the items to be batched. is greater than the `batchLimit`, the objects will be sent to the server in waves up to the `batchLimit`. Defaults to 50. - parameter transaction: Treat as an all-or-nothing operation. If some operation failure occurs that prevents the transaction from completing, then none of the objects are committed to the Parse Server database. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: Each element in the array is `nil` if the delete successful or a `ParseError` if it failed. + - throws: An error of type `ParseError`.. - important: If an object deleted has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else the transactions can fail. */ - @MainActor func deleteAll(batchLimit limit: Int? = nil, transaction: Bool = ParseSwift.configuration.useTransactions, options: API.Options = []) async throws -> [(Result)] { diff --git a/Sources/ParseSwift/Objects/ParseUser+combine.swift b/Sources/ParseSwift/Objects/ParseUser+combine.swift index 09768c61c..a052a4eb9 100644 --- a/Sources/ParseSwift/Objects/ParseUser+combine.swift +++ b/Sources/ParseSwift/Objects/ParseUser+combine.swift @@ -202,7 +202,8 @@ public extension Sequence where Element: ParseUser { `ParseObject`s. Use `["*"]` to include all keys. 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 a single value and then finishes or fails. + - 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. - important: If an object fetched has the same objectId as current, it will automatically update the current. */ func fetchAllPublisher(includeKeys: [String]? = nil, @@ -225,7 +226,8 @@ public extension Sequence where Element: ParseUser { when `ParseConfiguration.allowCustomObjectId = true` to allow for mixed `objectId` environments. Defaults to false. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. + - returns: A publisher that eventually produces an an array of Result enums with the object if a save was + successful or a `ParseError` if it failed. - important: If an object saved has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else @@ -261,7 +263,8 @@ public extension Sequence where Element: ParseUser { - parameter transaction: Treat as an all-or-nothing operation. If some operation failure occurs that prevents the transaction from completing, then none of the objects are committed to the Parse Server database. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. + - returns: A publisher that eventually produces an an array of Result enums with `nil` if a delete was + successful or a `ParseError` if it failed. - important: If an object deleted has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else diff --git a/Sources/ParseSwift/Objects/ParseUser.swift b/Sources/ParseSwift/Objects/ParseUser.swift index 538a1a4e7..c78a915ce 100644 --- a/Sources/ParseSwift/Objects/ParseUser.swift +++ b/Sources/ParseSwift/Objects/ParseUser.swift @@ -272,29 +272,25 @@ extension ParseUser { var options = options options.insert(.sessionToken(sessionToken)) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) - do { + do { try newUser.meCommand(sessionToken: sessionToken) .executeAsync(options: options, callbackQueue: callbackQueue) { result in if case .success(let foundResult) = result { - callbackQueue.async { - completion(.success(foundResult)) - } + completion(.success(foundResult)) } else { - callbackQueue.async { - completion(result) - } + completion(result) } } - } catch let error as ParseError { + } catch let error as ParseError { callbackQueue.async { completion(.failure(error)) } - } catch { + } catch { callbackQueue.async { completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) } - } + } } internal func meCommand(sessionToken: String) throws -> API.Command { @@ -605,9 +601,7 @@ extension ParseUser { try signupCommand() .executeAsync(options: options, callbackQueue: callbackQueue) { result in - callbackQueue.async { completion(result) - } } } catch { callbackQueue.async { @@ -650,19 +644,15 @@ extension ParseUser { if let current = Self.current { current.linkCommand(body: body) .executeAsync(options: options, - callbackQueue: .main) { result in - callbackQueue.async { - completion(result) - } + callbackQueue: callbackQueue) { result in + completion(result) } } else { do { try signupCommand(body: body) .executeAsync(options: options, - callbackQueue: .main) { result in - callbackQueue.async { + callbackQueue: callbackQueue) { result in completion(result) - } } } catch { callbackQueue.async { @@ -794,7 +784,6 @@ extension ParseUser { try fetchCommand(include: includeKeys) .executeAsync(options: options, callbackQueue: callbackQueue) { result in - callbackQueue.async { if case .success(let foundResult) = result { do { try Self.updateKeychainIfNeeded([foundResult]) @@ -812,7 +801,6 @@ extension ParseUser { completion(result) } } - } } catch { callbackQueue.async { if let error = error as? ParseError { @@ -948,12 +936,10 @@ extension ParseUser { callbackQueue: callbackQueue, childObjects: savedChildObjects, childFiles: savedChildFiles) { result in - callbackQueue.async { - if case .success(let foundResults) = result { - try? Self.updateKeychainIfNeeded([foundResults]) - } - completion(result) + if case .success(let foundResults) = result { + try? Self.updateKeychainIfNeeded([foundResults]) } + completion(result) } } catch { callbackQueue.async { @@ -1063,14 +1049,12 @@ extension ParseUser { options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) do { try deleteCommand().executeAsync(options: options, - callbackQueue: .main) { result in + callbackQueue: callbackQueue) { result in switch result { case .success: - callbackQueue.async { - try? Self.updateKeychainIfNeeded([self], deleting: true) - completion(.success(())) - } + try? Self.updateKeychainIfNeeded([self], deleting: true) + completion(.success(())) case .failure(let error): callbackQueue.async { completion(.failure(error)) @@ -1123,7 +1107,7 @@ public extension Sequence where Element: ParseUser { - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns a Result enum with the object if a save was successful or a `ParseError` if it failed. - - throws: `ParseError` + - throws: An error of type `ParseError`. - important: If an object saved has the same objectId as current, it will automatically update the current. - warning: If `transaction = true`, then `batchLimit` will be automatically be set to the amount of the objects in the transaction. The developer should ensure their respective Parse Servers can handle the limit or else @@ -1325,16 +1309,12 @@ public extension Sequence where Element: ParseUser { case .success(let saved): returnBatch.append(contentsOf: saved) if completed == (batches.count - 1) { - callbackQueue.async { - try? Self.Element.updateKeychainIfNeeded(returnBatch.compactMap {try? $0.get()}) - completion(.success(returnBatch)) - } + try? Self.Element.updateKeychainIfNeeded(returnBatch.compactMap {try? $0.get()}) + completion(.success(returnBatch)) } completed += 1 case .failure(let error): - callbackQueue.async { - completion(.failure(error)) - } + completion(.failure(error)) return } } @@ -1435,10 +1415,8 @@ public extension Sequence where Element: ParseUser { message: "objectId \"\(uniqueObjectId)\" was not found in className \"\(Self.Element.className)\""))) } } - callbackQueue.async { - try? Self.Element.updateKeychainIfNeeded(fetchedObjects) - completion(.success(fetchedObjectsToReturn)) - } + try? Self.Element.updateKeychainIfNeeded(fetchedObjects) + completion(.success(fetchedObjectsToReturn)) case .failure(let error): callbackQueue.async { completion(.failure(error)) diff --git a/Sources/ParseSwift/Parse.h b/Sources/ParseSwift/Parse.h index 2c6a817fb..f10f1ada2 100644 --- a/Sources/ParseSwift/Parse.h +++ b/Sources/ParseSwift/Parse.h @@ -15,5 +15,3 @@ FOUNDATION_EXPORT double ParseVersionNumber; FOUNDATION_EXPORT const unsigned char ParseVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index e72fc6aa8..fbaedb07d 100644 --- a/Sources/ParseSwift/ParseConstants.swift +++ b/Sources/ParseSwift/ParseConstants.swift @@ -10,7 +10,7 @@ import Foundation enum ParseConstants { static let sdk = "swift" - static let version = "2.3.1" + static let version = "2.4.0" static let fileManagementDirectory = "parse/" static let fileManagementPrivateDocumentsDirectory = "Private Documents/" static let fileManagementLibraryDirectory = "Library/" diff --git a/Sources/ParseSwift/Types/ParseAnalytics+async.swift b/Sources/ParseSwift/Types/ParseAnalytics+async.swift index 2e00c21e5..4d2bf8173 100644 --- a/Sources/ParseSwift/Types/ParseAnalytics+async.swift +++ b/Sources/ParseSwift/Types/ParseAnalytics+async.swift @@ -13,7 +13,7 @@ import Foundation import UIKit #endif -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseAnalytics { // MARK: Aysnc/Await @@ -22,7 +22,7 @@ public extension ParseAnalytics { /** Tracks *asynchronously* this application being launched. If this happened as the result of the user opening a push notification, this method sends along information to - correlate this open with that push. Publishes when complete. + correlate this open with that push. - parameter launchOptions: The dictionary indicating the reason the application was launched, if any. This value can be found as a parameter to various @@ -30,8 +30,7 @@ public extension ParseAnalytics { - parameter at: Explicitly set the time associated with a given event. If not provided the server time will be used. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ static func trackAppOpened(launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil, at date: Date? = nil, @@ -48,15 +47,14 @@ public extension ParseAnalytics { /** Tracks *asynchronously* this application being launched. If this happened as the result of the user opening a push notification, this method sends along information to - correlate this open with that push. Publishes when complete. + correlate this open with that push. - parameter dimensions: The dictionary of information by which to segment this event and can be empty or `nil`. - parameter at: Explicitly set the time associated with a given event. If not provided the server time will be used. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ static func trackAppOpened(dimensions: [String: String]? = nil, at date: Date? = nil, @@ -70,11 +68,10 @@ public extension ParseAnalytics { } /** - Tracks *asynchronously* the occurrence of a custom event. Publishes when complete. + Tracks *asynchronously* the occurrence of a custom event. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func track(options: API.Options = []) async throws { _ = try await withCheckedThrowingContinuation { continuation in @@ -85,17 +82,15 @@ public extension ParseAnalytics { /** Tracks *asynchronously* the occurrence of a custom event with additional dimensions. - Publishes when complete. - parameter dimensions: The dictionary of information by which to segment this event and can be empty or `nil`. - parameter at: Explicitly set the time associated with a given event. If not provided the server time will be used. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - warning: This method makes a copy of the current `ParseAnalytics` and then mutates it. You will not have access to the mutated analytic after calling this method. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func track(dimensions: [String: String]?, at date: Date? = nil, diff --git a/Sources/ParseSwift/Types/ParseCloud+async.swift b/Sources/ParseSwift/Types/ParseCloud+async.swift index e72f116ac..11b1a273a 100644 --- a/Sources/ParseSwift/Types/ParseCloud+async.swift +++ b/Sources/ParseSwift/Types/ParseCloud+async.swift @@ -9,7 +9,7 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseCloud { // MARK: Aysnc/Await @@ -18,7 +18,7 @@ public extension ParseCloud { Calls a Cloud Code function *asynchronously* and returns a result of it's execution. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: The return type. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func runFunction(options: API.Options = []) async throws -> ReturnType { try await withCheckedThrowingContinuation { continuation in @@ -30,8 +30,8 @@ public extension ParseCloud { /** Starts a Cloud Code Job *asynchronously* and returns a result with the jobStatusId of the job. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: The return type. + - throws: An error of type `ParseError`.. */ func startJob(options: API.Options = []) async throws -> ReturnType { try await withCheckedThrowingContinuation { continuation in diff --git a/Sources/ParseSwift/Types/ParseCloud.swift b/Sources/ParseSwift/Types/ParseCloud.swift index a7ce88947..1c9d6da63 100644 --- a/Sources/ParseSwift/Types/ParseCloud.swift +++ b/Sources/ParseSwift/Types/ParseCloud.swift @@ -52,9 +52,7 @@ extension ParseCloud { completion: @escaping (Result) -> Void) { runFunctionCommand() .executeAsync(options: options, callbackQueue: callbackQueue) { result in - callbackQueue.async { - completion(result) - } + completion(result) } } @@ -92,9 +90,7 @@ extension ParseCloud { completion: @escaping (Result) -> Void) { startJobCommand() .executeAsync(options: options, callbackQueue: callbackQueue) { result in - callbackQueue.async { - completion(result) - } + completion(result) } } diff --git a/Sources/ParseSwift/Types/ParseConfig+async.swift b/Sources/ParseSwift/Types/ParseConfig+async.swift index 342bb3e5b..ef1ffaeb4 100644 --- a/Sources/ParseSwift/Types/ParseConfig+async.swift +++ b/Sources/ParseSwift/Types/ParseConfig+async.swift @@ -9,7 +9,7 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseConfig { // MARK: Fetchable - Async/Await @@ -18,7 +18,7 @@ public extension ParseConfig { Fetch the Config *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: The return type of self. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. */ @@ -34,8 +34,8 @@ public extension ParseConfig { /** Update the Config *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: `true` if saved, `false` if save is unsuccessful. + - throws: An error of type `ParseError`.. */ func save(options: API.Options = []) async throws -> Bool { try await withCheckedThrowingContinuation { continuation in diff --git a/Sources/ParseSwift/Types/ParseConfig.swift b/Sources/ParseSwift/Types/ParseConfig.swift index 187caa1d5..75494902f 100644 --- a/Sources/ParseSwift/Types/ParseConfig.swift +++ b/Sources/ParseSwift/Types/ParseConfig.swift @@ -51,9 +51,7 @@ extension ParseConfig { options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) fetchCommand() .executeAsync(options: options, callbackQueue: callbackQueue) { result in - callbackQueue.async { - completion(result) - } + completion(result) } } @@ -101,9 +99,7 @@ extension ParseConfig { options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) updateCommand() .executeAsync(options: options, callbackQueue: callbackQueue) { result in - callbackQueue.async { - completion(result) - } + completion(result) } } diff --git a/Sources/ParseSwift/Types/ParseFile+async.swift b/Sources/ParseSwift/Types/ParseFile+async.swift index d44073ef9..b46343fa1 100644 --- a/Sources/ParseSwift/Types/ParseFile+async.swift +++ b/Sources/ParseSwift/Types/ParseFile+async.swift @@ -12,15 +12,15 @@ import Foundation import FoundationNetworking #endif -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseFile { // MARK: Async/Await /** Fetches a file with given url *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: A fetched `ParseFile`. + - throws: An error of type `ParseError`.. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. */ @@ -37,8 +37,8 @@ public extension ParseFile { - parameter progress: A block that will be called when file updates it's progress. It should have the following argument signature: `(task: URLSessionDownloadTask, bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)`. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: A fetched `ParseFile`. + - throws: An error of type `ParseError`.. - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer desires a different policy, it should be inserted in `options`. */ @@ -57,8 +57,8 @@ public extension ParseFile { A name will be assigned to it by the server. If the file hasn't been downloaded, it will automatically be downloaded before saved. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: A saved `ParseFile`. + - throws: An error of type `ParseError`.. */ func save(options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in @@ -76,7 +76,7 @@ public extension ParseFile { It should have the following argument signature: `(task: URLSessionDownloadTask, bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)`. - returns: A ParsFile. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func save(options: API.Options = [], progress: ((URLSessionTask, @@ -91,10 +91,10 @@ public extension ParseFile { } /** - Deletes the file from the Parse Server. Publishes when complete. + Deletes the file from the Parse Server. - requires: `.useMasterKey` has to be available. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func delete(options: API.Options = []) async throws { _ = try await withCheckedThrowingContinuation { continuation in diff --git a/Sources/ParseSwift/Types/ParseFile.swift b/Sources/ParseSwift/Types/ParseFile.swift index 552cfe7e3..a3d76abb6 100644 --- a/Sources/ParseSwift/Types/ParseFile.swift +++ b/Sources/ParseSwift/Types/ParseFile.swift @@ -204,14 +204,11 @@ extension ParseFile { options = options.union(self.options) deleteFileCommand().executeAsync(options: options, callbackQueue: callbackQueue) { result in - callbackQueue.async { - switch result { - - case .success: - completion(.success(())) - case .failure(let error): - completion(.failure(error)) - } + switch result { + case .success: + completion(.success(())) + case .failure(let error): + completion(.failure(error)) } } } @@ -458,9 +455,7 @@ extension ParseFile { .executeAsync(options: options, callbackQueue: callbackQueue, uploadProgress: progress) { result in - callbackQueue.async { - completion(result) - } + completion(result) } } catch { callbackQueue.async { @@ -484,9 +479,7 @@ extension ParseFile { .executeAsync(options: options, callbackQueue: callbackQueue, uploadProgress: progress) { result in - callbackQueue.async { - completion(result) - } + completion(result) } } catch { callbackQueue.async { @@ -704,9 +697,7 @@ extension ParseFile { .executeAsync(options: options, callbackQueue: callbackQueue, downloadProgress: progress) { result in - callbackQueue.async { completion(result) - } } } diff --git a/Sources/ParseSwift/Types/ParseGeoPoint.swift b/Sources/ParseSwift/Types/ParseGeoPoint.swift index bfb89f076..268b20907 100644 --- a/Sources/ParseSwift/Types/ParseGeoPoint.swift +++ b/Sources/ParseSwift/Types/ParseGeoPoint.swift @@ -40,7 +40,7 @@ public struct ParseGeoPoint: Codable, Hashable { Create a new `ParseGeoPoint` instance with the specified latitude and longitude. - parameter latitude: Latitude of point in degrees. - parameter longitude: Longitude of point in degrees. - - throws: An error of `ParseError` type. + - throws: An error of type `ParseError`. */ public init(latitude: Double, longitude: Double) throws { self.latitude = latitude @@ -142,7 +142,7 @@ extension ParseGeoPoint: CustomStringConvertible { } #if canImport(CoreLocation) -// MARK: CLLocation +// MARK: CoreLocation public extension ParseGeoPoint { /** diff --git a/Sources/ParseSwift/Types/ParseHealth+async.swift b/Sources/ParseSwift/Types/ParseHealth+async.swift index c9fdf27e6..552ffd632 100644 --- a/Sources/ParseSwift/Types/ParseHealth+async.swift +++ b/Sources/ParseSwift/Types/ParseHealth+async.swift @@ -9,7 +9,7 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseHealth { // MARK: Async/Await @@ -18,7 +18,7 @@ public extension ParseHealth { Calls the health check function *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Status of ParseServer. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ static func check(options: API.Options = []) async throws -> String { try await withCheckedThrowingContinuation { continuation in diff --git a/Sources/ParseSwift/Types/ParseHealth+combine.swift b/Sources/ParseSwift/Types/ParseHealth+combine.swift index e4873fbab..660ff4603 100644 --- a/Sources/ParseSwift/Types/ParseHealth+combine.swift +++ b/Sources/ParseSwift/Types/ParseHealth+combine.swift @@ -15,7 +15,7 @@ public extension ParseHealth { // MARK: Combine /** - Calls the health check function *asynchronously*. Publishes when complete. + Calls the health check function *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: A publisher that eventually produces a single value and then finishes or fails. */ diff --git a/Sources/ParseSwift/Types/ParseOperation+async.swift b/Sources/ParseSwift/Types/ParseOperation+async.swift index 50022116a..514d9bf36 100644 --- a/Sources/ParseSwift/Types/ParseOperation+async.swift +++ b/Sources/ParseSwift/Types/ParseOperation+async.swift @@ -9,7 +9,7 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension ParseOperation { // MARK: Async/Await @@ -18,8 +18,8 @@ public extension ParseOperation { Saves the operations on the `ParseObject` *asynchronously* and executes the given callback block. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: A saved `ParseFile`. + - throws: An error of type `ParseError`.. */ func save(options: API.Options = []) async throws -> T { try await withCheckedThrowingContinuation { continuation in diff --git a/Sources/ParseSwift/Types/ParsePolygon.swift b/Sources/ParseSwift/Types/ParsePolygon.swift index 3eaac7fe4..af15030a8 100644 --- a/Sources/ParseSwift/Types/ParsePolygon.swift +++ b/Sources/ParseSwift/Types/ParsePolygon.swift @@ -23,7 +23,7 @@ public struct ParsePolygon: Codable, Hashable { /** Create new `ParsePolygon` instance with coordinates. - parameter coordinates: The geopoints that make the polygon. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ public init(_ coordinates: [ParseGeoPoint]) throws { self.coordinates = coordinates @@ -33,7 +33,7 @@ public struct ParsePolygon: Codable, Hashable { /** Create new `ParsePolygon` instance with a variadic amount of coordinates. - parameter coordinates: variadic amount of zero or more `ParseGeoPoint`'s. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ public init(_ coordinates: ParseGeoPoint...) throws { self.coordinates = coordinates diff --git a/Sources/ParseSwift/Types/ParseRelation.swift b/Sources/ParseSwift/Types/ParseRelation.swift index b9e958b48..315fd8239 100644 --- a/Sources/ParseSwift/Types/ParseRelation.swift +++ b/Sources/ParseSwift/Types/ParseRelation.swift @@ -9,7 +9,7 @@ import Foundation /** - The `ParseRelation` class that is used to access all of the children of a many-to-many relationship. + The `ParseRelation` object that is used to access all of the children of a many-to-many relationship. Each instance of `ParseRelation` is associated with a particular parent object and key. In most cases, you do not need to create an instance of `ParseRelation` directly as it can be @@ -138,17 +138,48 @@ public struct ParseRelation: Encodable, Hashable where T: ParseObject { /** Returns a `Query` that is limited to objects for a specific `key` and `parent` in this relation. - parameter key: The key for the relation. - - parameter parent: The child class for the relation. + - parameter parent: The parent pointer object for the relation. + - returns: A relation query. + */ + public static func query(_ key: String, parent: Pointer) -> Query where U: ParseObject { + Query(related(key: key, object: parent)) + } + + /** + Returns a `Query` that is limited to objects for a specific `key` and `parent` in this relation. + - parameter key: The key for the relation. + - parameter parent: The parent object for the relation. + - throws: An error of type `ParseError`. + - returns: A relation query. + */ + public static func query(_ key: String, parent: U) throws -> Query where U: ParseObject { + Self.query(key, parent: try parent.toPointer()) + } + + /** + Returns a `Query` that is limited to objects for a specific `key` and `parent` in this relation. + - parameter key: The key for the relation. + - parameter parent: The parent object for the relation. - throws: An error of type `ParseError`. - returns: A relation query. */ public func query(_ key: String, parent: U) throws -> Query where U: ParseObject { - Query(related(key: key, object: try parent.toPointer())) + try Self.query(key, parent: parent) + } + + /** + Returns a `Query` that is limited to objects for a specific `key` and `parent` in this relation. + - parameter key: The key for the relation. + - parameter parent: The parent pointer object for the relation. + - returns: A relation query. + */ + public func query(_ key: String, parent: Pointer) -> Query where U: ParseObject { + Self.query(key, parent: parent) } /** Returns a `Query` that is limited to the key and objects in this relation. - - parameter child: The child class for the relation. + - parameter child: The child object for the relation. - throws: An error of type `ParseError`. - returns: A relation query. */ @@ -159,7 +190,7 @@ public struct ParseRelation: Encodable, Hashable where T: ParseObject { } if !isSameClass([child]) { throw ParseError(code: .unknownError, - message: "ParseRelation must have the same child class as the original relation.") + message: "ParseRelation must have the same child className as the original relation.") } return Query(related(key: key, object: try parent.toPointer())) } @@ -167,7 +198,7 @@ public struct ParseRelation: Encodable, Hashable where T: ParseObject { /** Returns a `Query` that is limited to objects for a specific `key` and `child` in this relation. - parameter key: The key for the relation. - - parameter child: The child class for the relation. + - parameter child: The child object for the relation. - throws: An error of type `ParseError`. - returns: A relation query. */ @@ -248,9 +279,31 @@ public extension ParseRelation { // MARK: ParseRelation public extension ParseObject { + /** + Returns a `Query` that is limited to objects for a specific `key` and `parent` in this relation. + - parameter key: The key for the relation. + - parameter parent: The parent object for the relation. + - throws: An error of type `ParseError`. + - returns: A relation query. + */ + static func queryRelations(_ key: String, parent: U) throws -> Query { + try ParseRelation.query(key, parent: parent) + } + + /** + Returns a `Query` that is limited to objects for a specific `key` and `parent` in this relation. + - parameter key: The key for the relation. + - parameter parent: The parent pointer object for the relation. + - throws: An error of type `ParseError`. + - returns: A relation query. + */ + static func queryRelations(_ key: String, parent: Pointer) -> Query { + ParseRelation.query(key, parent: parent) + } + /// Create a new relation. var relation: ParseRelation { - return ParseRelation(parent: self) + ParseRelation(parent: self) } /** diff --git a/Sources/ParseSwift/Types/Pointer+async.swift b/Sources/ParseSwift/Types/Pointer+async.swift index cc1a4daa3..b4505e1c3 100644 --- a/Sources/ParseSwift/Types/Pointer+async.swift +++ b/Sources/ParseSwift/Types/Pointer+async.swift @@ -10,7 +10,7 @@ import Foundation // MARK: Async/Await -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension Pointer { /** Fetches the `ParseObject` *aynchronously* with the current data from the server and sets an error if one occurs. diff --git a/Sources/ParseSwift/Types/Query+async.swift b/Sources/ParseSwift/Types/Query+async.swift index 5fa95af26..fe2875114 100644 --- a/Sources/ParseSwift/Types/Query+async.swift +++ b/Sources/ParseSwift/Types/Query+async.swift @@ -9,16 +9,16 @@ #if swift(>=5.5) && canImport(_Concurrency) import Foundation -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) +@MainActor public extension Query { // MARK: Async/Await /** - Finds objects *asynchronously* and publishes when complete. + Finds objects *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An array of ParseObjects. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func find(options: API.Options = []) async throws -> [ResultType] { try await withCheckedThrowingContinuation { continuation in @@ -28,14 +28,14 @@ public extension Query { } /** - Query plan information for finding objects *asynchronously* and publishes when complete. + Query plan information for finding objects *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be different for mongoDB and PostgreSQL. One way around this is to use a type-erased wrapper such as the [AnyCodable](https://github.com/Flight-School/AnyCodable) package. - returns: An array of ParseObjects. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func findExplain(options: API.Options = []) async throws -> [U] { try await withCheckedThrowingContinuation { continuation in @@ -45,12 +45,11 @@ public extension Query { } /** - Retrieves *asynchronously* a complete list of `ParseObject`'s that satisfy this query - and publishes when complete. + Retrieves *asynchronously* a complete list of `ParseObject`'s that satisfy this query. - parameter batchLimit: The maximum number of objects to send in each batch. If the items to be batched. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An array of ParseObjects. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. - warning: The items are processed in an unspecified order. The query may not have any sort order, and may not use limit or skip. */ @@ -64,10 +63,10 @@ public extension Query { } /** - Gets an object *asynchronously* and publishes when complete. + Gets an object *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: The first `ParseObject`. + - throws: An error of type `ParseError`.. */ func first(options: API.Options = []) async throws -> ResultType { try await withCheckedThrowingContinuation { continuation in @@ -77,14 +76,14 @@ public extension Query { } /** - Query plan information for getting an object *asynchronously* and publishes when complete. + Query plan information for getting an object *asynchronously*. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be different for mongoDB and PostgreSQL. One way around this is to use a type-erased wrapper such as the [AnyCodable](https://github.com/Flight-School/AnyCodable) package. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An array of ParseObjects. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func firstExplain(options: API.Options = []) async throws -> U { try await withCheckedThrowingContinuation { continuation in @@ -94,10 +93,10 @@ public extension Query { } /** - Count objects *asynchronously* and publishes when complete. + Count objects *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: A publisher that eventually produces a single value and then finishes or fails. - - throws: `ParseError`. + - returns: The count of `ParseObject`'s. + - throws: An error of type `ParseError`.. */ func count(options: API.Options = []) async throws -> Int { try await withCheckedThrowingContinuation { continuation in @@ -107,7 +106,7 @@ public extension Query { } /** - Query plan information for counting objects *asynchronously* and publishes when complete. + Query plan information for counting objects *asynchronously*. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be different for mongoDB and PostgreSQL. One way around this is to use a type-erased wrapper @@ -115,7 +114,7 @@ public extension Query { - parameter explain: Used to toggle the information on the query plan. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An array of ParseObjects. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func countExplain(options: API.Options = []) async throws -> [U] { try await withCheckedThrowingContinuation { continuation in @@ -125,12 +124,12 @@ public extension Query { } /** - Executes an aggregate query *asynchronously* and publishes when complete. + Executes an aggregate query *asynchronously*. - requires: `.useMasterKey` has to be available. - parameter pipeline: A pipeline of stages to process query. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An array of ParseObjects. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func aggregate(_ pipeline: [[String: Encodable]], options: API.Options = []) async throws -> [ResultType] { @@ -142,7 +141,7 @@ public extension Query { } /** - Query plan information for executing an aggregate query *asynchronously* and publishes when complete. + Query plan information for executing an aggregate query *asynchronously*. - requires: `.useMasterKey` has to be available. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be @@ -151,7 +150,7 @@ public extension Query { - parameter pipeline: A pipeline of stages to process query. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An array of ParseObjects. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func aggregateExplain(_ pipeline: [[String: Encodable]], options: API.Options = []) async throws -> [U] { @@ -163,12 +162,12 @@ public extension Query { } /** - Executes a distinct query *asynchronously* and publishes unique values when complete. + Executes a distinct query *asynchronously* and returns unique values when complete. - requires: `.useMasterKey` has to be available. - parameter key: A field to find distinct values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An array of ParseObjects. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func distinct(_ key: String, options: API.Options = []) async throws -> [ResultType] { @@ -180,7 +179,7 @@ public extension Query { } /** - Query plan information for executing a distinct query *asynchronously* and publishes unique values when complete. + Query plan information for executing a distinct query *asynchronously* and returns unique values when complete. - requires: `.useMasterKey` has to be available. - note: An explain query will have many different underlying types. Since Swift is a strongly typed language, a developer should specify the type expected to be decoded which will be @@ -189,7 +188,7 @@ public extension Query { - parameter key: A field to find distinct values. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: An array of ParseObjects. - - throws: `ParseError`. + - throws: An error of type `ParseError`.. */ func distinctExplain(_ key: String, options: API.Options = []) async throws -> [U] { diff --git a/Sources/ParseSwift/Types/Query.swift b/Sources/ParseSwift/Types/Query.swift index 7e3dbb09a..fb3ded4e8 100644 --- a/Sources/ParseSwift/Types/Query.swift +++ b/Sources/ParseSwift/Types/Query.swift @@ -592,7 +592,7 @@ internal struct RelatedCondition : Encodable where T: ParseObject { } /** - Add a constraint that requires a key is related. + Add a constraint that requires a key and object are related. - parameter key: The key that should be related. - parameter object: The object that should be related. - returns: The resulting `QueryConstraint`. @@ -605,7 +605,7 @@ public func related (key: String, object: T) throws -> QueryConstraint where } /** - Add a constraint that requires a key is related. + Add a constraint that requires a key and object are related. - parameter key: The key that should be related. - parameter object: The pointer object that should be related. - returns: The resulting `QueryConstraint`. @@ -627,7 +627,7 @@ public func related(key: String) -> QueryConstraint { } /** - Add a constraint that requires a key is related. + Add a constraint that requires an object is related. - parameter object: The object that should be related. - returns: The resulting `QueryConstraint`. - throws: An error of type `ParseError`. @@ -639,7 +639,7 @@ public func related (object: T) throws -> QueryConstraint where T: ParseObjec } /** - Add a constraint that requires a key is related. + Add a constraint that requires an object is related. - parameter object: The pointer object that should be related. - returns: The resulting `QueryConstraint`. */ @@ -661,10 +661,10 @@ internal struct QueryWhere: Encodable, Equatable { var container = encoder.container(keyedBy: RawCodingKey.self) try constraints.forEach { (key, value) in try value.forEach { (constraint) in - if constraint.comparator != nil { + if let comparotor = constraint.comparator { var nestedContainer = container.nestedContainer(keyedBy: QueryConstraint.Comparator.self, - forKey: .key(key)) - try constraint.encode(to: nestedContainer.superEncoder(forKey: constraint.comparator!)) + forKey: .key(key)) + try constraint.encode(to: nestedContainer.superEncoder(forKey: comparotor)) } else { try container.encode(constraint, forKey: .key(key)) } diff --git a/Tests/ParseSwiftTests/ParseAnanlyticsAsyncTests.swift b/Tests/ParseSwiftTests/ParseAnanlyticsAsyncTests.swift index 28dbee3f3..0769109f4 100644 --- a/Tests/ParseSwiftTests/ParseAnanlyticsAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseAnanlyticsAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseAnanlyticsAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length override func setUpWithError() throws { try super.setUpWithError() diff --git a/Tests/ParseSwiftTests/ParseAnonymousAsyncTests.swift b/Tests/ParseSwiftTests/ParseAnonymousAsyncTests.swift index 0270e2a59..481f2bc41 100644 --- a/Tests/ParseSwiftTests/ParseAnonymousAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseAnonymousAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseAnonymousAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct User: ParseUser { diff --git a/Tests/ParseSwiftTests/ParseAppleAsyncTests.swift b/Tests/ParseSwiftTests/ParseAppleAsyncTests.swift index 8acea3c27..9ec9ab817 100644 --- a/Tests/ParseSwiftTests/ParseAppleAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseAppleAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseAppleAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct User: ParseUser { diff --git a/Tests/ParseSwiftTests/ParseAuthenticationAsyncTests.swift b/Tests/ParseSwiftTests/ParseAuthenticationAsyncTests.swift index ecce64c2c..0265e0e82 100644 --- a/Tests/ParseSwiftTests/ParseAuthenticationAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseAuthenticationAsyncTests.swift @@ -12,7 +12,6 @@ import XCTest @testable import ParseSwift import Combine -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseAuthenticationAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct User: ParseUser { @@ -98,13 +97,11 @@ class ParseAuthenticationAsyncTests: XCTestCase { // swiftlint:disable:this type } #if swift(>=5.5) && canImport(_Concurrency) - @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func login(authData: [String: String], options: API.Options) async throws -> AuthenticatedUser { throw ParseError(code: .unknownError, message: "Not implemented") } - @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func link(authData: [String: String], options: API.Options) async throws -> AuthenticatedUser { throw ParseError(code: .unknownError, message: "Not implemented") diff --git a/Tests/ParseSwiftTests/ParseAuthenticationCombineTests.swift b/Tests/ParseSwiftTests/ParseAuthenticationCombineTests.swift index 2b4ad63c5..450b862ca 100644 --- a/Tests/ParseSwiftTests/ParseAuthenticationCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseAuthenticationCombineTests.swift @@ -101,13 +101,11 @@ class ParseAuthenticationCombineTests: XCTestCase { #endif #if swift(>=5.5) && canImport(_Concurrency) - @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func login(authData: [String: String], options: API.Options) async throws -> AuthenticatedUser { throw ParseError(code: .unknownError, message: "Not implemented") } - @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func link(authData: [String: String], options: API.Options) async throws -> AuthenticatedUser { throw ParseError(code: .unknownError, message: "Not implemented") diff --git a/Tests/ParseSwiftTests/ParseAuthenticationTests.swift b/Tests/ParseSwiftTests/ParseAuthenticationTests.swift index 37c7123e8..761c5b008 100644 --- a/Tests/ParseSwiftTests/ParseAuthenticationTests.swift +++ b/Tests/ParseSwiftTests/ParseAuthenticationTests.swift @@ -101,13 +101,11 @@ class ParseAuthenticationTests: XCTestCase { #endif #if swift(>=5.5) && canImport(_Concurrency) - @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func login(authData: [String: String], options: API.Options) async throws -> AuthenticatedUser { throw ParseError(code: .unknownError, message: "Not implemented") } - @available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) func link(authData: [String: String], options: API.Options) async throws -> AuthenticatedUser { throw ParseError(code: .unknownError, message: "Not implemented") diff --git a/Tests/ParseSwiftTests/ParseCloudAsyncTests.swift b/Tests/ParseSwiftTests/ParseCloudAsyncTests.swift index 76affc20e..9801e4cce 100644 --- a/Tests/ParseSwiftTests/ParseCloudAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseCloudAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseCloudAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct Cloud: ParseCloud { typealias ReturnType = String? // swiftlint:disable:this nesting diff --git a/Tests/ParseSwiftTests/ParseConfigAsyncTests.swift b/Tests/ParseSwiftTests/ParseConfigAsyncTests.swift index 0c3c7bd3a..45f0c7a00 100644 --- a/Tests/ParseSwiftTests/ParseConfigAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseConfigAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseConfigAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct Config: ParseConfig { var welcomeMessage: String? diff --git a/Tests/ParseSwiftTests/ParseFacebookAsyncTests.swift b/Tests/ParseSwiftTests/ParseFacebookAsyncTests.swift index 9483ef0a3..d5db0101b 100644 --- a/Tests/ParseSwiftTests/ParseFacebookAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseFacebookAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseFacebookAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct User: ParseUser { diff --git a/Tests/ParseSwiftTests/ParseFileAsyncTests.swift b/Tests/ParseSwiftTests/ParseFileAsyncTests.swift index 28f6ebdc9..d1c1d80e8 100644 --- a/Tests/ParseSwiftTests/ParseFileAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseFileAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseFileAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length let temporaryDirectory = "\(NSTemporaryDirectory())test/" diff --git a/Tests/ParseSwiftTests/ParseHealthAsyncTests.swift b/Tests/ParseSwiftTests/ParseHealthAsyncTests.swift index ca505596a..67b2287f7 100644 --- a/Tests/ParseSwiftTests/ParseHealthAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseHealthAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseHealthAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length override func setUpWithError() throws { try super.setUpWithError() diff --git a/Tests/ParseSwiftTests/ParseInstallationAsyncTests.swift b/Tests/ParseSwiftTests/ParseInstallationAsyncTests.swift index dd9cdb77e..220fe8dc0 100644 --- a/Tests/ParseSwiftTests/ParseInstallationAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseInstallationAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseInstallationAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct User: ParseUser { diff --git a/Tests/ParseSwiftTests/ParseLDAPAsyncTests.swift b/Tests/ParseSwiftTests/ParseLDAPAsyncTests.swift index 4d8b42af7..ddd0c3d8d 100644 --- a/Tests/ParseSwiftTests/ParseLDAPAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseLDAPAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseLDAPAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct User: ParseUser { diff --git a/Tests/ParseSwiftTests/ParseLiveQueryAsyncTests.swift b/Tests/ParseSwiftTests/ParseLiveQueryAsyncTests.swift index 536cb0204..09251c40d 100644 --- a/Tests/ParseSwiftTests/ParseLiveQueryAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseLiveQueryAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseLiveQueryAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length override func setUpWithError() throws { try super.setUpWithError() diff --git a/Tests/ParseSwiftTests/ParseObjectAsyncTests.swift b/Tests/ParseSwiftTests/ParseObjectAsyncTests.swift index 999656bef..417f567b5 100644 --- a/Tests/ParseSwiftTests/ParseObjectAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseObjectAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct GameScore: ParseObject { diff --git a/Tests/ParseSwiftTests/ParseOperationAsyncTests.swift b/Tests/ParseSwiftTests/ParseOperationAsyncTests.swift index 993c79a1f..93b19f1f8 100644 --- a/Tests/ParseSwiftTests/ParseOperationAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseOperationAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseOperationAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct GameScore: ParseObject { //: These are required by ParseObject diff --git a/Tests/ParseSwiftTests/ParsePointerAsyncTests.swift b/Tests/ParseSwiftTests/ParsePointerAsyncTests.swift index e4763aa34..94282ce1a 100644 --- a/Tests/ParseSwiftTests/ParsePointerAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParsePointerAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParsePointerAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct GameScore: ParseObject { diff --git a/Tests/ParseSwiftTests/ParseQueryAsyncTests.swift b/Tests/ParseSwiftTests/ParseQueryAsyncTests.swift index 3838ae7ed..036958e9b 100644 --- a/Tests/ParseSwiftTests/ParseQueryAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseQueryAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseQueryAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct GameScore: ParseObject { //: These are required by ParseObject diff --git a/Tests/ParseSwiftTests/ParseQueryTests.swift b/Tests/ParseSwiftTests/ParseQueryTests.swift index 667a87e7c..d356e9ab6 100644 --- a/Tests/ParseSwiftTests/ParseQueryTests.swift +++ b/Tests/ParseSwiftTests/ParseQueryTests.swift @@ -1923,6 +1923,44 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length } } + func testWhereKeyRelatedNoObject() throws { + let expected: [String: AnyCodable] = [ + "$relatedTo": [ + "key": "yolo" + ] + ] + var object = GameScore(score: 50) + object.objectId = "hello" + let constraint = related(key: "yolo") + let query = GameScore.query(constraint) + let queryWhere = query.`where` + + do { + let encoded = try ParseCoding.jsonEncoder().encode(queryWhere) + let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded) + XCTAssertEqual(expected.keys, decodedDictionary.keys) + + guard let expectedValues = expected.values.first?.value as? [String: Any], + let expectedKey = expectedValues["key"] as? String else { + XCTFail("Should have casted") + return + } + + guard let decodedValues = decodedDictionary.values.first?.value as? [String: Any], + let decodedKey = decodedValues["key"] as? String else { + XCTFail("Should have casted") + return + } + + XCTAssertEqual(expectedKey, decodedKey) + XCTAssertNil(decodedValues["object"]) + + } catch { + XCTFail(error.localizedDescription) + return + } + } + func testWhereKeyRelatedPointer() throws { let expected: [String: AnyCodable] = [ "$relatedTo": [ @@ -1966,6 +2004,129 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length } } + func testWhereKeyRelatedPointerNoKey() throws { + let expected: [String: AnyCodable] = [ + "$relatedTo": [ + "object": ["__type": "Pointer", + "className": "GameScore", + "objectId": "hello"] + ] + ] + var object = GameScore(score: 50) + object.objectId = "hello" + let constraint = related(object: try object.toPointer()) + let query = GameScore.query(constraint) + let queryWhere = query.`where` + + do { + let encoded = try ParseCoding.jsonEncoder().encode(queryWhere) + let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded) + XCTAssertEqual(expected.keys, decodedDictionary.keys) + + guard let expectedValues = expected.values.first?.value as? [String: Any], + let expectedObject = expectedValues["object"] as? [String: String] else { + XCTFail("Should have casted") + return + } + + guard let decodedValues = decodedDictionary.values.first?.value as? [String: Any], + let decodedObject = decodedValues["object"] as? [String: String] else { + XCTFail("Should have casted") + return + } + + XCTAssertNil(decodedValues["key"]) + XCTAssertEqual(expectedObject, decodedObject) + + } catch { + XCTFail(error.localizedDescription) + return + } + } + + func testWhereKeyRelatedObject() throws { + let expected: [String: AnyCodable] = [ + "$relatedTo": [ + "key": "yolo", + "object": ["__type": "Pointer", + "className": "GameScore", + "objectId": "hello"] + ] + ] + var object = GameScore(score: 50) + object.objectId = "hello" + let constraint = try related(key: "yolo", object: object) + let query = GameScore.query(constraint) + let queryWhere = query.`where` + + do { + let encoded = try ParseCoding.jsonEncoder().encode(queryWhere) + let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded) + XCTAssertEqual(expected.keys, decodedDictionary.keys) + + guard let expectedValues = expected.values.first?.value as? [String: Any], + let expectedKey = expectedValues["key"] as? String, + let expectedObject = expectedValues["object"] as? [String: String] else { + XCTFail("Should have casted") + return + } + + guard let decodedValues = decodedDictionary.values.first?.value as? [String: Any], + let decodedKey = decodedValues["key"] as? String, + let decodedObject = decodedValues["object"] as? [String: String] else { + XCTFail("Should have casted") + return + } + + XCTAssertEqual(expectedKey, decodedKey) + XCTAssertEqual(expectedObject, decodedObject) + + } catch { + XCTFail(error.localizedDescription) + return + } + } + + func testWhereKeyRelatedObjectNoKey() throws { + let expected: [String: AnyCodable] = [ + "$relatedTo": [ + "object": ["__type": "Pointer", + "className": "GameScore", + "objectId": "hello"] + ] + ] + var object = GameScore(score: 50) + object.objectId = "hello" + let constraint = try related(object: object) + let query = GameScore.query(constraint) + let queryWhere = query.`where` + + do { + let encoded = try ParseCoding.jsonEncoder().encode(queryWhere) + let decodedDictionary = try JSONDecoder().decode([String: AnyCodable].self, from: encoded) + XCTAssertEqual(expected.keys, decodedDictionary.keys) + + guard let expectedValues = expected.values.first?.value as? [String: Any], + let expectedObject = expectedValues["object"] as? [String: String] else { + XCTFail("Should have casted") + return + } + + guard let decodedValues = decodedDictionary.values.first?.value as? [String: Any], + let decodedObject = decodedValues["object"] as? [String: String] else { + XCTFail("Should have casted") + return + } + + XCTAssertNil(decodedValues["key"]) + XCTAssertEqual(expectedObject, decodedObject) + + } catch { + XCTFail(error.localizedDescription) + return + } + } + func testWhereKeyRelativeToTime() throws { let expected: [String: AnyCodable] = [ "yolo": [ diff --git a/Tests/ParseSwiftTests/ParseRelationTests.swift b/Tests/ParseSwiftTests/ParseRelationTests.swift index 8be8c4b3d..a1bb20979 100644 --- a/Tests/ParseSwiftTests/ParseRelationTests.swift +++ b/Tests/ParseSwiftTests/ParseRelationTests.swift @@ -231,6 +231,12 @@ class ParseRelationTests: XCTestCase { XCTAssertEqual(decoded, expected) } + func testIsSameClassNone() throws { + let score = GameScore(score: 10) + let relation = score.relation + XCTAssertFalse(relation.isSameClass([GameScore]())) + } + func testRemoveIncorrectClassError() throws { var score = GameScore(score: 10) let objectId = "hello" @@ -350,5 +356,25 @@ class ParseRelationTests: XCTestCase { let decoded3 = try XCTUnwrap(String(data: encoded3, encoding: .utf8)) XCTAssertEqual(decoded3, expected3) } + + func testQueryStatic() throws { + var score = GameScore(score: 10) + let objectId = "hello" + score.objectId = objectId + + let query = Level.queryRelations("levels", parent: try score.toPointer()) + // swiftlint:disable:next line_length + let expected = "{\"limit\":100,\"skip\":0,\"_method\":\"GET\",\"where\":{\"$relatedTo\":{\"key\":\"levels\",\"object\":{\"__type\":\"Pointer\",\"className\":\"GameScore\",\"objectId\":\"hello\"}}}}" + let encoded = try ParseCoding.jsonEncoder().encode(query) + let decoded = try XCTUnwrap(String(data: encoded, encoding: .utf8)) + XCTAssertEqual(decoded, expected) + + let query2 = try Level.queryRelations("levels", parent: score) + // swiftlint:disable:next line_length + let expected2 = "{\"limit\":100,\"skip\":0,\"_method\":\"GET\",\"where\":{\"$relatedTo\":{\"key\":\"levels\",\"object\":{\"__type\":\"Pointer\",\"className\":\"GameScore\",\"objectId\":\"hello\"}}}}" + let encoded2 = try ParseCoding.jsonEncoder().encode(query2) + let decoded2 = try XCTUnwrap(String(data: encoded2, encoding: .utf8)) + XCTAssertEqual(decoded2, expected2) + } } #endif diff --git a/Tests/ParseSwiftTests/ParseTwitterAsyncTests.swift b/Tests/ParseSwiftTests/ParseTwitterAsyncTests.swift index 15724c04d..886a143f1 100644 --- a/Tests/ParseSwiftTests/ParseTwitterAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseTwitterAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseTwitterAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct User: ParseUser { diff --git a/Tests/ParseSwiftTests/ParseUserAsyncTests.swift b/Tests/ParseSwiftTests/ParseUserAsyncTests.swift index 921843a70..607c6d9e1 100644 --- a/Tests/ParseSwiftTests/ParseUserAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseUserAsyncTests.swift @@ -11,7 +11,6 @@ import Foundation import XCTest @testable import ParseSwift -@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) class ParseUserAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length struct User: ParseUser {