From af447a62311ff61339d810a3818ac189b116a643 Mon Sep 17 00:00:00 2001 From: Corey Date: Wed, 3 Nov 2021 23:24:39 -0400 Subject: [PATCH] fix: default cache policy for ParseFile uses SDK default (#274) * default cache policy for ParseFile uses SDK default * Failing operation set test * improve operation set comparison --- .codecov.yml | 2 +- CHANGELOG.md | 8 +++++- .../Objects/ParseInstallation+combine.swift | 4 +-- .../Objects/ParseObject+combine.swift | 2 +- .../Objects/ParseUser+combine.swift | 4 +-- .../Operations/ParseOperation.swift | 18 ++++++++----- Sources/ParseSwift/ParseConstants.swift | 2 +- .../Types/ParseConfig+combine.swift | 4 +-- Sources/ParseSwift/Types/ParseFile.swift | 25 +---------------- Sources/ParseSwift/Types/Pointer.swift | 22 ++++++++++++--- .../ParseSwiftTests/ParseOperationTests.swift | 20 ++++++++++++++ Tests/ParseSwiftTests/ParsePointerTests.swift | 27 +++++++++++++++++++ 12 files changed, 91 insertions(+), 47 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 104773319..7009a7cb2 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -6,7 +6,7 @@ coverage: status: patch: default: - target: 36 + target: auto changes: false project: default: diff --git a/CHANGELOG.md b/CHANGELOG.md index 161e741e3..433c03aa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,15 @@ ### main -[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.2.0...main) +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.2.1...main) * _Contributing to this repo? Add info about your change here to be included in the next release_ +### 2.2.1 +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.2.0...2.2.1) + +__Fixes__ +- Set the default cache policy for `ParseFile` to the default policy set when initializing the SDK ([#274](https://github.com/parse-community/Parse-Swift/pull/274)), thanks to [Corey Baker](https://github.com/cbaker6). + ### 2.2.0 [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.1.0...2.2.0) diff --git a/Sources/ParseSwift/Objects/ParseInstallation+combine.swift b/Sources/ParseSwift/Objects/ParseInstallation+combine.swift index 2158e4c50..928acb5e3 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation+combine.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation+combine.swift @@ -12,7 +12,7 @@ import Combine public extension ParseInstallation { - // MARK: Fetchable - Combine + // MARK: Combine /** Fetches the `ParseInstallation` *aynchronously* with the current data from the server and sets an error if one occurs. Publishes when complete. @@ -34,7 +34,6 @@ public extension ParseInstallation { } } - // MARK: Savable - Combine /** Saves the `ParseInstallation` *asynchronously* and publishes when complete. @@ -51,7 +50,6 @@ public extension ParseInstallation { } } - // MARK: Deletable - Combine /** Deletes the `ParseInstallation` *asynchronously* and publishes when complete. diff --git a/Sources/ParseSwift/Objects/ParseObject+combine.swift b/Sources/ParseSwift/Objects/ParseObject+combine.swift index 08b52fc08..2e9b91ab5 100644 --- a/Sources/ParseSwift/Objects/ParseObject+combine.swift +++ b/Sources/ParseSwift/Objects/ParseObject+combine.swift @@ -12,7 +12,7 @@ import Combine public extension ParseObject { - // MARK: Fetchable - Combine + // MARK: Combine /** Fetches the `ParseObject` *aynchronously* with the current data from the server and sets an error if one occurs. Publishes when complete. diff --git a/Sources/ParseSwift/Objects/ParseUser+combine.swift b/Sources/ParseSwift/Objects/ParseUser+combine.swift index 28b399a5f..35ee7c586 100644 --- a/Sources/ParseSwift/Objects/ParseUser+combine.swift +++ b/Sources/ParseSwift/Objects/ParseUser+combine.swift @@ -135,7 +135,7 @@ public extension ParseUser { } } - // MARK: Fetchable - Combine + // MARK: Combine /** Fetches the `ParseUser` *aynchronously* with the current data from the server and sets an error if one occurs. Publishes when complete. @@ -157,7 +157,6 @@ public extension ParseUser { } } - // MARK: Savable - Combine /** Saves the `ParseUser` *asynchronously* and publishes when complete. @@ -186,7 +185,6 @@ public extension ParseUser { } } - // MARK: Deletable - Combine /** Deletes the `ParseUser` *asynchronously* and publishes when complete. diff --git a/Sources/ParseSwift/Operations/ParseOperation.swift b/Sources/ParseSwift/Operations/ParseOperation.swift index d109351ba..2e5063667 100644 --- a/Sources/ParseSwift/Operations/ParseOperation.swift +++ b/Sources/ParseSwift/Operations/ParseOperation.swift @@ -38,13 +38,7 @@ public struct ParseOperation: Savable where T: ParseObject { 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 - } - } else { + if !isEqual(target[keyPath: key.1], to: value) { mutableOperation.operations[key.0] = value mutableOperation.target?[keyPath: key.1] = value } @@ -403,6 +397,16 @@ public struct ParseOperation: Savable where T: ParseObject { try value.encode(to: encoder) } } + + func isEqual(_ lhs: Encodable, to rhs: Encodable) -> Bool { + guard let lhsData = try? ParseCoding.parseEncoder().encode(lhs), + let lhsString = String(data: lhsData, encoding: .utf8), + let rhsData = try? ParseCoding.parseEncoder().encode(rhs), + let rhsString = String(data: rhsData, encoding: .utf8) else { + return false + } + return lhsString == rhsString + } } // MARK: Savable diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index 1a4b99eb5..c65c461ce 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.2.0" + static let version = "2.2.1" static let fileManagementDirectory = "parse/" static let fileManagementPrivateDocumentsDirectory = "Private Documents/" static let fileManagementLibraryDirectory = "Library/" diff --git a/Sources/ParseSwift/Types/ParseConfig+combine.swift b/Sources/ParseSwift/Types/ParseConfig+combine.swift index a2688c68e..347b03f6b 100644 --- a/Sources/ParseSwift/Types/ParseConfig+combine.swift +++ b/Sources/ParseSwift/Types/ParseConfig+combine.swift @@ -12,7 +12,7 @@ import Combine public extension ParseConfig { - // MARK: Fetchable - Combine + // MARK: Combine /** Fetch the Config *asynchronously*. Publishes when complete. @@ -28,8 +28,6 @@ public extension ParseConfig { } } - // MARK: Savable - Combine - /** Update the Config *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. diff --git a/Sources/ParseSwift/Types/ParseFile.swift b/Sources/ParseSwift/Types/ParseFile.swift index 7d5d2c356..53b4c4406 100644 --- a/Sources/ParseSwift/Types/ParseFile.swift +++ b/Sources/ParseSwift/Types/ParseFile.swift @@ -266,15 +266,12 @@ 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, callbackQueue: DispatchQueue = .main, progress: ((URLSessionTask, Int64, Int64, Int64) -> Void)? = nil) throws { var options = options - options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) if let mimeType = mimeType { options.insert(.mimeType(mimeType)) } else { @@ -300,13 +297,10 @@ 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 { var options = options - options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) if let mimeType = mimeType { options.insert(.mimeType(mimeType)) } else { @@ -376,14 +370,11 @@ 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, progress: ((URLSessionTask, Int64, Int64, Int64) -> Void)?) throws -> ParseFile { var options = options - options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) if let mimeType = mimeType { options.insert(.mimeType(mimeType)) } else { @@ -538,8 +529,6 @@ 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, @@ -556,7 +545,6 @@ 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, @@ -570,8 +558,6 @@ 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 = [], @@ -588,7 +574,6 @@ 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, @@ -600,8 +585,6 @@ 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 { @@ -649,8 +632,6 @@ 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, @@ -667,7 +648,6 @@ 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, @@ -715,9 +695,7 @@ extension ParseFile { It should have the following argument signature: `(task: URLSessionDownloadTask, 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`. + It should have the following argument signature: `(Result)`. */ public func fetch(options: API.Options = [], callbackQueue: DispatchQueue = .main, @@ -735,7 +713,6 @@ 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/Pointer.swift b/Sources/ParseSwift/Types/Pointer.swift index d3dcbb2d5..cadd9c37c 100644 --- a/Sources/ParseSwift/Types/Pointer.swift +++ b/Sources/ParseSwift/Types/Pointer.swift @@ -12,12 +12,10 @@ protocol ParsePointer: Encodable { extension ParsePointer { /** Determines if two objects have the same objectId. - - parameter as: Object to compare. - - returns: Returns a `true` if the other object has the same `objectId` or `false` if unsuccessful. */ - public func hasSameObjectId(as other: ParsePointer) -> Bool { + func hasSameObjectId(as other: ParsePointer) -> Bool { return other.className == className && other.objectId == objectId } } @@ -82,6 +80,24 @@ public struct Pointer: ParsePointer, Fetchable, Encodable, Hasha public extension Pointer { + /** + Determines if a `ParseObject` and `Pointer`have the same `objectId`. + - parameter as: `ParseObject` to compare. + - returns: Returns a `true` if the other object has the same `objectId` or `false` if unsuccessful. + */ + func hasSameObjectId(as other: T) -> Bool { + return other.className == className && other.objectId == objectId + } + + /** + Determines if two `Pointer`'s have the same `objectId`. + - parameter as: `Pointer` to compare. + - returns: Returns a `true` if the other object has the same `objectId` or `false` if unsuccessful. + */ + func hasSameObjectId(as other: Self) -> Bool { + return other.className == className && other.objectId == objectId + } + /** Fetches the `ParseObject` *synchronously* with the current data from the server and sets an error if one occurs. - parameter includeKeys: The name(s) of the key(s) to include that are diff --git a/Tests/ParseSwiftTests/ParseOperationTests.swift b/Tests/ParseSwiftTests/ParseOperationTests.swift index 4f20ffa91..39c40ffd6 100644 --- a/Tests/ParseSwiftTests/ParseOperationTests.swift +++ b/Tests/ParseSwiftTests/ParseOperationTests.swift @@ -503,6 +503,15 @@ class ParseOperationTests: XCTestCase { let decoded = try XCTUnwrap(String(data: encoded, encoding: .utf8)) XCTAssertEqual(decoded, expected) XCTAssertEqual(operations.target?.score, 15) + var level = Level(level: 12) + level.members = ["hello", "world"] + let operations2 = try score.operation.set(("previous", \.previous), value: [level]) + let expected2 = "{\"previous\":[{\"level\":12,\"members\":[\"hello\",\"world\"]}]}" + let encoded2 = try ParseCoding.parseEncoder() + .encode(operations2) + let decoded2 = try XCTUnwrap(String(data: encoded2, encoding: .utf8)) + XCTAssertEqual(decoded2, expected2) + XCTAssertEqual(operations2.target?.previous, [level]) } #endif @@ -516,6 +525,17 @@ class ParseOperationTests: XCTestCase { .encode(operations) let decoded = try XCTUnwrap(String(data: encoded, encoding: .utf8)) XCTAssertEqual(decoded, expected) + XCTAssertEqual(operations.target?.objectId, "test") + var level = Level(level: 12) + level.members = ["hello", "world"] + score.previous = [level] + let expected2 = "{}" + let operations2 = try score.operation.set(("previous", \.previous), value: [level]) + let encoded2 = try ParseCoding.parseEncoder() + .encode(operations2) + let decoded2 = try XCTUnwrap(String(data: encoded2, encoding: .utf8)) + XCTAssertEqual(decoded2, expected2) + XCTAssertEqual(operations2.target?.previous, [level]) } func testUnchangedSet() throws { diff --git a/Tests/ParseSwiftTests/ParsePointerTests.swift b/Tests/ParseSwiftTests/ParsePointerTests.swift index 6f83f0540..e10c6fec7 100644 --- a/Tests/ParseSwiftTests/ParsePointerTests.swift +++ b/Tests/ParseSwiftTests/ParsePointerTests.swift @@ -92,6 +92,33 @@ class ParsePointerTests: XCTestCase { XCTAssertEqual(pointer.objectId, score.objectId) } + func testHasSameObjectId() throws { + var score = GameScore(score: 10) + let objectId = "yarr" + score.objectId = objectId + let pointer = try score.toPointer() + let pointer2 = pointer + XCTAssertTrue(pointer.hasSameObjectId(as: pointer2)) + XCTAssertTrue(pointer.hasSameObjectId(as: score)) + score.objectId = "hello" + let pointer3 = try score.toPointer() + XCTAssertFalse(pointer.hasSameObjectId(as: pointer3)) + XCTAssertFalse(pointer.hasSameObjectId(as: score)) + } + + func testPointerEquality() throws { + var score = GameScore(score: 10) + let objectId = "yarr" + score.objectId = objectId + let pointer = try score.toPointer() + var score2 = GameScore(score: 10) + score2.objectId = objectId + var pointer2 = try score2.toPointer() + XCTAssertEqual(pointer, pointer2) + pointer2.objectId = "hello" + XCTAssertNotEqual(pointer, pointer2) + } + func testDetectCircularDependency() throws { var score = GameScore(score: 10) score.objectId = "nice"