diff --git a/CHANGELOG.md b/CHANGELOG.md index 935cda0fb..e19037527 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,15 @@ # Parse-Swift Changelog ### main -[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.3.1...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift) +[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.3.2...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift) * _Contributing to this repo? Add info about your change here to be included in the next release_ +### 5.3.2 +[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.3.1...5.3.2), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.3.2/documentation/parseswift) + +__Fixes__ +* ParseFile id creation should be consistent to ensure proper hashing ([#83](https://github.com/netreconlab/Parse-Swift/pull/83)), thanks to [Corey Baker](https://github.com/cbaker6). + ### 5.3.1 [Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.3.0...5.3.1), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.3.1/documentation/parseswift) diff --git a/Sources/ParseSwift/API/API+Command+async.swift b/Sources/ParseSwift/API/API+Command+async.swift index cf445acc4..b398dd998 100644 --- a/Sources/ParseSwift/API/API+Command+async.swift +++ b/Sources/ParseSwift/API/API+Command+async.swift @@ -17,7 +17,7 @@ internal extension API.Command { func prepareURLRequest(options: API.Options, batching: Bool = false, childObjects: [String: PointerType]? = nil, - childFiles: [UUID: ParseFile]? = nil) async -> Result { + childFiles: [String: ParseFile]? = nil) async -> Result { await withCheckedContinuation { continuation in self.prepareURLRequest(options: options, batching: batching, @@ -33,7 +33,7 @@ internal extension API.Command { callbackQueue: DispatchQueue, notificationQueue: DispatchQueue? = nil, childObjects: [String: PointerType]? = nil, - childFiles: [UUID: ParseFile]? = nil, + childFiles: [String: ParseFile]? = nil, allowIntermediateResponses: Bool = false, uploadProgress: ((URLSessionTask, Int64, Int64, Int64) -> Void)? = nil, downloadProgress: ((URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? = nil) async throws -> U { diff --git a/Sources/ParseSwift/API/API+Command.swift b/Sources/ParseSwift/API/API+Command.swift index 11498fab5..1a55abb72 100644 --- a/Sources/ParseSwift/API/API+Command.swift +++ b/Sources/ParseSwift/API/API+Command.swift @@ -53,7 +53,7 @@ internal extension API { func executeStream(options: API.Options, callbackQueue: DispatchQueue, childObjects: [String: PointerType]? = nil, - childFiles: [UUID: ParseFile]? = nil, + childFiles: [String: ParseFile]? = nil, uploadProgress: ((URLSessionTask, Int64, Int64, Int64) -> Void)? = nil, stream: InputStream, completion: @escaping (ParseError?) -> Void) { @@ -94,7 +94,7 @@ internal extension API { callbackQueue: DispatchQueue, notificationQueue: DispatchQueue? = nil, childObjects: [String: PointerType]? = nil, - childFiles: [UUID: ParseFile]? = nil, + childFiles: [String: ParseFile]? = nil, allowIntermediateResponses: Bool = false, uploadProgress: ((URLSessionTask, Int64, Int64, Int64) -> Void)? = nil, downloadProgress: ((URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? = nil, @@ -252,7 +252,7 @@ internal extension API { func prepareURLRequest(options: API.Options, batching: Bool = false, childObjects: [String: PointerType]? = nil, - childFiles: [UUID: ParseFile]? = nil, + childFiles: [String: ParseFile]? = nil, completion: @escaping(Result) -> Void) { let params = self.params?.getURLQueryItems() Task { diff --git a/Sources/ParseSwift/API/API+NonParseBodyCommand.swift b/Sources/ParseSwift/API/API+NonParseBodyCommand.swift index c1a77421e..9858e57fb 100644 --- a/Sources/ParseSwift/API/API+NonParseBodyCommand.swift +++ b/Sources/ParseSwift/API/API+NonParseBodyCommand.swift @@ -168,7 +168,7 @@ internal extension API.NonParseBodyCommand { transaction: Bool, objectsSavedBeforeThisOne: [String: PointerType]?, // swiftlint:disable:next line_length - filesSavedBeforeThisOne: [UUID: ParseFile]?) async throws -> RESTBatchCommandTypeEncodablePointer { + filesSavedBeforeThisOne: [String: ParseFile]?) async throws -> RESTBatchCommandTypeEncodablePointer { let defaultACL = try? await ParseACL.defaultACL() let batchCommands = try objects.compactMap { (object) -> API.BatchCommand? in guard var objectable = object as? Objectable else { diff --git a/Sources/ParseSwift/Coding/ParseEncoder.swift b/Sources/ParseSwift/Coding/ParseEncoder.swift index 79795eeb7..7be469738 100644 --- a/Sources/ParseSwift/Coding/ParseEncoder.swift +++ b/Sources/ParseSwift/Coding/ParseEncoder.swift @@ -110,7 +110,7 @@ public struct ParseEncoder { acl: ParseACL? = nil, batching: Bool = false, objectsSavedBeforeThisOne: [String: PointerType]? = nil, - filesSavedBeforeThisOne: [UUID: ParseFile]? = nil) throws -> Data { + filesSavedBeforeThisOne: [String: ParseFile]? = nil) throws -> Data { var keysToSkip = SkipKeys.none.keys() if batching { keysToSkip = SkipKeys.object.keys() @@ -159,9 +159,9 @@ public struct ParseEncoder { acl: ParseACL? = nil, collectChildren: Bool, objectsSavedBeforeThisOne: [String: PointerType]?, - filesSavedBeforeThisOne: [UUID: ParseFile]?) throws -> (encoded: Data, - unique: PointerType?, - unsavedChildren: [Encodable]) { + filesSavedBeforeThisOne: [String: ParseFile]?) throws -> (encoded: Data, + unique: PointerType?, + unsavedChildren: [Encodable]) { let keysToSkip: Set! if !Parse.configuration.isRequiringCustomObjectIds { keysToSkip = SkipKeys.object.keys() @@ -188,7 +188,7 @@ public struct ParseEncoder { batching: Bool, collectChildren: Bool, objectsSavedBeforeThisOne: [String: PointerType]?, - filesSavedBeforeThisOne: [UUID: ParseFile]?) throws -> (encoded: Data, unique: PointerType?, unsavedChildren: [Encodable]) { + filesSavedBeforeThisOne: [String: ParseFile]?) throws -> (encoded: Data, unique: PointerType?, unsavedChildren: [Encodable]) { let keysToSkip: Set! if !Parse.configuration.isRequiringCustomObjectIds { keysToSkip = SkipKeys.object.keys() @@ -218,12 +218,11 @@ internal class _ParseEncoder: JSONEncoder, Encoder { let dictionary: NSMutableDictionary let skippedKeys: Set var uniquePointer: PointerType? - var uniqueFiles = Set() var newObjects = [Encodable]() var collectChildren = false var batching = false var objectsSavedBeforeThisOne: [String: PointerType]? - var filesSavedBeforeThisOne: [UUID: ParseFile]? + var filesSavedBeforeThisOne: [String: ParseFile]? /// The encoder's storage. var storage: _ParseEncodingStorage var ignoreSkipKeys = false @@ -280,7 +279,7 @@ internal class _ParseEncoder: JSONEncoder, Encoder { collectChildren: Bool, uniquePointer: PointerType?, objectsSavedBeforeThisOne: [String: PointerType]?, - filesSavedBeforeThisOne: [UUID: ParseFile]?) throws -> (encoded: Data, unique: PointerType?, unsavedChildren: [Encodable]) { + filesSavedBeforeThisOne: [String: ParseFile]?) throws -> (encoded: Data, unique: PointerType?, unsavedChildren: [Encodable]) { self.acl = acl let encoder = _ParseEncoder(codingPath: codingPath, dictionary: dictionary, skippingKeys: skippedKeys) encoder.outputFormatting = outputFormatting @@ -364,7 +363,7 @@ internal class _ParseEncoder: JSONEncoder, Encoder { if let uniquePointer = self.uniquePointer, uniquePointer.hasSameObjectId(as: pointer) { throw ParseError(code: .otherCause, - message: "Found a circular dependency when encoding.") + message: "Found a circular dependency when encoding objects. The object: \(pointer) cannot have the same objectId as: \(uniquePointer)") } valueToEncode = pointer } else if let object = value as? Objectable { @@ -373,7 +372,7 @@ internal class _ParseEncoder: JSONEncoder, Encoder { if let uniquePointer = self.uniquePointer, uniquePointer.hasSameObjectId(as: pointer) { throw ParseError(code: .otherCause, - message: "Found a circular dependency when encoding.") + message: "Found a circular dependency when encoding objects. The object: \(pointer) cannot have the same objectId as: \(uniquePointer)") } valueToEncode = pointer } else { @@ -401,10 +400,6 @@ internal class _ParseEncoder: JSONEncoder, Encoder { func deepFindAndReplaceParseFiles(_ value: ParseFile) throws -> Encodable? { var valueToEncode: Encodable? if value.isSaved { - if self.uniqueFiles.contains(value) { - throw ParseError(code: .otherCause, message: "Found a circular dependency when encoding.") - } - self.uniqueFiles.insert(value) if !self.collectChildren { valueToEncode = value } @@ -413,13 +408,12 @@ internal class _ParseEncoder: JSONEncoder, Encoder { if let updatedFile = self.filesSavedBeforeThisOne?[value.id] { valueToEncode = updatedFile } else { - // New object needs to be saved before it can be stored + // New file needs to be saved before it can be stored self.newObjects.append(value) } } else if let currentFile = self.filesSavedBeforeThisOne?[value.id] { valueToEncode = currentFile } else if dictionary.count > 0 { - // Only top level objects can be saved without a pointer throw ParseError(code: .otherCause, message: "Error. Could not resolve unsaved file while encoding.") } } @@ -1035,7 +1029,7 @@ private class _ParseReferencingEncoder: _ParseEncoder { // MARK: - Initialization /// Initializes `self` by referencing the given array container in the given encoder. - init(referencing encoder: _ParseEncoder, at index: Int, wrapping array: NSMutableArray, skippingKeys: Set, collectChildren: Bool, objectsSavedBeforeThisOne: [String: PointerType]?, filesSavedBeforeThisOne: [UUID: ParseFile]?) { + init(referencing encoder: _ParseEncoder, at index: Int, wrapping array: NSMutableArray, skippingKeys: Set, collectChildren: Bool, objectsSavedBeforeThisOne: [String: PointerType]?, filesSavedBeforeThisOne: [String: ParseFile]?) { self.encoder = encoder self.reference = .array(array, index) super.init(codingPath: encoder.codingPath, dictionary: NSMutableDictionary(), skippingKeys: skippingKeys) @@ -1046,7 +1040,7 @@ private class _ParseReferencingEncoder: _ParseEncoder { } /// Initializes `self` by referencing the given dictionary container in the given encoder. - init(referencing encoder: _ParseEncoder, key: CodingKey, wrapping dictionary: NSMutableDictionary, skippingKeys: Set, collectChildren: Bool, objectsSavedBeforeThisOne: [String: PointerType]?, filesSavedBeforeThisOne: [UUID: ParseFile]?) { + init(referencing encoder: _ParseEncoder, key: CodingKey, wrapping dictionary: NSMutableDictionary, skippingKeys: Set, collectChildren: Bool, objectsSavedBeforeThisOne: [String: PointerType]?, filesSavedBeforeThisOne: [String: ParseFile]?) { self.encoder = encoder self.reference = .dictionary(dictionary, key.stringValue) super.init(codingPath: encoder.codingPath, dictionary: dictionary, skippingKeys: skippingKeys) diff --git a/Sources/ParseSwift/Objects/ParseInstallation+async.swift b/Sources/ParseSwift/Objects/ParseInstallation+async.swift index 29b43c6e6..bd6c19041 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation+async.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation+async.swift @@ -372,7 +372,7 @@ internal extension Sequence where Element: ParseInstallation { var options = options options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) var childObjects = [String: PointerType]() - var childFiles = [UUID: ParseFile]() + var childFiles = [String: ParseFile]() var commands = [API.Command]() let objects = map { $0 } for object in objects { @@ -381,13 +381,15 @@ internal extension Sequence where Element: ParseInstallation { isShouldReturnIfChildObjectsFound: transaction) try savedChildObjects.forEach {(key, value) in guard childObjects[key] == nil else { - throw ParseError(code: .otherCause, message: "circular dependency") + throw ParseError(code: .otherCause, + message: "Found a circular dependency in ParseInstallation.") } childObjects[key] = value } try savedChildFiles.forEach {(key, value) in guard childFiles[key] == nil else { - throw ParseError(code: .otherCause, message: "circular dependency") + throw ParseError(code: .otherCause, + message: "Found a circular dependency in ParseInstallation.") } childFiles[key] = value } diff --git a/Sources/ParseSwift/Objects/ParseObject+async.swift b/Sources/ParseSwift/Objects/ParseObject+async.swift index 658eb098a..5390cbbcf 100644 --- a/Sources/ParseSwift/Objects/ParseObject+async.swift +++ b/Sources/ParseSwift/Objects/ParseObject+async.swift @@ -294,7 +294,7 @@ internal extension ParseObject { // swiftlint:disable:next function_body_length func ensureDeepSave(options: API.Options = [], isShouldReturnIfChildObjectsFound: Bool = false) async throws -> ([String: PointerType], - [UUID: ParseFile]) { + [String: ParseFile]) { var options = options // Remove any caching policy added by the developer as fresh data @@ -302,7 +302,7 @@ internal extension ParseObject { options.remove(.cachePolicy(.reloadIgnoringLocalCacheData)) options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) var objectsFinishedSaving = [String: PointerType]() - var filesFinishedSaving = [UUID: ParseFile]() + var filesFinishedSaving = [String: ParseFile]() let defaultACL = try? await ParseACL.defaultACL() do { let object = try ParseCoding.parseEncoder() @@ -414,7 +414,7 @@ internal extension Sequence where Element: ParseObject { var options = options options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) var childObjects = [String: PointerType]() - var childFiles = [UUID: ParseFile]() + var childFiles = [String: ParseFile]() var commands = [API.Command]() let objects = map { $0 } for object in objects { @@ -423,13 +423,15 @@ internal extension Sequence where Element: ParseObject { isShouldReturnIfChildObjectsFound: transaction) try savedChildObjects.forEach {(key, value) in guard childObjects[key] == nil else { - throw ParseError(code: .otherCause, message: "circular dependency") + throw ParseError(code: .otherCause, + message: "Found a circular dependency in ParseObject.") } childObjects[key] = value } try savedChildFiles.forEach {(key, value) in guard childFiles[key] == nil else { - throw ParseError(code: .otherCause, message: "circular dependency") + throw ParseError(code: .otherCause, + message: "Found a circular dependency in ParseObject.") } childFiles[key] = value } @@ -478,7 +480,7 @@ internal extension ParseEncodable { func saveAll(objects: [ParseEncodable], transaction: Bool = configuration.isUsingTransactions, objectsSavedBeforeThisOne: [String: PointerType]?, - filesSavedBeforeThisOne: [UUID: ParseFile]?, + filesSavedBeforeThisOne: [String: ParseFile]?, options: API.Options = [], callbackQueue: DispatchQueue = .main) async throws -> [(Result)] { try await API.NonParseBodyCommand diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index 130188179..8e9617000 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -167,9 +167,10 @@ public extension ParseObject { } /** - A computed property that is the same value as `objectId` and makes it easy to use `ParseObject`'s + A computed property that is a unique identifier and makes it easy to use `ParseObject`'s as models in MVVM and SwiftUI. - - note: `id` allows `ParseObjects`'s to be used even when they are unsaved and do not have an `objectId`. + - note: `id` allows `ParseObject`'s to be used even when they are not saved and do not have an `objectId`. + - important: `id` will have the same value as `objectId` when a `ParseObject` is saved. */ var id: String { objectId ?? UUID().uuidString diff --git a/Sources/ParseSwift/Objects/ParseUser+async.swift b/Sources/ParseSwift/Objects/ParseUser+async.swift index 028a3fd1f..1fbb801ec 100644 --- a/Sources/ParseSwift/Objects/ParseUser+async.swift +++ b/Sources/ParseSwift/Objects/ParseUser+async.swift @@ -603,7 +603,7 @@ internal extension Sequence where Element: ParseUser { var options = options options.insert(.cachePolicy(.reloadIgnoringLocalCacheData)) var childObjects = [String: PointerType]() - var childFiles = [UUID: ParseFile]() + var childFiles = [String: ParseFile]() var commands = [API.Command]() let objects = map { $0 } for object in objects { @@ -612,13 +612,15 @@ internal extension Sequence where Element: ParseUser { isShouldReturnIfChildObjectsFound: transaction) try savedChildObjects.forEach {(key, value) in guard childObjects[key] == nil else { - throw ParseError(code: .otherCause, message: "circular dependency") + throw ParseError(code: .otherCause, + message: "Found a circular dependency in ParseUser.") } childObjects[key] = value } try savedChildFiles.forEach {(key, value) in guard childFiles[key] == nil else { - throw ParseError(code: .otherCause, message: "circular dependency") + throw ParseError(code: .otherCause, + message: "Found a circular dependency in ParseUser.") } childFiles[key] = value } diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index 88fd42a68..1abcdbf48 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 = "5.3.1" + static let version = "5.3.2" static let fileManagementDirectory = "parse/" static let fileManagementPrivateDocumentsDirectory = "Private Documents/" static let fileManagementLibraryDirectory = "Library/" diff --git a/Sources/ParseSwift/Types/ParseFile.swift b/Sources/ParseSwift/Types/ParseFile.swift index ab0e84bef..70c6bcf5b 100644 --- a/Sources/ParseSwift/Types/ParseFile.swift +++ b/Sources/ParseSwift/Types/ParseFile.swift @@ -7,7 +7,7 @@ import FoundationNetworking A `ParseFile` object representes a file of binary data stored on the Parse server. This can be a image, video, or anything else that an application needs to reference in a non-relational way. */ -public struct ParseFile: Fileable, Savable, Deletable, Hashable { +public struct ParseFile: Fileable, Savable, Deletable, Hashable, Identifiable { internal static var type: String { "File" @@ -22,7 +22,27 @@ public struct ParseFile: Fileable, Savable, Deletable, Hashable { && data == nil } - public var id: UUID + /** + A computed property that is a unique identifier and makes it easy to use `ParseFile`'s + as models in MVVM and SwiftUI. + - note: `id` allows `ParseFile`'s to be used even when they are not saved. + - important: `id` will have the same value as `name` when a `ParseFile` is saved. + */ + public var id: String { + guard isSaved else { + guard let cloudURL = cloudURL else { + guard let localURL = localURL else { + guard let data = data else { + return name + } + return "\(name)_\(data)" + } + return combineName(with: localURL) + } + return combineName(with: cloudURL) + } + return name + } /** The name of the file. @@ -87,7 +107,6 @@ public struct ParseFile: Fileable, Savable, Deletable, Hashable { self.metadata = metadata self.tags = tags self.options = options - self.id = UUID() } /** @@ -113,7 +132,6 @@ public struct ParseFile: Fileable, Savable, Deletable, Hashable { self.metadata = metadata self.tags = tags self.options = options - self.id = UUID() } /** @@ -139,7 +157,10 @@ public struct ParseFile: Fileable, Savable, Deletable, Hashable { self.metadata = metadata self.tags = tags self.options = options - self.id = UUID() + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(self.id) } enum CodingKeys: String, CodingKey { @@ -151,6 +172,10 @@ public struct ParseFile: Fileable, Savable, Deletable, Hashable { // MARK: Helper Methods (internal) extension ParseFile { + func combineName(with url: URL) -> String { + "\(name)_\(url)" + } + func setDefaultOptions(_ options: API.Options) -> API.Options { var options = options if let mimeType = mimeType { @@ -208,7 +233,6 @@ extension ParseFile { let values = try decoder.container(keyedBy: CodingKeys.self) url = try values.decodeIfPresent(URL.self, forKey: .url) name = try values.decode(String.self, forKey: .name) - id = UUID() } } diff --git a/Tests/ParseSwiftTests/ParseFileTests.swift b/Tests/ParseSwiftTests/ParseFileTests.swift index eb393c420..d7801315e 100644 --- a/Tests/ParseSwiftTests/ParseFileTests.swift +++ b/Tests/ParseSwiftTests/ParseFileTests.swift @@ -164,31 +164,53 @@ class ParseFileTests: XCTestCase { // swiftlint:disable:this type_body_length "localId should remain the same no matter how many times the getter is called") } - func testFileEquality() async throws { + func testFileHashable() async throws { guard let sampleData = "Hello World".data(using: .utf8) else { throw ParseError(code: .otherCause, message: "Should have converted to data") } + guard let sampleData2 = "Bye World".data(using: .utf8) else { + throw ParseError(code: .otherCause, message: "Should have converted to data") + } + guard let url1 = URL(string: "https://parseplatform.org/img/logo.svg"), let url2 = URL(string: "https://parseplatform.org/img/logo2.svg") else { throw ParseError(code: .otherCause, message: "Should have created urls") } var parseFile1 = ParseFile(name: "sampleData.txt", data: sampleData) - parseFile1.url = url1 var parseFile2 = ParseFile(name: "sampleData2.txt", data: sampleData) - parseFile2.url = url2 var parseFile3 = ParseFile(name: "sampleData3.txt", data: sampleData) + var parseFile4 = ParseFile(name: "sampleData.txt", data: sampleData2) + XCTAssertEqual(parseFile1.id, parseFile1.id, "no urls, but names and data should be the same") + XCTAssertNotEqual(parseFile1.id, parseFile2.id, "no urls, but names and data are different") + XCTAssertNotEqual(parseFile1.id, parseFile4.id, "no urls, but names are the same, data is different") + parseFile1.data = nil + parseFile2.data = nil + parseFile4.data = nil + XCTAssertNotEqual(parseFile1.id, parseFile2.id, "no urls or data, but are different") + XCTAssertEqual(parseFile1.id, parseFile4.id, "no urls or data, but names are the same") + parseFile1.data = sampleData + parseFile2.data = sampleData + parseFile3.data = sampleData2 + parseFile1.url = url1 + parseFile2.url = url2 parseFile3.url = url1 - XCTAssertNotEqual(parseFile1, parseFile2, "different urls, url takes precedence over localId") - XCTAssertEqual(parseFile1, parseFile3, "same urls") + XCTAssertNotEqual(parseFile1.id, parseFile2.id, "different urls, url takes precedence over localId") + XCTAssertEqual(parseFile1.id, parseFile1.id, "same urls and same names") + XCTAssertNotEqual(parseFile1.id, parseFile3.id, "same urls, but different names") parseFile1.url = nil parseFile2.url = nil - XCTAssertNotEqual(parseFile1, parseFile2, "no urls, but localIds shoud be different") - let uuid = UUID() - parseFile1.id = uuid - parseFile2.id = uuid - XCTAssertEqual(parseFile1, parseFile2, "no urls, but localIds shoud be the same") + parseFile3.url = nil + XCTAssertNotEqual(parseFile1.id, parseFile2.id, "no urls, but localIds should be different") + parseFile1.cloudURL = url1 + parseFile2.cloudURL = url2 + parseFile3.cloudURL = url1 + XCTAssertEqual(parseFile1.id, parseFile1.id, "no urls, but cloud urls and names are the same") + XCTAssertNotEqual(parseFile1.id, parseFile2.id, "no urls, cloud urls and name are different") + XCTAssertNotEqual(parseFile1.id, parseFile3.id, "no urls, but cloud urls are the same, but names are different") + parseFile4.cloudURL = url2 + XCTAssertNotEqual(parseFile1.id, parseFile4.id, "no urls, cloud urls are different, but names are the same") } func testDebugString() async throws { diff --git a/Tests/ParseSwiftTests/ParseQueryTests.swift b/Tests/ParseSwiftTests/ParseQueryTests.swift index 67c3f50d2..1bb40e4bf 100644 --- a/Tests/ParseSwiftTests/ParseQueryTests.swift +++ b/Tests/ParseSwiftTests/ParseQueryTests.swift @@ -511,10 +511,10 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length return } - XCTAssertEqual(jsonSkip, parseSkip, "Parse shoud always match JSON") - XCTAssertEqual(jsonMethod, parseMethod, "Parse shoud always match JSON") - XCTAssertEqual(jsonLimit, parseLimit, "Parse shoud always match JSON") - XCTAssertEqual(jsonWhere, parseWhere, "Parse shoud always match JSON") + XCTAssertEqual(jsonSkip, parseSkip, "Parse should always match JSON") + XCTAssertEqual(jsonMethod, parseMethod, "Parse should always match JSON") + XCTAssertEqual(jsonLimit, parseLimit, "Parse should always match JSON") + XCTAssertEqual(jsonWhere, parseWhere, "Parse should always match JSON") } func findAsync(scoreOnServer: GameScore, callbackQueue: DispatchQueue) {