diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ccca6287..6abd0579b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,18 @@ ### main -[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.1.0...main) +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.2.0...main) * _Contributing to this repo? Add info about your change here to be included in the next release_ +### 2.1.0 +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.1.0...2.2.0) + +__Improvements__ +- Added ability to fetch ParsePointer using async/await ([#271](https://github.com/parse-community/Parse-Swift/pull/271)), thanks to [Corey Baker](https://github.com/cbaker6). + +__Fixes__ +- By default, don't use cache when fetching ParseObject's and `ParseFile`s. Developers can choose to fetch from cache if desired by passing the necessary option while fetching. Fixed a bug when the incorrect file location for a dowloaded ParseFile was being cached ([#272](https://github.com/parse-community/Parse-Swift/pull/272)), thanks to [Corey Baker](https://github.com/cbaker6). + ### 2.1.0 [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.0.3...2.1.0) diff --git a/Sources/ParseSwift/API/API+Command.swift b/Sources/ParseSwift/API/API+Command.swift index 8f93e3500..77c6d1220 100644 --- a/Sources/ParseSwift/API/API+Command.swift +++ b/Sources/ParseSwift/API/API+Command.swift @@ -321,8 +321,10 @@ internal extension API.Command { .appendingPathComponent(ParseConstants.fileDownloadsDirectory, isDirectory: true) try fileManager.createDirectoryIfNeeded(downloadDirectoryPath.relativePath) let fileLocation = downloadDirectoryPath.appendingPathComponent(object.name) - try? FileManager.default.removeItem(at: fileLocation) //Remove file if it's already present - try FileManager.default.moveItem(at: tempFileLocation, to: fileLocation) + if tempFileLocation != fileLocation { + try? FileManager.default.removeItem(at: fileLocation) //Remove file if it's already present + try FileManager.default.moveItem(at: tempFileLocation, to: fileLocation) + } var object = object object.localURL = fileLocation return object diff --git a/Sources/ParseSwift/API/API.swift b/Sources/ParseSwift/API/API.swift index 7baf32023..8e6bad3b4 100644 --- a/Sources/ParseSwift/API/API.swift +++ b/Sources/ParseSwift/API/API.swift @@ -164,7 +164,7 @@ public struct API { } public static func == (lhs: API.Option, rhs: API.Option) -> Bool { - return AnyEncodable(lhs) == AnyEncodable(rhs) + lhs.hashValue == rhs.hashValue } } diff --git a/Sources/ParseSwift/API/URLSession+extensions.swift b/Sources/ParseSwift/API/URLSession+extensions.swift index 3df1e2f0a..63aa8dc30 100755 --- a/Sources/ParseSwift/API/URLSession+extensions.swift +++ b/Sources/ParseSwift/API/URLSession+extensions.swift @@ -110,11 +110,6 @@ extension URLSession { if let location = location { do { let data = try ParseCoding.jsonEncoder().encode(location) - if URLSession.parse.configuration.urlCache?.cachedResponse(for: request) == nil { - URLSession.parse.configuration.urlCache?.storeCachedResponse(.init(response: response, - data: data), - for: request) - } return try .success(mapper(data)) } catch { guard let parseError = error as? ParseError else { @@ -189,10 +184,26 @@ extension URLSession { completion: @escaping(Result) -> Void ) { let task = downloadTask(with: request) { (location, urlResponse, responseError) in - completion(self.makeResult(request: request, - location: location, - urlResponse: urlResponse, - responseError: responseError, mapper: mapper)) + let result = self.makeResult(request: request, + location: location, + urlResponse: urlResponse, + responseError: responseError, mapper: mapper) + if case .success(let file) = result { + guard let response = urlResponse, + let parseFile = file as? ParseFile, + let fileLocation = parseFile.localURL, + let data = try? ParseCoding.jsonEncoder().encode(fileLocation) else { + completion(result) + return + } + if URLSession.parse.configuration.urlCache?.cachedResponse(for: request) == nil { + URLSession.parse.configuration.urlCache? + .storeCachedResponse(.init(response: response, + data: data), + for: request) + } + } + completion(result) } ParseSwift.sessionDelegate.downloadDelegates[task] = progress ParseSwift.sessionDelegate.taskCallbackQueues[task] = callbackQueue diff --git a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift index 1ab615004..988e5fc59 100644 --- a/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift +++ b/Sources/ParseSwift/Authentication/Protocols/ParseAuthentication.swift @@ -208,6 +208,8 @@ public extension ParseUser { - throws: An error of type `ParseError`. - returns: An instance of the logged in `ParseUser`. If login failed due to either an incorrect password or incorrect username, it throws a `ParseError`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ static func login(_ type: String, authData: [String: String], @@ -298,6 +300,8 @@ public extension ParseUser { - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: The block to execute. It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func unlink(_ type: String, options: API.Options = [], @@ -347,6 +351,8 @@ public extension ParseUser { - throws: An error of type `ParseError`. - returns: An instance of the logged in `ParseUser`. If login failed due to either an incorrect password or incorrect username, it throws a `ParseError`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ static func link(_ type: String, authData: [String: String], @@ -371,6 +377,8 @@ public extension ParseUser { - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: The block to execute. It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ static func link(_ type: String, authData: [String: String], diff --git a/Sources/ParseSwift/LiveQuery/Subscription.swift b/Sources/ParseSwift/LiveQuery/Subscription.swift index 7a8d61e07..958615feb 100644 --- a/Sources/ParseSwift/LiveQuery/Subscription.swift +++ b/Sources/ParseSwift/LiveQuery/Subscription.swift @@ -58,7 +58,7 @@ private func == (lhs: Event, rhs: Event) -> Bool { #if canImport(Combine) /** - A default implementation of the `ParseSubscription` protocol. Suitable for `ObjectObserved` + A default implementation of the `QuerySubscribable` protocol. Suitable for `ObjectObserved` as the subscription can be used as a SwiftUI publisher. Meaning it can serve indepedently as a ViewModel in MVVM. */ diff --git a/Sources/ParseSwift/LiveQuery/SubscriptionCallback.swift b/Sources/ParseSwift/LiveQuery/SubscriptionCallback.swift index f1d8f175e..5c9af3a8c 100644 --- a/Sources/ParseSwift/LiveQuery/SubscriptionCallback.swift +++ b/Sources/ParseSwift/LiveQuery/SubscriptionCallback.swift @@ -9,7 +9,7 @@ import Foundation /** - A default implementation of the `ParseSubscription` protocol using closures for callbacks. + A default implementation of the `QuerySubscribable` protocol using closures for callbacks. */ open class SubscriptionCallback: QuerySubscribable { diff --git a/Sources/ParseSwift/Objects/ParseInstallation+async.swift b/Sources/ParseSwift/Objects/ParseInstallation+async.swift index 4971a06df..9f1388dcb 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation+async.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation+async.swift @@ -23,6 +23,8 @@ public extension ParseInstallation { - returns: Returns saved `ParseInstallation`. - throws: `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`. */ func fetch(includeKeys: [String]? = nil, options: API.Options = []) async throws -> Self { diff --git a/Sources/ParseSwift/Objects/ParseInstallation+combine.swift b/Sources/ParseSwift/Objects/ParseInstallation+combine.swift index 344f97a66..2158e4c50 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation+combine.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation+combine.swift @@ -22,6 +22,8 @@ public extension ParseInstallation { - 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 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`. */ func fetchPublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { diff --git a/Sources/ParseSwift/Objects/ParseInstallation.swift b/Sources/ParseSwift/Objects/ParseInstallation.swift index 18cebf503..661f3c3c1 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation.swift @@ -354,9 +354,13 @@ extension ParseInstallation { - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of `ParseError` type. - 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`. */ public func fetch(includeKeys: [String]? = nil, options: API.Options = []) throws -> Self { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) let result: Self = try fetchCommand(include: includeKeys) .execute(options: options, callbackQueue: .main) try Self.updateKeychainIfNeeded([result]) @@ -374,6 +378,8 @@ extension ParseInstallation { - parameter completion: The block to execute when completed. It should have the following argument signature: `(Result)`. - 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`. */ public func fetch( includeKeys: [String]? = nil, @@ -381,6 +387,8 @@ extension ParseInstallation { callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void ) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) do { try fetchCommand(include: includeKeys) .executeAsync(options: options, @@ -469,6 +477,8 @@ extension ParseInstallation { increase the probability of colliding `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`. */ public func save(isIgnoreCustomObjectIdConfig: Bool, options: API.Options = []) throws -> Self { @@ -520,6 +530,8 @@ extension ParseInstallation { increase the probability of colliding `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`. */ public func save( isIgnoreCustomObjectIdConfig: Bool = false, @@ -615,6 +627,8 @@ extension ParseInstallation { - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of `ParseError` type. - important: If an object deleted 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`. */ public func delete(options: API.Options = []) throws { var options = options @@ -632,6 +646,8 @@ extension ParseInstallation { - parameter completion: The block to execute when completed. It should have the following argument signature: `(Result)`. - important: If an object deleted 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`. */ public func delete( options: API.Options = [], @@ -724,6 +740,8 @@ public extension Sequence where Element: ParseInstallation { increase the probability of colliding `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, // swiftlint:disable:this function_body_length transaction: Bool = false, @@ -825,6 +843,8 @@ public extension Sequence where Element: ParseInstallation { increase the probability of colliding `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( // swiftlint:disable:this function_body_length cyclomatic_complexity batchLimit limit: Int? = nil, @@ -1067,6 +1087,8 @@ public extension Sequence where Element: ParseInstallation { - 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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func deleteAll(batchLimit limit: Int? = nil, transaction: Bool = false, @@ -1117,6 +1139,8 @@ public extension Sequence where Element: ParseInstallation { - 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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func deleteAll( batchLimit limit: Int? = nil, diff --git a/Sources/ParseSwift/Objects/ParseObject+async.swift b/Sources/ParseSwift/Objects/ParseObject+async.swift index fa6fb1ea5..d0db0b985 100644 --- a/Sources/ParseSwift/Objects/ParseObject+async.swift +++ b/Sources/ParseSwift/Objects/ParseObject+async.swift @@ -21,6 +21,8 @@ public extension ParseObject { - 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`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetch(includeKeys: [String]? = nil, options: API.Options = []) async throws -> Self { diff --git a/Sources/ParseSwift/Objects/ParseObject+combine.swift b/Sources/ParseSwift/Objects/ParseObject+combine.swift index 8c29dffaf..08b52fc08 100644 --- a/Sources/ParseSwift/Objects/ParseObject+combine.swift +++ b/Sources/ParseSwift/Objects/ParseObject+combine.swift @@ -21,6 +21,8 @@ public extension ParseObject { `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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetchPublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index 61daa7b17..3ed3c7a06 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -109,6 +109,8 @@ public extension Sequence where Element: ParseObject { 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, // swiftlint:disable:this function_body_length transaction: Bool = false, @@ -206,6 +208,8 @@ public extension Sequence where Element: ParseObject { 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( // swiftlint:disable:this function_body_length cyclomatic_complexity batchLimit limit: Int? = nil, @@ -440,6 +444,8 @@ public extension Sequence where Element: ParseObject { - 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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func deleteAll(batchLimit limit: Int? = nil, transaction: Bool = false, @@ -486,6 +492,8 @@ public extension Sequence where Element: ParseObject { - 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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func deleteAll( batchLimit limit: Int? = nil, @@ -571,11 +579,15 @@ 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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func fetch(includeKeys: [String]? = nil, options: API.Options = []) throws -> Self { - try fetchCommand(include: includeKeys).execute(options: options, - callbackQueue: .main) + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + return try fetchCommand(include: includeKeys).execute(options: options, + callbackQueue: .main) } /** @@ -587,6 +599,8 @@ extension ParseObject { value of .main. - parameter completion: The block to execute when completed. It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func fetch( includeKeys: [String]? = nil, @@ -594,6 +608,8 @@ extension ParseObject { callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void ) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) do { try fetchCommand(include: includeKeys) .executeAsync(options: options, @@ -652,6 +668,8 @@ extension ParseObject { 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`. */ public func save(isIgnoreCustomObjectIdConfig: Bool = false, options: API.Options = []) throws -> Self { @@ -751,6 +769,9 @@ extension ParseObject { autoreleaseFrequency: .inherit, target: nil) var options = options + // Remove any caching policy added by the developer as fresh data + // from the server is needed. + options.remove(.cachePolicy(.reloadIgnoringLocalCacheData)) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) queue.sync { diff --git a/Sources/ParseSwift/Objects/ParseUser+async.swift b/Sources/ParseSwift/Objects/ParseUser+async.swift index 5faa21f0f..4613e1cf4 100644 --- a/Sources/ParseSwift/Objects/ParseUser+async.swift +++ b/Sources/ParseSwift/Objects/ParseUser+async.swift @@ -159,6 +159,8 @@ public extension ParseUser { - returns: A publisher that eventually produces a single value and then finishes or fails. - throws: `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, diff --git a/Sources/ParseSwift/Objects/ParseUser+combine.swift b/Sources/ParseSwift/Objects/ParseUser+combine.swift index 4c4378755..28b399a5f 100644 --- a/Sources/ParseSwift/Objects/ParseUser+combine.swift +++ b/Sources/ParseSwift/Objects/ParseUser+combine.swift @@ -145,6 +145,8 @@ public extension ParseUser { - 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 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`. */ func fetchPublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { diff --git a/Sources/ParseSwift/Objects/ParseUser.swift b/Sources/ParseSwift/Objects/ParseUser.swift index 36b9cd197..17486ddb2 100644 --- a/Sources/ParseSwift/Objects/ParseUser.swift +++ b/Sources/ParseSwift/Objects/ParseUser.swift @@ -169,6 +169,8 @@ extension ParseUser { - throws: An error of type `ParseError`. - returns: An instance of the logged in `ParseUser`. If login failed due to either an incorrect password or incorrect username, it throws a `ParseError`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public static func login(username: String, password: String, options: API.Options = []) throws -> Self { @@ -188,6 +190,8 @@ extension ParseUser { - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: The block to execute. It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public static func login( username: String, @@ -233,6 +237,8 @@ extension ParseUser { - 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. - throws: An Error of `ParseError` type. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func become(sessionToken: String, options: API.Options = []) throws -> Self { var newUser = self @@ -255,6 +261,8 @@ extension ParseUser { value of .main. - parameter completion: The block to execute when completed. It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func become(sessionToken: String, options: API.Options = [], @@ -317,6 +325,10 @@ extension ParseUser { /** Logs out the currently logged in user in Keychain *synchronously*. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - throws: An error of `ParseError` type. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public static func logout(options: API.Options = []) throws { var options = options @@ -339,6 +351,8 @@ extension ParseUser { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: A block that will be called when logging out completes or fails. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public static func logout(options: API.Options = [], callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { @@ -385,6 +399,9 @@ 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. + - throws: An error of `ParseError` type. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public static func passwordReset(email: String, options: API.Options = []) throws { var options = options @@ -401,6 +418,8 @@ extension ParseUser { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: A block that will be called when the password reset completes or fails. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public static func passwordReset(email: String, options: API.Options = [], callbackQueue: DispatchQueue = .main, @@ -440,6 +459,9 @@ 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. + - throws: An error of `ParseError` type. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public static func verificationEmail(email: String, options: API.Options = []) throws { @@ -457,6 +479,8 @@ extension ParseUser { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: A block that will be called when the verification request completes or fails. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public static func verificationEmail(email: String, options: API.Options = [], @@ -505,6 +529,8 @@ extension ParseUser { - parameter password: The password of the user. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns whether the sign up was successful. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public static func signup(username: String, password: String, @@ -530,6 +556,9 @@ extension ParseUser { - 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: Returns whether the sign up was successful. + - throws: An error of `ParseError` type. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func signup(options: API.Options = []) throws -> Self { var options = options @@ -553,6 +582,8 @@ extension ParseUser { - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: The block to execute. It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func signup(options: API.Options = [], callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { @@ -599,6 +630,8 @@ extension ParseUser { - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: The block to execute. It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public static func signup( username: String, @@ -715,9 +748,13 @@ extension ParseUser { - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of `ParseError` type. - 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`. */ public func fetch(includeKeys: [String]? = nil, options: API.Options = []) throws -> Self { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) let result: Self = try fetchCommand(include: includeKeys) .execute(options: options, callbackQueue: .main) @@ -736,6 +773,8 @@ extension ParseUser { - parameter completion: The block to execute when completed. It should have the following argument signature: `(Result)`. - 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`. */ public func fetch( includeKeys: [String]? = nil, @@ -743,6 +782,8 @@ extension ParseUser { callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void ) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) do { try fetchCommand(include: includeKeys) .executeAsync(options: options, @@ -830,6 +871,8 @@ extension ParseUser { 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`. */ public func save(isIgnoreCustomObjectIdConfig: Bool, options: API.Options = []) throws -> Self { @@ -881,6 +924,8 @@ extension ParseUser { 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`. */ public func save( isIgnoreCustomObjectIdConfig: Bool = false, @@ -977,6 +1022,8 @@ extension ParseUser { - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of `ParseError` type. - important: If an object deleted 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`. */ public func delete(options: API.Options = []) throws { var options = options @@ -994,6 +1041,8 @@ extension ParseUser { - parameter completion: The block to execute when completed. It should have the following argument signature: `(Result)`. - important: If an object deleted 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`. */ public func delete( options: API.Options = [], @@ -1077,6 +1126,8 @@ public extension Sequence where Element: ParseUser { 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, // swiftlint:disable:this function_body_length transaction: Bool = false, @@ -1177,6 +1228,8 @@ public extension Sequence where Element: ParseUser { 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( // swiftlint:disable:this function_body_length cyclomatic_complexity batchLimit limit: Int? = nil, @@ -1300,7 +1353,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 fetch was successful or a `ParseError` if it failed. - - throws: `ParseError` + - throws: An error of `ParseError` type. - important: If an object fetched has the same objectId as current, it will automatically update the current. - warning: The order in which users are returned are not guarenteed. You shouldn't expect results in any particular order. @@ -1411,11 +1464,13 @@ public extension Sequence where Element: ParseUser { 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 `ParseError` type. - 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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func deleteAll(batchLimit limit: Int? = nil, transaction: Bool = false, @@ -1465,6 +1520,8 @@ public extension Sequence where Element: ParseUser { - 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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func deleteAll( batchLimit limit: Int? = nil, diff --git a/Sources/ParseSwift/Operations/ParseOperation.swift b/Sources/ParseSwift/Operations/ParseOperation.swift index b8f11e640..d109351ba 100644 --- a/Sources/ParseSwift/Operations/ParseOperation.swift +++ b/Sources/ParseSwift/Operations/ParseOperation.swift @@ -34,16 +34,16 @@ public struct ParseOperation: Savable where T: ParseObject { */ public func set(_ key: (String, WritableKeyPath), value: W) throws -> Self where W: Encodable { - var mutableOperation = self guard let target = self.target else { throw ParseError(code: .unknownError, message: "Target shouldn't be nil") } + var mutableOperation = self if let currentValue = target[keyPath: key.1] as? NSObject, let updatedValue = value as? NSObject { - if currentValue != updatedValue { - mutableOperation.operations[key.0] = value - mutableOperation.target?[keyPath: key.1] = value - } + if currentValue != updatedValue { + mutableOperation.operations[key.0] = value + mutableOperation.target?[keyPath: key.1] = value + } } else { mutableOperation.operations[key.0] = value mutableOperation.target?[keyPath: key.1] = value @@ -60,6 +60,9 @@ public struct ParseOperation: Savable where T: ParseObject { */ public func forceSet(_ key: (String, WritableKeyPath), value: W) throws -> Self where W: Encodable { + guard self.target != nil else { + throw ParseError(code: .unknownError, message: "Target shouldn't be nil") + } var mutableOperation = self mutableOperation.operations[key.0] = value mutableOperation.target?[keyPath: key.1] = value @@ -312,6 +315,9 @@ public struct ParseOperation: Savable where T: ParseObject { - returns: The updated operations. */ public func removeRelation(_ key: String, objects: [W]) throws -> Self where W: ParseObject { + guard self.target != nil else { + throw ParseError(code: .unknownError, message: "Target shouldn't be nil") + } var mutableOperation = self mutableOperation.operations[key] = try RemoveRelation(objects: objects) return mutableOperation diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index 1cffdfc31..1a4b99eb5 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.1.0" + static let version = "2.2.0" static let fileManagementDirectory = "parse/" static let fileManagementPrivateDocumentsDirectory = "Private Documents/" static let fileManagementLibraryDirectory = "Library/" diff --git a/Sources/ParseSwift/Storage/ParseFileManager.swift b/Sources/ParseSwift/Storage/ParseFileManager.swift index 536962eca..197ca61f5 100644 --- a/Sources/ParseSwift/Storage/ParseFileManager.swift +++ b/Sources/ParseSwift/Storage/ParseFileManager.swift @@ -148,11 +148,15 @@ public struct ParseFileManager { func moveItem(_ fromPath: URL, toPath: URL, completion: @escaping(Error?) -> Void) { synchronizationQueue.async { - do { - try FileManager.default.moveItem(at: fromPath, to: toPath) + if fromPath != toPath { + do { + try FileManager.default.moveItem(at: fromPath, to: toPath) + completion(nil) + } catch { + completion(error) + } + } else { completion(nil) - } catch { - completion(error) } } } diff --git a/Sources/ParseSwift/Types/ParseAnalytics.swift b/Sources/ParseSwift/Types/ParseAnalytics.swift index 508894a29..99575fc50 100644 --- a/Sources/ParseSwift/Types/ParseAnalytics.swift +++ b/Sources/ParseSwift/Types/ParseAnalytics.swift @@ -68,7 +68,9 @@ public struct ParseAnalytics: ParseType, Hashable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: A block that will be called when file deletes or fails. - It should have the following argument signature: `(Result)` + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ static public func trackAppOpened(launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil, at date: Date? = nil, @@ -123,7 +125,9 @@ public struct ParseAnalytics: ParseType, Hashable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: A block that will be called when file deletes or fails. - It should have the following argument signature: `(Result)` + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ static public func trackAppOpened(dimensions: [String: String]? = nil, at date: Date? = nil, @@ -169,7 +173,9 @@ public struct ParseAnalytics: ParseType, Hashable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: A block that will be called when file deletes or fails. - It should have the following argument signature: `(Result)` + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func track(options: API.Options = [], callbackQueue: DispatchQueue = .main, @@ -214,7 +220,9 @@ public struct ParseAnalytics: ParseType, Hashable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: A block that will be called when file deletes or fails. - It should have the following argument signature: `(Result)` + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public mutating func track(dimensions: [String: String]?, at date: Date? = nil, diff --git a/Sources/ParseSwift/Types/ParseConfig+async.swift b/Sources/ParseSwift/Types/ParseConfig+async.swift index 0587be4ca..342bb3e5b 100644 --- a/Sources/ParseSwift/Types/ParseConfig+async.swift +++ b/Sources/ParseSwift/Types/ParseConfig+async.swift @@ -19,6 +19,8 @@ public extension ParseConfig { - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: The return type of self. - throws: `ParseError`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetch(options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in diff --git a/Sources/ParseSwift/Types/ParseConfig+combine.swift b/Sources/ParseSwift/Types/ParseConfig+combine.swift index 18fae84fb..a2688c68e 100644 --- a/Sources/ParseSwift/Types/ParseConfig+combine.swift +++ b/Sources/ParseSwift/Types/ParseConfig+combine.swift @@ -18,6 +18,8 @@ public extension ParseConfig { Fetch the Config *asynchronously*. Publishes when complete. - 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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetchPublisher(options: API.Options = []) -> Future { Future { promise in diff --git a/Sources/ParseSwift/Types/ParseConfig.swift b/Sources/ParseSwift/Types/ParseConfig.swift index 8b250a791..59d7ae4c6 100644 --- a/Sources/ParseSwift/Types/ParseConfig.swift +++ b/Sources/ParseSwift/Types/ParseConfig.swift @@ -26,9 +26,13 @@ extension ParseConfig { - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: Returns `Self`. - 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`. */ public func fetch(options: API.Options = []) throws -> Self { - try fetchCommand().execute(options: options, callbackQueue: .main) + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) + return try fetchCommand().execute(options: options, callbackQueue: .main) } /** @@ -37,10 +41,14 @@ extension ParseConfig { - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: A block that will be called when retrieving the config completes or fails. It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func fetch(options: API.Options = [], callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) fetchCommand() .executeAsync(options: options, callbackQueue: callbackQueue) { result in callbackQueue.async { @@ -65,8 +73,10 @@ extension ParseConfig { /** Update the Config *synchronously*. - - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: Returns `true` if updated, `false` otherwise. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: Returns `true` if updated, `false` otherwise. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func save(options: API.Options = []) throws -> Bool { var options = options @@ -76,10 +86,12 @@ extension ParseConfig { /** Update the Config *asynchronously*. - - parameter options: A set of header options sent to the server. Defaults to an empty set. - - parameter callbackQueue: The queue to return to after completion. Default value of .main. - - parameter completion: A block that will be called when retrieving the config completes or fails. - It should have the following argument signature: `(Result)`. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default value of .main. + - parameter completion: A block that will be called when retrieving the config completes or fails. + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func save(options: API.Options = [], callbackQueue: DispatchQueue = .main, diff --git a/Sources/ParseSwift/Types/ParseFile+async.swift b/Sources/ParseSwift/Types/ParseFile+async.swift index 250ebb7ac..56879c87f 100644 --- a/Sources/ParseSwift/Types/ParseFile+async.swift +++ b/Sources/ParseSwift/Types/ParseFile+async.swift @@ -21,6 +21,8 @@ public extension ParseFile { - 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`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetch(options: API.Options = []) async throws -> Self { try await withCheckedThrowingContinuation { continuation in @@ -37,6 +39,8 @@ public extension ParseFile { bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)`. - returns: A publisher that eventually produces a single value and then finishes or fails. - throws: `ParseError`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetch(options: API.Options = [], progress: @escaping ((URLSessionDownloadTask, diff --git a/Sources/ParseSwift/Types/ParseFile+combine.swift b/Sources/ParseSwift/Types/ParseFile+combine.swift index 1fb723d09..c1f3f3802 100644 --- a/Sources/ParseSwift/Types/ParseFile+combine.swift +++ b/Sources/ParseSwift/Types/ParseFile+combine.swift @@ -20,6 +20,8 @@ public extension ParseFile { Fetches a file with given url *synchronously*. Publishes when complete. - 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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetchPublisher(options: API.Options = []) -> Future { Future { promise in @@ -35,6 +37,8 @@ public extension ParseFile { 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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetchPublisher(options: API.Options = [], progress: @escaping ((URLSessionDownloadTask, diff --git a/Sources/ParseSwift/Types/ParseFile.swift b/Sources/ParseSwift/Types/ParseFile.swift index 1971ce205..7d5d2c356 100644 --- a/Sources/ParseSwift/Types/ParseFile.swift +++ b/Sources/ParseSwift/Types/ParseFile.swift @@ -162,6 +162,8 @@ extension ParseFile { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after synchronous completion. - throws: A `ParseError` if there was an issue deleting the file. Otherwise it was successful. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func delete(options: API.Options, callbackQueue: DispatchQueue) throws { @@ -190,7 +192,9 @@ extension ParseFile { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: A block that will be called when file deletes or fails. - It should have the following argument signature: `(Result)` + It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func delete(options: API.Options, callbackQueue: DispatchQueue = .main, @@ -262,6 +266,8 @@ extension ParseFile { - parameter callbackQueue: The queue to return to after synchronous completion. Default value of .main. - returns: A saved `ParseFile`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func save(options: API.Options = [], stream: InputStream, @@ -294,6 +300,8 @@ extension ParseFile { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after synchronous completion. - returns: A saved `ParseFile`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func save(options: API.Options = [], callbackQueue: DispatchQueue) throws -> ParseFile { @@ -368,6 +376,8 @@ extension ParseFile { It should have the following argument signature: `(task: URLSessionDownloadTask, bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)`. - returns: A saved `ParseFile`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func save(options: API.Options = [], callbackQueue: DispatchQueue = .main, @@ -448,7 +458,6 @@ extension ParseFile { progress: ((URLSessionTask, Int64, Int64, Int64) -> Void)? = nil, completion: @escaping (Result) -> Void) { var options = options - options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) if let mimeType = mimeType { options.insert(.mimeType(mimeType)) } else { @@ -529,6 +538,8 @@ extension ParseFile { - parameter callbackQueue: The queue to return to after synchronous completion. Default value of .main. - returns: A saved `ParseFile`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func fetch(options: API.Options = [], stream: InputStream, @@ -545,6 +556,7 @@ extension ParseFile { if let tags = tags { options.insert(.tags(tags)) } + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) options = options.union(self.options) return try downloadFileCommand() .executeStream(options: options, @@ -558,6 +570,8 @@ extension ParseFile { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after synchronous completion. - returns: A saved `ParseFile`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func fetch(includeKeys: [String]? = nil, options: API.Options = [], @@ -574,6 +588,7 @@ extension ParseFile { if let tags = tags { options.insert(.tags(tags)) } + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) options = options.union(self.options) return try downloadFileCommand() .execute(options: options, @@ -585,6 +600,8 @@ extension ParseFile { - parameter includeKeys: Currently not used for `ParseFile`. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: A saved `ParseFile`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func fetch(includeKeys: [String]? = nil, options: API.Options = []) throws -> ParseFile { @@ -632,6 +649,8 @@ extension ParseFile { It should have the following argument signature: `(task: URLSessionDownloadTask, bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)`. - returns: A saved `ParseFile`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func fetch(options: API.Options = [], callbackQueue: DispatchQueue = .main, @@ -648,6 +667,7 @@ extension ParseFile { if let tags = tags { options.insert(.tags(tags)) } + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) options = options.union(self.options) return try downloadFileCommand() .execute(options: options, @@ -696,6 +716,8 @@ extension ParseFile { bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)`. - parameter completion: A block that will be called when file fetches or fails. It should have the following argument signature: `(Result)` + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ public func fetch(options: API.Options = [], callbackQueue: DispatchQueue = .main, @@ -713,6 +735,7 @@ extension ParseFile { if let tags = tags { options.insert(.tags(tags)) } + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) options = options.union(self.options) downloadFileCommand() .executeAsync(options: options, diff --git a/Sources/ParseSwift/Types/ParseHealth.swift b/Sources/ParseSwift/Types/ParseHealth.swift index 9dff82b48..1c7becc05 100644 --- a/Sources/ParseSwift/Types/ParseHealth.swift +++ b/Sources/ParseSwift/Types/ParseHealth.swift @@ -15,9 +15,11 @@ public struct ParseHealth: ParseType, Decodable { /** Calls the health check function *synchronously* 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: Returns the status of the server. - - throws: An error of type `ParseError`. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - returns: Returns the status of the server. + - 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`. */ static public func check(options: API.Options = []) throws -> String { var options = options @@ -27,10 +29,10 @@ public struct ParseHealth: ParseType, Decodable { /** Calls the health check 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. - - parameter callbackQueue: The queue to return to after completion. Default value of .main. - - parameter completion: A block that will be called when the health check completes or fails. - It should have the following argument signature: `(Result)`. + - parameter options: A set of header options sent to the server. Defaults to an empty set. + - parameter callbackQueue: The queue to return to after completion. Default value of .main. + - parameter completion: A block that will be called when the health check completes or fails. + It should have the following argument signature: `(Result)`. */ static public func check(options: API.Options = [], callbackQueue: DispatchQueue = .main, diff --git a/Sources/ParseSwift/Types/Pointer+async.swift b/Sources/ParseSwift/Types/Pointer+async.swift index f74af8c03..cbdd43302 100644 --- a/Sources/ParseSwift/Types/Pointer+async.swift +++ b/Sources/ParseSwift/Types/Pointer+async.swift @@ -19,6 +19,8 @@ public extension Pointer { `includeAll` for `Query`. - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: The `ParseObject` with respect to the `Pointer`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetch(includeKeys: [String]? = nil, options: API.Options = []) async throws -> T { diff --git a/Sources/ParseSwift/Types/Pointer+combine.swift b/Sources/ParseSwift/Types/Pointer+combine.swift index 8507672da..6b3974137 100644 --- a/Sources/ParseSwift/Types/Pointer+combine.swift +++ b/Sources/ParseSwift/Types/Pointer+combine.swift @@ -20,6 +20,8 @@ public extension Pointer { `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. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetchPublisher(includeKeys: [String]? = nil, options: API.Options = []) -> Future { diff --git a/Sources/ParseSwift/Types/Pointer.swift b/Sources/ParseSwift/Types/Pointer.swift index 698f49301..d3dcbb2d5 100644 --- a/Sources/ParseSwift/Types/Pointer.swift +++ b/Sources/ParseSwift/Types/Pointer.swift @@ -90,9 +90,13 @@ public extension Pointer { - parameter options: A set of header options sent to the server. Defaults to an empty set. - returns: The `ParseObject` with respect to the `Pointer`. - throws: An error of `ParseError` type. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetch(includeKeys: [String]? = nil, options: API.Options = []) throws -> T { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) let path = API.Endpoint.object(className: className, objectId: objectId) return try API.NonParseBodyCommand(method: .GET, path: path) { (data) -> T in @@ -109,11 +113,15 @@ public extension Pointer { value of .main. - parameter completion: The block to execute when completed. It should have the following argument signature: `(Result)`. + - note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer + desires a different policy, it should be inserted in `options`. */ func fetch(includeKeys: [String]? = nil, options: API.Options = [], callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { + var options = options + options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) let path = API.Endpoint.object(className: className, objectId: objectId) API.NonParseBodyCommand(method: .GET, path: path) { (data) -> T in diff --git a/Sources/ParseSwift/Types/Query.swift b/Sources/ParseSwift/Types/Query.swift index 9f0742262..03f9c932a 100644 --- a/Sources/ParseSwift/Types/Query.swift +++ b/Sources/ParseSwift/Types/Query.swift @@ -65,10 +65,11 @@ public struct QueryConstraint: Encodable, Equatable { public static func == (lhs: QueryConstraint, rhs: QueryConstraint) -> Bool { guard lhs.key == rhs.key, lhs.comparator == rhs.comparator, - AnyEncodable(lhs.value) == AnyEncodable(rhs.value) else { + let lhsObject = lhs.value as? NSObject, + let rhsObject = rhs.value as? NSObject else { return false } - return true + return lhsObject == rhsObject } } diff --git a/Tests/ParseSwiftTests/APICommandTests.swift b/Tests/ParseSwiftTests/APICommandTests.swift index b483cea20..00ca2d6f2 100644 --- a/Tests/ParseSwiftTests/APICommandTests.swift +++ b/Tests/ParseSwiftTests/APICommandTests.swift @@ -117,6 +117,16 @@ class APICommandTests: XCTestCase { } } + func testOptionCacheHasher() throws { + var options = API.Options() + options.insert(.cachePolicy(.returnCacheDataDontLoad)) + XCTAssertFalse(options.contains(.useMasterKey)) + XCTAssertTrue(options.contains(.cachePolicy(.returnCacheDataDontLoad))) + XCTAssertTrue(options.contains(.cachePolicy(.reloadRevalidatingCacheData))) + options.insert(.useMasterKey) + XCTAssertTrue(options.contains(.useMasterKey)) + } + func testExecuteCorrectly() { let originalObject = "test" MockURLProtocol.mockRequests { _ in diff --git a/Tests/ParseSwiftTests/ParseFileManagerTests.swift b/Tests/ParseSwiftTests/ParseFileManagerTests.swift index 1c97edd05..56bac7319 100644 --- a/Tests/ParseSwiftTests/ParseFileManagerTests.swift +++ b/Tests/ParseSwiftTests/ParseFileManagerTests.swift @@ -164,6 +164,41 @@ class ParseFileManagerTests: XCTestCase { wait(for: [expectation1], timeout: 20.0) } + func testDontMoveSameItem() throws { + let dataAsString = "Hello World" + guard let fileManager = ParseFileManager(), + let filePath = fileManager.dataItemPathForPathComponent("test.txt"), + let filePath2 = fileManager.dataItemPathForPathComponent("test.txt") else { + throw ParseError(code: .unknownError, message: "Should have initialized file manage") + } + + let expectation1 = XCTestExpectation(description: "ParseFile async") + fileManager.writeString(dataAsString, filePath: filePath) { error in + guard let error = error else { + guard let readFile = try? String(contentsOf: filePath) else { + XCTFail("Should have read as string") + return + } + XCTAssertEqual(readFile, dataAsString) + + fileManager.moveItem(filePath, toPath: filePath2) { _ in + guard let readFile2 = try? String(contentsOf: filePath2) else { + XCTFail("Should have read as string") + return + } + XCTAssertTrue(FileManager.default.fileExists(atPath: filePath2.relativePath)) + XCTAssertEqual(readFile2, dataAsString) + expectation1.fulfill() + } + return + } + XCTFail(error.localizedDescription) + expectation1.fulfill() + } + + wait(for: [expectation1], timeout: 20.0) + } + func testMoveContentsOfDirectory() throws { let dataAsString = "Hello World" guard let fileManager = ParseFileManager(), diff --git a/Tests/ParseSwiftTests/ParseFileTests.swift b/Tests/ParseSwiftTests/ParseFileTests.swift index c95a48220..7862f2b13 100644 --- a/Tests/ParseSwiftTests/ParseFileTests.swift +++ b/Tests/ParseSwiftTests/ParseFileTests.swift @@ -959,7 +959,6 @@ class ParseFileTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(fetchedFile.url, response.url) XCTAssertNotNil(fetchedFile.localURL) - #if !os(tvOS) // Remove URL so we can check cache MockURLProtocol.removeAll() @@ -970,9 +969,9 @@ class ParseFileTests: XCTestCase { // swiftlint:disable:this type_body_length // More cache tests guard let currentMemoryUsage = URLSession.parse.configuration.urlCache?.currentMemoryUsage, - let currentDiskUsage = URLSession.parse.configuration.urlCache?.currentDiskUsage else { - XCTFail("Should have unwrapped") - return + let currentDiskUsage = URLSession.parse.configuration.urlCache?.currentDiskUsage else { + XCTFail("Should have unwrapped") + return } XCTAssertGreaterThan(currentMemoryUsage, 0) XCTAssertGreaterThan(currentDiskUsage, 0) @@ -982,7 +981,6 @@ class ParseFileTests: XCTestCase { // swiftlint:disable:this type_body_length return } XCTAssertLessThan(updatedMemoryUsage, currentMemoryUsage) - #endif } func testFetchFileProgress() throws {