diff --git a/.codecov.yml b/.codecov.yml index 5148b8498..2517c8e20 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -5,7 +5,7 @@ coverage: status: patch: default: - target: auto + target: 72 changes: false project: default: diff --git a/CHANGELOG.md b/CHANGELOG.md index 39fc970f5..1f17b7d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,21 @@ # Parse-Swift Changelog ### main -[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.1.7...main) +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.2.0...main) * _Contributing to this repo? Add info about your change here to be included in the next release_ -### 1.1.7 -[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.1.6...1.1.7) +### 1.2.0 +[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.1.6...1.2.0) + +__Breaking changes__ +- Allows return types to be specified for `ParseCloud`, query `hint`, and `explain` (see playgrounds for examples). Changed functionality of synchronous `query.first()`. It use to return nil if no values are found. Now it will throw an error if none are found. ([#92](https://github.com/parse-community/Parse-Swift/pull/92)), thanks to [Corey Baker](https://github.com/cbaker6). __New features__ - Add transaction support to batch saveAll and deleteAll ([#89](https://github.com/parse-community/Parse-Swift/pull/89)), thanks to [Corey Baker](https://github.com/cbaker6). - Add modifiers to containsString, hasPrefix, hasSuffix ([#85](https://github.com/parse-community/Parse-Swift/pull/85)), thanks to [Corey Baker](https://github.com/cbaker6). __Improvements__ +- Better error reporting when decode errors occur ([#92](https://github.com/parse-community/Parse-Swift/pull/92)), thanks to [Corey Baker](https://github.com/cbaker6). - Can use a variadic version of exclude. Added examples of select and exclude query in playgrounds ([#88](https://github.com/parse-community/Parse-Swift/pull/88)), thanks to [Corey Baker](https://github.com/cbaker6). ### 1.1.6 diff --git a/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift index 01b5fd0fd..79623268a 100644 --- a/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift @@ -15,6 +15,10 @@ initializeParse() //: Create your own value typed `ParseCloud` type. struct Cloud: ParseCloud { + + //: Return type of your Cloud Function + typealias ReturnType = String + //: These are required for Object var functionJobName: String diff --git a/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift index ed73aeef9..01a8a7742 100644 --- a/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/7 - GeoPoint.xcplaygroundpage/Contents.swift @@ -22,7 +22,7 @@ struct GameScore: ParseObject { var ACL: ParseACL? var location: ParseGeoPoint? //: Your own properties - var score: Int + var score: Int? //: A custom initializer. init(score: Int) { @@ -126,7 +126,6 @@ query3.find { results in switch results { case .success(let scores): - assert(scores.count >= 1) scores.forEach { (score) in print(""" Someone has a score of \"\(score.score)\" with no geopoint \(String(describing: score.location)) @@ -164,7 +163,6 @@ query7.find { results in switch results { case .success(let scores): - assert(scores.count >= 1) scores.forEach { (score) in print(""" Someone has a score of \"\(score.score)\" with geopoint using OR \(String(describing: score.location)) @@ -177,11 +175,19 @@ query7.find { results in } //: Explain the previous query. -let explain = try query2.find(explain: true) +let explain: AnyDecodable = try query2.first(explain: true) print(explain) -let hint = try query2.find(explain: false, hint: "objectId") -print(hint) +//: Hint of the previous query (asynchronous) +query2.find(explain: false, + hint: "_id_") { (result: Result<[GameScore], ParseError>) in + switch result { + case .success(let scores): + print(scores) + case .failure(let error): + print(error.localizedDescription) + } +} PlaygroundPage.current.finishExecution() //: [Next](@next) diff --git a/ParseSwift.podspec b/ParseSwift.podspec index 8e4c51a08..09a584f74 100644 --- a/ParseSwift.podspec +++ b/ParseSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "ParseSwift" - s.version = "1.1.7" + s.version = "1.2.0" s.summary = "Parse Pure Swift SDK" s.homepage = "https://github.com/parse-community/Parse-Swift" s.authors = { diff --git a/Scripts/jazzy.sh b/Scripts/jazzy.sh index 3a0a8df81..46d9a287c 100755 --- a/Scripts/jazzy.sh +++ b/Scripts/jazzy.sh @@ -5,7 +5,7 @@ bundle exec jazzy \ --author_url http://parseplatform.org \ --github_url https://github.com/parse-community/Parse-Swift \ --root-url http://parseplatform.org/Parse-Swift/api/ \ - --module-version 1.1.7 \ + --module-version 1.2.0 \ --theme fullwidth \ --skip-undocumented \ --output ./docs/api \ diff --git a/Sources/ParseSwift/API/API+Commands.swift b/Sources/ParseSwift/API/API+Commands.swift index 6a414cf43..754d02ef7 100644 --- a/Sources/ParseSwift/API/API+Commands.swift +++ b/Sources/ParseSwift/API/API+Commands.swift @@ -224,7 +224,7 @@ internal extension API { var urlRequest = URLRequest(url: urlComponents) urlRequest.allHTTPHeaderFields = headers if let urlBody = body { - if (urlBody as? ParseCloud) != nil { + if (urlBody as? CloudType) != nil { guard let bodyData = try? ParseCoding.parseEncoder().encode(urlBody, skipKeys: .cloud) else { return .failure(ParseError(code: .unknownError, message: "couldn't encode body \(urlBody)")) diff --git a/Sources/ParseSwift/API/Responses.swift b/Sources/ParseSwift/API/Responses.swift index 9c70e603c..0ea3e3d79 100644 --- a/Sources/ParseSwift/API/Responses.swift +++ b/Sources/ParseSwift/API/Responses.swift @@ -136,13 +136,13 @@ internal struct FileUploadResponse: Decodable { } // MARK: AnyResultResponse -internal struct AnyResultResponse: Codable { - let result: AnyCodable? +internal struct AnyResultResponse: Decodable { + let result: U } // MARK: AnyResultsResponse -internal struct AnyResultsResponse: Codable { - let results: AnyCodable? +internal struct AnyResultsResponse: Decodable { + let results: [U] } // MARK: ConfigResponse diff --git a/Sources/ParseSwift/API/URLSession+extensions.swift b/Sources/ParseSwift/API/URLSession+extensions.swift index 97ad5b557..828fd5a62 100755 --- a/Sources/ParseSwift/API/URLSession+extensions.swift +++ b/Sources/ParseSwift/API/URLSession+extensions.swift @@ -43,9 +43,17 @@ extension URLSession { return .failure(error) } guard let parseError = error as? ParseError else { + guard JSONSerialization.isValidJSONObject(responseData) == true, + let json = try? JSONSerialization + .data(withJSONObject: responseData, + options: .prettyPrinted) else { + return .failure(ParseError(code: .unknownError, + // swiftlint:disable:next line_length + message: "Error decoding parse-server response: \(String(describing: urlResponse)) with error: \(error.localizedDescription) Format: \(String(describing: String(data: responseData, encoding: .utf8)))")) + } return .failure(ParseError(code: .unknownError, // swiftlint:disable:next line_length - message: "Error decoding parse-server response: \(String(describing: urlResponse)) with error: \(error.localizedDescription)")) + message: "Error decoding parse-server response: \(String(describing: urlResponse)) with error: \(error.localizedDescription) Format: \(String(describing: String(data: json, encoding: .utf8)))")) } return .failure(parseError) } diff --git a/Sources/ParseSwift/Coding/ParseCoding.swift b/Sources/ParseSwift/Coding/ParseCoding.swift index ee0808e0e..2005e6f45 100644 --- a/Sources/ParseSwift/Coding/ParseCoding.swift +++ b/Sources/ParseSwift/Coding/ParseCoding.swift @@ -76,7 +76,7 @@ extension ParseCoding { message: "An invalid date string was provided when decoding dates." ) } - } catch let error { + } catch { let container = try decoder.container(keyedBy: DateEncodingKeys.self) if diff --git a/Sources/ParseSwift/Objects/ParseInstallation.swift b/Sources/ParseSwift/Objects/ParseInstallation.swift index 9929c364b..f120c5378 100644 --- a/Sources/ParseSwift/Objects/ParseInstallation.swift +++ b/Sources/ParseSwift/Objects/ParseInstallation.swift @@ -395,13 +395,14 @@ extension ParseInstallation { completion(result) } } - } catch let error as ParseError { - callbackQueue.async { - completion(.failure(error)) - } } catch { callbackQueue.async { - completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) + if let error = error as? ParseError { + completion(.failure(error)) + } else { + completion(.failure(ParseError(code: .unknownError, + message: error.localizedDescription))) + } } } } diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index d607da5ee..9a52e1475 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -539,13 +539,14 @@ extension ParseObject { completion(result) } } - } catch let error as ParseError { - callbackQueue.async { - completion(.failure(error)) - } } catch { callbackQueue.async { - completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) + if let error = error as? ParseError { + completion(.failure(error)) + } else { + completion(.failure(ParseError(code: .unknownError, + message: error.localizedDescription))) + } } } } @@ -646,7 +647,7 @@ extension ParseObject { } internal func saveCommand() -> API.Command { - return API.Command.saveCommand(self) + API.Command.saveCommand(self) } // swiftlint:disable:next function_body_length diff --git a/Sources/ParseSwift/Objects/ParseUser.swift b/Sources/ParseSwift/Objects/ParseUser.swift index ff53f872f..ff5317830 100644 --- a/Sources/ParseSwift/Objects/ParseUser.swift +++ b/Sources/ParseSwift/Objects/ParseUser.swift @@ -706,13 +706,14 @@ extension ParseUser { } } } - } catch let error as ParseError { - callbackQueue.async { - completion(.failure(error)) - } } catch { callbackQueue.async { - completion(.failure(ParseError(code: .unknownError, message: error.localizedDescription))) + if let error = error as? ParseError { + completion(.failure(error)) + } else { + completion(.failure(ParseError(code: .unknownError, + message: error.localizedDescription))) + } } } } diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index 0e9248139..bd9d67ca0 100644 --- a/Sources/ParseSwift/ParseConstants.swift +++ b/Sources/ParseSwift/ParseConstants.swift @@ -9,7 +9,7 @@ import Foundation enum ParseConstants { - static let parseVersion = "1.1.7" + static let parseVersion = "1.2.0" static let hashingKey = "parseSwift" static let fileManagementDirectory = "parse/" static let fileManagementPrivateDocumentsDirectory = "Private Documents/" diff --git a/Sources/ParseSwift/Protocols/Queryable.swift b/Sources/ParseSwift/Protocols/Queryable.swift index e04cf9f9c..7d31b482e 100644 --- a/Sources/ParseSwift/Protocols/Queryable.swift +++ b/Sources/ParseSwift/Protocols/Queryable.swift @@ -11,7 +11,7 @@ public protocol Queryable { associatedtype ResultType func find(options: API.Options) throws -> [ResultType] - func first(options: API.Options) throws -> ResultType? + func first(options: API.Options) throws -> ResultType func count(options: API.Options) throws -> Int func find(options: API.Options, callbackQueue: DispatchQueue, completion: @escaping (Result<[ResultType], ParseError>) -> Void) diff --git a/Sources/ParseSwift/Storage/ParseKeyValueStore.swift b/Sources/ParseSwift/Storage/ParseKeyValueStore.swift index 13eb25a75..aa64ee6d3 100644 --- a/Sources/ParseSwift/Storage/ParseKeyValueStore.swift +++ b/Sources/ParseSwift/Storage/ParseKeyValueStore.swift @@ -32,8 +32,8 @@ public protocol ParseKeyValueStore { /// It works by encoding / decoding all values just like a real `Codable` store would /// but it stores all values as `Data` blobs in memory. struct InMemoryKeyValueStore: ParseKeyValueStore { - var decoder = JSONDecoder() - var encoder = JSONEncoder() + var decoder = ParseCoding.jsonDecoder() + var encoder = ParseCoding.jsonEncoder() var storage = [String: Data]() mutating func delete(valueFor key: String) throws { diff --git a/Sources/ParseSwift/Types/ParseCloud+combine.swift b/Sources/ParseSwift/Types/ParseCloud+combine.swift index ef3405af5..07f4b4402 100644 --- a/Sources/ParseSwift/Types/ParseCloud+combine.swift +++ b/Sources/ParseSwift/Types/ParseCloud+combine.swift @@ -22,7 +22,7 @@ public extension ParseCloud { - 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. */ - func runFunctionPublisher(options: API.Options = []) -> Future { + func runFunctionPublisher(options: API.Options = []) -> Future { Future { promise in self.runFunction(options: options, completion: promise) @@ -37,7 +37,7 @@ public extension ParseCloud { - 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. */ - func startJobPublisher(options: API.Options = []) -> Future { + func startJobPublisher(options: API.Options = []) -> Future { Future { promise in self.startJob(options: options, completion: promise) diff --git a/Sources/ParseSwift/Types/ParseCloud.swift b/Sources/ParseSwift/Types/ParseCloud.swift index 65b436d01..078057bd3 100644 --- a/Sources/ParseSwift/Types/ParseCloud.swift +++ b/Sources/ParseSwift/Types/ParseCloud.swift @@ -8,12 +8,16 @@ import Foundation +public protocol CloudType: Decodable, CustomDebugStringConvertible { } + /** Objects that conform to the `ParseCloud` protocol are able to call Parse Cloud Functions and Jobs. An object should be instantiated for each function and job type. When conforming to `ParseCloud`, any properties added will be passed as parameters to your Cloud Function or Job. */ -public protocol ParseCloud: ParseType, Decodable, CustomDebugStringConvertible { +public protocol ParseCloud: ParseType, CloudType { + + associatedtype ReturnType: Decodable /** The name of the function or job. */ @@ -27,10 +31,10 @@ extension ParseCloud { /** Calls a Cloud Code 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 a JSON response of `AnyCodable` type. + - returns: Returns a `Decodable` type. - throws: An error of type `ParseError`. */ - public func runFunction(options: API.Options = []) throws -> AnyCodable { + public func runFunction(options: API.Options = []) throws -> ReturnType { try runFunctionCommand().execute(options: options, callbackQueue: .main) } @@ -39,11 +43,11 @@ extension ParseCloud { - 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. - It should have the following argument signature: `(Result)`. + It should have the following argument signature: `(Result)`. */ public func runFunction(options: API.Options = [], callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { + completion: @escaping (Result) -> Void) { runFunctionCommand() .executeAsync(options: options, callbackQueue: callbackQueue) { result in callbackQueue.async { @@ -52,19 +56,13 @@ extension ParseCloud { } } - internal func runFunctionCommand() -> API.Command { + internal func runFunctionCommand() -> API.Command { return API.Command(method: .POST, path: .functions(name: functionJobName), - body: self) { (data) -> AnyCodable in - let response = try ParseCoding.jsonDecoder().decode(AnyResultResponse.self, from: data) - guard let result = response.result else { - if let error = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data) { - throw error - } - return AnyCodable() - } - return result + body: self) { (data) -> ReturnType in + let response = try ParseCoding.jsonDecoder().decode(AnyResultResponse.self, from: data) + return response.result } } } @@ -74,9 +72,9 @@ extension ParseCloud { /** Starts a Cloud Code job *synchronously* and returns a result with the jobStatusId of the job. - parameter options: A set of header options sent to the server. Defaults to an empty set. - - returns: Returns a JSON response of `AnyCodable` type. + - returns: Returns a `Decodable` type. */ - public func startJob(options: API.Options = []) throws -> AnyCodable { + public func startJob(options: API.Options = []) throws -> ReturnType { try startJobCommand().execute(options: options, callbackQueue: .main) } @@ -85,11 +83,11 @@ extension ParseCloud { - 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. - It should have the following argument signature: `(Result)`. + It should have the following argument signature: `(Result)`. */ public func startJob(options: API.Options = [], callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { + completion: @escaping (Result) -> Void) { startJobCommand() .executeAsync(options: options, callbackQueue: callbackQueue) { result in callbackQueue.async { @@ -98,18 +96,12 @@ extension ParseCloud { } } - internal func startJobCommand() -> API.Command { + internal func startJobCommand() -> API.Command { return API.Command(method: .POST, path: .jobs(name: functionJobName), - body: self) { (data) -> AnyCodable in - let response = try ParseCoding.jsonDecoder().decode(AnyResultResponse.self, from: data) - guard let result = response.result else { - if let error = try? ParseCoding.jsonDecoder().decode(ParseError.self, from: data) { - throw error - } - return AnyCodable() - } - return result + body: self) { (data) -> ReturnType in + let response = try ParseCoding.jsonDecoder().decode(AnyResultResponse.self, from: data) + return response.result } } } diff --git a/Sources/ParseSwift/Types/Query+combine.swift b/Sources/ParseSwift/Types/Query+combine.swift index d7b591231..5e4960bdd 100644 --- a/Sources/ParseSwift/Types/Query+combine.swift +++ b/Sources/ParseSwift/Types/Query+combine.swift @@ -35,9 +35,9 @@ public extension 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. */ - func findPublisher(explain: Bool, - hint: String? = nil, - options: API.Options = []) -> Future { + func findPublisher(explain: Bool, + hint: String? = nil, + options: API.Options = []) -> Future<[U], ParseError> { Future { promise in self.find(explain: explain, hint: hint, @@ -65,9 +65,9 @@ public extension 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. */ - func firstPublisher(explain: Bool, - hint: String? = nil, - options: API.Options = []) -> Future { + func firstPublisher(explain: Bool, + hint: String? = nil, + options: API.Options = []) -> Future { Future { promise in self.first(explain: explain, hint: hint, @@ -95,9 +95,9 @@ public extension 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. */ - func countPublisher(explain: Bool, - hint: String? = nil, - options: API.Options = []) -> Future { + func countPublisher(explain: Bool, + hint: String? = nil, + options: API.Options = []) -> Future { Future { promise in self.count(explain: explain, hint: hint, diff --git a/Sources/ParseSwift/Types/Query.swift b/Sources/ParseSwift/Types/Query.swift index d91f242d8..eb89e8cc8 100644 --- a/Sources/ParseSwift/Types/Query.swift +++ b/Sources/ParseSwift/Types/Query.swift @@ -859,9 +859,11 @@ extension Query: Queryable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of type `ParseError`. - - returns: Returns a dictionary of `AnyResultType` that is the JSON response of the query. + - returns: Returns a response of `Decodable` type. */ - public func find(explain: Bool, hint: String? = nil, options: API.Options = []) throws -> AnyCodable { + public func find(explain: Bool, + hint: String? = nil, + options: API.Options = []) throws -> [U] { try findCommand(explain: explain, hint: hint).execute(options: options) } @@ -873,7 +875,8 @@ extension Query: Queryable { - parameter completion: The block to execute. It should have the following argument signature: `(Result<[ResultType], ParseError>)`. */ - public func find(options: API.Options = [], callbackQueue: DispatchQueue = .main, + public func find(options: API.Options = [], + callbackQueue: DispatchQueue = .main, completion: @escaping (Result<[ResultType], ParseError>) -> Void) { findCommand().executeAsync(options: options) { result in callbackQueue.async { @@ -890,11 +893,13 @@ extension Query: Queryable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of .main. - parameter completion: The block to execute. - It should have the following argument signature: `(Result)`. + It should have the following argument signature: `(Result<[Decodable], ParseError>)`. */ - public func find(explain: Bool, hint: String? = nil, options: API.Options = [], - callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { + public func find(explain: Bool, + hint: String? = nil, + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result<[U], ParseError>) -> Void) { findCommand(explain: explain, hint: hint).executeAsync(options: options) { result in callbackQueue.async { completion(result) @@ -909,9 +914,9 @@ extension Query: Queryable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of type `ParseError`. - - returns: Returns a `ParseObject`, or `nil` if none was found. + - returns: Returns a `ParseObject`. */ - public func first(options: API.Options = []) throws -> ResultType? { + public func first(options: API.Options = []) throws -> ResultType { try firstCommand().execute(options: options) } @@ -924,9 +929,11 @@ extension Query: Queryable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of type `ParseError`. - - returns: Returns a dictionary of `AnyResultType` that is the JSON response of the query. + - returns: Returns a response of `Decodable` type. */ - public func first(explain: Bool, hint: String? = nil, options: API.Options = []) throws -> AnyCodable { + public func first(explain: Bool, + hint: String? = nil, + options: API.Options = []) throws -> U { try firstCommand(explain: explain, hint: hint).execute(options: options) } @@ -939,22 +946,12 @@ extension Query: Queryable { - parameter completion: The block to execute. It should have the following argument signature: `(Result)`. */ - public func first(options: API.Options = [], callbackQueue: DispatchQueue = .main, + public func first(options: API.Options = [], + callbackQueue: DispatchQueue = .main, completion: @escaping (Result) -> Void) { firstCommand().executeAsync(options: options) { result in - callbackQueue.async { - switch result { - case .success(let first): - guard let first = first else { - completion(.failure(ParseError(code: .objectNotFound, - message: "Object not found on the server."))) - return - } - completion(.success(first)) - case .failure(let error): - completion(.failure(error)) - } + completion(result) } } } @@ -968,11 +965,12 @@ extension Query: Queryable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of `.main`. - parameter completion: The block to execute. - It should have the following argument signature: `(Result)`. + It should have the following argument signature: `(Result)`. */ - public func first(explain: Bool, hint: String? = nil, options: API.Options = [], - callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { + public func first(explain: Bool, hint: String? = nil, + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void) { firstCommand(explain: explain, hint: hint).executeAsync(options: options) { result in callbackQueue.async { completion(result) @@ -1000,9 +998,11 @@ extension Query: Queryable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - throws: An error of type `ParseError`. - - returns: Returns a dictionary of `AnyResultType` that is the JSON response of the query. + - returns: Returns a response of `Decodable` type. */ - public func count(explain: Bool, hint: String? = nil, options: API.Options = []) throws -> AnyCodable { + public func count(explain: Bool, + hint: String? = nil, + options: API.Options = []) throws -> U { try countCommand(explain: explain, hint: hint).execute(options: options) } @@ -1030,11 +1030,13 @@ extension Query: Queryable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of `.main`. - parameter completion: The block to execute. - It should have the following argument signature: `(Result)`. + It should have the following argument signature: `(Result)`. */ - public func count(explain: Bool, hint: String? = nil, options: API.Options = [], - callbackQueue: DispatchQueue = .main, - completion: @escaping (Result) -> Void) { + public func count(explain: Bool, + hint: String? = nil, + options: API.Options = [], + callbackQueue: DispatchQueue = .main, + completion: @escaping (Result) -> Void) { countCommand(explain: explain, hint: hint).executeAsync(options: options) { result in callbackQueue.async { completion(result) @@ -1077,7 +1079,7 @@ extension Query: Queryable { - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter callbackQueue: The queue to return to after completion. Default value of `.main`. - parameter completion: The block to execute. - It should have the following argument signature: `(Result<[ResultType], ParseError>)`. + It should have the following argument signature: `(Result<[ParseObject], ParseError>)`. - warning: This hasn't been tested thoroughly. */ public func aggregate(_ pipeline: AggregateType, @@ -1120,11 +1122,15 @@ extension Query { } } - func firstCommand() -> API.NonParseBodyCommand, ResultType?> { + func firstCommand() -> API.NonParseBodyCommand, ResultType> { var query = self query.limit = 1 return API.NonParseBodyCommand(method: .POST, path: query.endpoint, body: query) { - try ParseCoding.jsonDecoder().decode(QueryResponse.self, from: $0).results.first + if let decoded = try ParseCoding.jsonDecoder().decode(QueryResponse.self, from: $0).results.first { + return decoded + } + throw ParseError(code: .objectNotFound, + message: "Object not found on the server.") } } @@ -1132,47 +1138,51 @@ extension Query { var query = self query.limit = 1 query.isCount = true - return API.NonParseBodyCommand(method: .POST, path: query.endpoint, body: query) { + return API.NonParseBodyCommand(method: .POST, + path: query.endpoint, + body: query) { try ParseCoding.jsonDecoder().decode(QueryResponse.self, from: $0).count ?? 0 } } - func findCommand(explain: Bool, hint: String?) -> API.NonParseBodyCommand, AnyCodable> { + func findCommand(explain: Bool, + hint: String?) -> API.NonParseBodyCommand, [U]> { var query = self query.explain = explain query.hint = hint return API.NonParseBodyCommand(method: .POST, path: query.endpoint, body: query) { - if let results = try JSONDecoder().decode(AnyResultsResponse.self, from: $0).results { - return results - } - return AnyCodable() + try ParseCoding.jsonDecoder().decode(AnyResultsResponse.self, from: $0).results } } - func firstCommand(explain: Bool, hint: String?) -> API.NonParseBodyCommand, AnyCodable> { + func firstCommand(explain: Bool, + hint: String?) -> API.NonParseBodyCommand, U> { var query = self query.limit = 1 query.explain = explain query.hint = hint return API.NonParseBodyCommand(method: .POST, path: query.endpoint, body: query) { - if let results = try JSONDecoder().decode(AnyResultsResponse.self, from: $0).results { - return results + if let decoded: U = try ParseCoding.jsonDecoder().decode(AnyResultsResponse.self, from: $0).results.first { + return decoded } - return AnyCodable() + throw ParseError(code: .objectNotFound, + message: "Object not found on the server.") } } - func countCommand(explain: Bool, hint: String?) -> API.NonParseBodyCommand, AnyCodable> { + func countCommand(explain: Bool, + hint: String?) -> API.NonParseBodyCommand, U> { var query = self query.limit = 1 query.isCount = true query.explain = explain query.hint = hint return API.NonParseBodyCommand(method: .POST, path: query.endpoint, body: query) { - if let results = try JSONDecoder().decode(AnyResultsResponse.self, from: $0).results { - return results + if let decoded: U = try ParseCoding.jsonDecoder().decode(AnyResultsResponse.self, from: $0).results.first { + return decoded } - return AnyCodable() + throw ParseError(code: .objectNotFound, + message: "Object not found on the server.") } } diff --git a/Tests/ParseSwiftTests/APICommandTests.swift b/Tests/ParseSwiftTests/APICommandTests.swift index 50e8302a5..2920b0d6b 100644 --- a/Tests/ParseSwiftTests/APICommandTests.swift +++ b/Tests/ParseSwiftTests/APICommandTests.swift @@ -24,8 +24,8 @@ class APICommandTests: XCTestCase { var name = "First" } - override func setUp() { - super.setUp() + override func setUpWithError() throws { + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -37,13 +37,13 @@ class APICommandTests: XCTestCase { testing: true) } - override func tearDown() { - super.tearDown() + override func tearDownWithError() throws { + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) - try? KeychainStore.shared.deleteAll() + try KeychainStore.shared.deleteAll() #endif - try? ParseStorage.shared.deleteAll() + try ParseStorage.shared.deleteAll() } func testExecuteCorrectly() { diff --git a/Tests/ParseSwiftTests/KeychainStoreTests.swift b/Tests/ParseSwiftTests/KeychainStoreTests.swift index 4ffc7e7a1..03c7bd62a 100644 --- a/Tests/ParseSwiftTests/KeychainStoreTests.swift +++ b/Tests/ParseSwiftTests/KeychainStoreTests.swift @@ -12,13 +12,13 @@ import XCTest class KeychainStoreTests: XCTestCase { var testStore: KeychainStore! - override func setUp() { - super.setUp() + override func setUpWithError() throws { + try super.setUpWithError() testStore = KeychainStore(service: "test") } - override func tearDown() { - super.tearDown() + override func tearDownWithError() throws { + try super.tearDownWithError() _ = testStore.removeAllObjects() } diff --git a/Tests/ParseSwiftTests/ParseACLTests.swift b/Tests/ParseSwiftTests/ParseACLTests.swift index f9a7f82b1..b08fd7579 100644 --- a/Tests/ParseSwiftTests/ParseACLTests.swift +++ b/Tests/ParseSwiftTests/ParseACLTests.swift @@ -12,8 +12,8 @@ import XCTest class ParseACLTests: XCTestCase { - override func setUp() { - super.setUp() + override func setUpWithError() throws { + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -24,12 +24,12 @@ class ParseACLTests: XCTestCase { serverURL: url, testing: true) } - override func tearDown() { - super.tearDown() + override func tearDownWithError() throws { + try super.tearDownWithError() #if !os(Linux) - try? KeychainStore.shared.deleteAll() + try KeychainStore.shared.deleteAll() #endif - try? ParseStorage.shared.deleteAll() + try ParseStorage.shared.deleteAll() } struct User: ParseUser { diff --git a/Tests/ParseSwiftTests/ParseAnonymousCombineTests.swift b/Tests/ParseSwiftTests/ParseAnonymousCombineTests.swift index f1dff0e3e..ff9b429e6 100644 --- a/Tests/ParseSwiftTests/ParseAnonymousCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseAnonymousCombineTests.swift @@ -61,7 +61,7 @@ class ParseAuthenticationCombineTests: XCTestCase { // swiftlint:disable:this ty } override func setUpWithError() throws { - super.setUp() + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -74,7 +74,7 @@ class ParseAuthenticationCombineTests: XCTestCase { // swiftlint:disable:this ty } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() diff --git a/Tests/ParseSwiftTests/ParseAppleCombineTests.swift b/Tests/ParseSwiftTests/ParseAppleCombineTests.swift index ed6e02a2f..d7a52afed 100644 --- a/Tests/ParseSwiftTests/ParseAppleCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseAppleCombineTests.swift @@ -62,7 +62,7 @@ class ParseAppleCombineTests: XCTestCase { // swiftlint:disable:this type_body_l } override func setUpWithError() throws { - super.setUp() + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -75,7 +75,7 @@ class ParseAppleCombineTests: XCTestCase { // swiftlint:disable:this type_body_l } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() diff --git a/Tests/ParseSwiftTests/ParseCloudCombineTests.swift b/Tests/ParseSwiftTests/ParseCloudCombineTests.swift index 367d79bf1..c45097c61 100644 --- a/Tests/ParseSwiftTests/ParseCloudCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseCloudCombineTests.swift @@ -17,12 +17,18 @@ import Combine class ParseCloudCombineTests: XCTestCase { // swiftlint:disable:this type_body_length struct Cloud: ParseCloud { + typealias ReturnType = String? // swiftlint:disable:this nesting + // Those are required for Object var functionJobName: String } + struct AnyResultResponse: Codable { + let result: U + } + override func setUpWithError() throws { - super.setUp() + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -35,7 +41,7 @@ class ParseCloudCombineTests: XCTestCase { // swiftlint:disable:this type_body_l } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() @@ -47,7 +53,7 @@ class ParseCloudCombineTests: XCTestCase { // swiftlint:disable:this type_body_l var subscriptions = Set() let expectation1 = XCTestExpectation(description: "Save") - let response = AnyResultResponse(result: nil) + let response = AnyResultResponse(result: nil) MockURLProtocol.mockRequests { _ in do { @@ -69,7 +75,7 @@ class ParseCloudCombineTests: XCTestCase { // swiftlint:disable:this type_body_l }, receiveValue: { functionResponse in - XCTAssertEqual(functionResponse, AnyCodable()) + XCTAssertNil(functionResponse) }) publisher.store(in: &subscriptions) @@ -80,7 +86,7 @@ class ParseCloudCombineTests: XCTestCase { // swiftlint:disable:this type_body_l var subscriptions = Set() let expectation1 = XCTestExpectation(description: "Save") - let response = AnyResultResponse(result: nil) + let response = AnyResultResponse(result: nil) MockURLProtocol.mockRequests { _ in do { @@ -102,7 +108,7 @@ class ParseCloudCombineTests: XCTestCase { // swiftlint:disable:this type_body_l }, receiveValue: { functionResponse in - XCTAssertEqual(functionResponse, AnyCodable()) + XCTAssertNil(functionResponse) }) publisher.store(in: &subscriptions) diff --git a/Tests/ParseSwiftTests/ParseCloudTests.swift b/Tests/ParseSwiftTests/ParseCloudTests.swift index d0e7faa05..f0c8e3cd8 100644 --- a/Tests/ParseSwiftTests/ParseCloudTests.swift +++ b/Tests/ParseSwiftTests/ParseCloudTests.swift @@ -13,11 +13,15 @@ import XCTest class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length struct Cloud: ParseCloud { + typealias ReturnType = String? // swiftlint:disable:this nesting + // Those are required for Object var functionJobName: String } struct Cloud2: ParseCloud { + typealias ReturnType = String? // swiftlint:disable:this nesting + // Those are required for Object var functionJobName: String @@ -25,6 +29,17 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length var customKey: String? } + struct Cloud3: ParseCloud { + typealias ReturnType = [String: String] // swiftlint:disable:this nesting + + // Those are required for Object + var functionJobName: String + } + + struct AnyResultResponse: Codable { + let result: U + } + override func setUpWithError() throws { try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { @@ -39,7 +54,7 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() @@ -106,7 +121,7 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length } func testFunction() { - let response = AnyResultResponse(result: nil) + let response = AnyResultResponse(result: nil) MockURLProtocol.mockRequests { _ in do { @@ -119,34 +134,30 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length do { let cloud = Cloud(functionJobName: "test") let functionResponse = try cloud.runFunction() - XCTAssertEqual(functionResponse, AnyCodable()) + XCTAssertNil(functionResponse) } catch { XCTFail(error.localizedDescription) } } func testFunction2() { - var result: AnyCodable = ["hello": "world"] + var result = ["hello": "world"] let response = AnyResultResponse(result: result) MockURLProtocol.mockRequests { _ in do { let encoded = try ParseCoding.jsonEncoder().encode(response) let encodedResult = try ParseCoding.jsonEncoder().encode(result) - result = try ParseCoding.jsonDecoder().decode(AnyCodable.self, from: encodedResult) + result = try ParseCoding.jsonDecoder().decode([String: String].self, from: encodedResult) return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) } catch { return nil } } do { - let cloud = Cloud(functionJobName: "test") + let cloud = Cloud3(functionJobName: "test") let functionResponse = try cloud.runFunction() - guard let resultAsDictionary = functionResponse.value as? [String: String] else { - XCTFail("Should have casted result to dictionary") - return - } - XCTAssertEqual(resultAsDictionary, ["hello": "world"]) + XCTAssertEqual(functionResponse, ["hello": "world"]) } catch { XCTFail(error.localizedDescription) } @@ -180,25 +191,16 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length } } - func functionAsync(serverResponse: AnyCodable, callbackQueue: DispatchQueue) { + func functionAsync(serverResponse: [String: String], callbackQueue: DispatchQueue) { let expectation1 = XCTestExpectation(description: "Logout user1") - let cloud = Cloud(functionJobName: "test") + let cloud = Cloud3(functionJobName: "test") cloud.runFunction(callbackQueue: callbackQueue) { result in switch result { case .success(let response): - if serverResponse == AnyCodable() { - XCTAssertEqual(response, serverResponse) - } else { - guard let resultAsDictionary = serverResponse.value as? [String: String] else { - XCTFail("Should have casted result to dictionary") - expectation1.fulfill() - return - } - XCTAssertEqual(resultAsDictionary, ["hello": "world"]) - } + XCTAssertEqual(response, serverResponse) case .failure(let error): XCTFail(error.localizedDescription) } @@ -208,23 +210,7 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length } func testFunctionMainQueue() { - let response = AnyResultResponse(result: nil) - - MockURLProtocol.mockRequests { _ in - do { - let encoded = try ParseCoding.jsonEncoder().encode(response) - return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) - } catch { - return nil - } - } - - self.functionAsync(serverResponse: AnyCodable(), callbackQueue: .main) - } - - func testFunctionMainQueue2() { - let result: AnyCodable = ["hello": "world"] - let response = AnyResultResponse(result: result) + let response = AnyResultResponse(result: ["hello": "world"]) MockURLProtocol.mockRequests { _ in do { @@ -235,7 +221,7 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length } } - self.functionAsync(serverResponse: result, callbackQueue: .main) + self.functionAsync(serverResponse: ["hello": "world"], callbackQueue: .main) } func functionAsyncError(parseError: ParseError, callbackQueue: DispatchQueue) { @@ -295,7 +281,7 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length } func testJob() { - let response = AnyResultResponse(result: nil) + let response = AnyResultResponse(result: nil) MockURLProtocol.mockRequests { _ in do { @@ -308,15 +294,14 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length do { let cloud = Cloud(functionJobName: "test") let functionResponse = try cloud.startJob() - XCTAssertEqual(functionResponse, AnyCodable()) + XCTAssertNil(functionResponse) } catch { XCTFail(error.localizedDescription) } } func testJob2() { - let result: AnyCodable = ["hello": "world"] - let response = AnyResultResponse(result: result) + let response = AnyResultResponse(result: ["hello": "world"]) MockURLProtocol.mockRequests { _ in do { @@ -327,13 +312,9 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length } } do { - let cloud = Cloud(functionJobName: "test") + let cloud = Cloud3(functionJobName: "test") let functionResponse = try cloud.startJob() - guard let resultAsDictionary = functionResponse.value as? [String: String] else { - XCTFail("Should have casted result to dictionary") - return - } - XCTAssertEqual(resultAsDictionary, ["hello": "world"]) + XCTAssertEqual(functionResponse, response.result) } catch { XCTFail(error.localizedDescription) } @@ -367,25 +348,16 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length } } - func jobAsync(serverResponse: AnyCodable, callbackQueue: DispatchQueue) { + func jobAsync(serverResponse: [String: String], callbackQueue: DispatchQueue) { let expectation1 = XCTestExpectation(description: "Logout user1") - let cloud = Cloud(functionJobName: "test") + let cloud = Cloud3(functionJobName: "test") cloud.startJob(callbackQueue: callbackQueue) { result in switch result { case .success(let response): - if serverResponse == AnyCodable() { - XCTAssertEqual(response, serverResponse) - } else { - guard let resultAsDictionary = serverResponse.value as? [String: String] else { - XCTFail("Should have casted result to dictionary") - expectation1.fulfill() - return - } - XCTAssertEqual(resultAsDictionary, ["hello": "world"]) - } + XCTAssertEqual(response, serverResponse) case .failure(let error): XCTFail(error.localizedDescription) } @@ -395,23 +367,7 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length } func testJobMainQueue() { - let response = AnyResultResponse(result: nil) - - MockURLProtocol.mockRequests { _ in - do { - let encoded = try ParseCoding.jsonEncoder().encode(response) - return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) - } catch { - return nil - } - } - - self.jobAsync(serverResponse: AnyCodable(), callbackQueue: .main) - } - - func testJobMainQueue2() { - let result: AnyCodable = ["hello": "world"] - let response = AnyResultResponse(result: result) + let response = AnyResultResponse(result: ["hello": "world"]) MockURLProtocol.mockRequests { _ in do { @@ -422,7 +378,7 @@ class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length } } - self.jobAsync(serverResponse: result, callbackQueue: .main) + self.jobAsync(serverResponse: ["hello": "world"], callbackQueue: .main) } func jobAsyncError(parseError: ParseError, callbackQueue: DispatchQueue) { diff --git a/Tests/ParseSwiftTests/ParseConfigCombineTests.swift b/Tests/ParseSwiftTests/ParseConfigCombineTests.swift index 5d471cb30..745e7f878 100644 --- a/Tests/ParseSwiftTests/ParseConfigCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseConfigCombineTests.swift @@ -70,7 +70,7 @@ class ParseConfigCombineTests: XCTestCase { // swiftlint:disable:this type_body_ } override func setUpWithError() throws { - super.setUp() + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -83,7 +83,7 @@ class ParseConfigCombineTests: XCTestCase { // swiftlint:disable:this type_body_ } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() @@ -145,6 +145,7 @@ class ParseConfigCombineTests: XCTestCase { // swiftlint:disable:this type_body_ }, receiveValue: { fetched in XCTAssertEqual(fetched.welcomeMessage, configOnServer.welcomeMessage) + XCTAssertEqual(Config.current?.welcomeMessage, configOnServer.welcomeMessage) #if !os(Linux) //Should be updated in Keychain @@ -155,8 +156,6 @@ class ParseConfigCombineTests: XCTestCase { // swiftlint:disable:this type_body_ } XCTAssertEqual(keychainConfig.currentConfig?.welcomeMessage, configOnServer.welcomeMessage) #endif - - XCTAssertEqual(Config.current?.welcomeMessage, configOnServer.welcomeMessage) }) publisher.store(in: &subscriptions) @@ -195,6 +194,7 @@ class ParseConfigCombineTests: XCTestCase { // swiftlint:disable:this type_body_ }, receiveValue: { saved in XCTAssertTrue(saved) + XCTAssertEqual(Config.current?.welcomeMessage, config.welcomeMessage) #if !os(Linux) //Should be updated in Keychain @@ -205,8 +205,6 @@ class ParseConfigCombineTests: XCTestCase { // swiftlint:disable:this type_body_ } XCTAssertEqual(keychainConfig.currentConfig?.welcomeMessage, config.welcomeMessage) #endif - - XCTAssertEqual(Config.current?.welcomeMessage, config.welcomeMessage) }) publisher.store(in: &subscriptions) diff --git a/Tests/ParseSwiftTests/ParseConfigTests.swift b/Tests/ParseSwiftTests/ParseConfigTests.swift index 0ac54ee80..372fd6c92 100644 --- a/Tests/ParseSwiftTests/ParseConfigTests.swift +++ b/Tests/ParseSwiftTests/ParseConfigTests.swift @@ -79,7 +79,7 @@ class ParseConfigTests: XCTestCase { // swiftlint:disable:this type_body_length } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() @@ -163,6 +163,7 @@ class ParseConfigTests: XCTestCase { // swiftlint:disable:this type_body_length do { let fetched = try config.fetch() XCTAssertEqual(fetched.welcomeMessage, configOnServer.welcomeMessage) + XCTAssertEqual(Config.current?.welcomeMessage, configOnServer.welcomeMessage) #if !os(Linux) //Should be updated in Keychain @@ -174,7 +175,6 @@ class ParseConfigTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(keychainConfig.currentConfig?.welcomeMessage, configOnServer.welcomeMessage) #endif - XCTAssertEqual(Config.current?.welcomeMessage, configOnServer.welcomeMessage) } catch { XCTFail(error.localizedDescription) } @@ -205,6 +205,7 @@ class ParseConfigTests: XCTestCase { // swiftlint:disable:this type_body_length case .success(let fetched): XCTAssertEqual(fetched.welcomeMessage, configOnServer.welcomeMessage) + XCTAssertEqual(Config.current?.welcomeMessage, configOnServer.welcomeMessage) #if !os(Linux) //Should be updated in Keychain @@ -217,7 +218,6 @@ class ParseConfigTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(keychainConfig.currentConfig?.welcomeMessage, configOnServer.welcomeMessage) #endif - XCTAssertEqual(Config.current?.welcomeMessage, configOnServer.welcomeMessage) case .failure(let error): XCTFail(error.localizedDescription) } @@ -256,6 +256,7 @@ class ParseConfigTests: XCTestCase { // swiftlint:disable:this type_body_length do { let saved = try config.save() XCTAssertTrue(saved) + XCTAssertEqual(Config.current?.welcomeMessage, config.welcomeMessage) #if !os(Linux) //Should be updated in Keychain @@ -266,8 +267,6 @@ class ParseConfigTests: XCTestCase { // swiftlint:disable:this type_body_length } XCTAssertEqual(keychainConfig.currentConfig?.welcomeMessage, config.welcomeMessage) #endif - - XCTAssertEqual(Config.current?.welcomeMessage, config.welcomeMessage) } catch { XCTFail(error.localizedDescription) } @@ -297,6 +296,7 @@ class ParseConfigTests: XCTestCase { // swiftlint:disable:this type_body_length case .success(let saved): XCTAssertTrue(saved) + XCTAssertEqual(Config.current?.welcomeMessage, config.welcomeMessage) #if !os(Linux) //Should be updated in Keychain @@ -309,7 +309,6 @@ class ParseConfigTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(keychainConfig.currentConfig?.welcomeMessage, config.welcomeMessage) #endif - XCTAssertEqual(Config.current?.welcomeMessage, config.welcomeMessage) case .failure(let error): XCTFail(error.localizedDescription) } diff --git a/Tests/ParseSwiftTests/ParseEncoderTests.swift b/Tests/ParseSwiftTests/ParseEncoderTests.swift index 5cce56ea8..07eee4b7b 100644 --- a/Tests/ParseSwiftTests/ParseEncoderTests.swift +++ b/Tests/ParseSwiftTests/ParseEncoderTests.swift @@ -92,4 +92,13 @@ class ParseEncoderTests: XCTestCase { XCTAssertNil(decoded["updatedAt"]) XCTAssertNil(decoded["className"]) } + + func testDateStringEncoding() throws { + let jsonScore = "{\"createdAt\":\"2021-03-15T02:24:47.841Z\",\"score\":5}" + guard let encoded = jsonScore.data(using: .utf8) else { + XCTFail("Shuld have created data") + return + } + XCTAssertNoThrow(try ParseCoding.jsonDecoder().decode(GameScore.self, from: encoded)) + } } diff --git a/Tests/ParseSwiftTests/ParseFileCombineTests.swift b/Tests/ParseSwiftTests/ParseFileCombineTests.swift index 4a8162d02..983982d9d 100644 --- a/Tests/ParseSwiftTests/ParseFileCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseFileCombineTests.swift @@ -24,7 +24,7 @@ class ParseFileCombineTests: XCTestCase { // swiftlint:disable:this type_body_le } override func setUpWithError() throws { - super.setUp() + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -41,7 +41,7 @@ class ParseFileCombineTests: XCTestCase { // swiftlint:disable:this type_body_le } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() diff --git a/Tests/ParseSwiftTests/ParseGeoPointTests.swift b/Tests/ParseSwiftTests/ParseGeoPointTests.swift index ddbf27397..23e5fabd8 100644 --- a/Tests/ParseSwiftTests/ParseGeoPointTests.swift +++ b/Tests/ParseSwiftTests/ParseGeoPointTests.swift @@ -13,8 +13,8 @@ import CoreLocation @testable import ParseSwift class ParseGeoPointTests: XCTestCase { - override func setUp() { - super.setUp() + override func setUpWithError() throws { + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -26,13 +26,13 @@ class ParseGeoPointTests: XCTestCase { testing: true) } - override func tearDown() { - super.tearDown() + override func tearDownWithError() throws { + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) - try? KeychainStore.shared.deleteAll() + try KeychainStore.shared.deleteAll() #endif - try? ParseStorage.shared.deleteAll() + try ParseStorage.shared.deleteAll() } func testDefaults() { diff --git a/Tests/ParseSwiftTests/ParseInstallationCombineTests.swift b/Tests/ParseSwiftTests/ParseInstallationCombineTests.swift index 3f0d87b0b..80dce92ee 100644 --- a/Tests/ParseSwiftTests/ParseInstallationCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseInstallationCombineTests.swift @@ -89,7 +89,7 @@ class ParseInstallationCombineTests: XCTestCase { // swiftlint:disable:this type let loginPassword = "world" override func setUpWithError() throws { - super.setUp() + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -103,7 +103,7 @@ class ParseInstallationCombineTests: XCTestCase { // swiftlint:disable:this type } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() diff --git a/Tests/ParseSwiftTests/ParseInstallationTests.swift b/Tests/ParseSwiftTests/ParseInstallationTests.swift index fea297e30..827dc976c 100644 --- a/Tests/ParseSwiftTests/ParseInstallationTests.swift +++ b/Tests/ParseSwiftTests/ParseInstallationTests.swift @@ -86,8 +86,8 @@ class ParseInstallationTests: XCTestCase { // swiftlint:disable:this type_body_l let testInstallationObjectId = "yarr" - override func setUp() { - super.setUp() + override func setUpWithError() throws { + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -100,13 +100,13 @@ class ParseInstallationTests: XCTestCase { // swiftlint:disable:this type_body_l userLogin() } - override func tearDown() { - super.tearDown() + override func tearDownWithError() throws { + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) - try? KeychainStore.shared.deleteAll() + try KeychainStore.shared.deleteAll() #endif - try? ParseStorage.shared.deleteAll() + try ParseStorage.shared.deleteAll() } func userLogin() { diff --git a/Tests/ParseSwiftTests/ParseLDAPCombineTests.swift b/Tests/ParseSwiftTests/ParseLDAPCombineTests.swift index a1403428f..bc3e81ed8 100644 --- a/Tests/ParseSwiftTests/ParseLDAPCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseLDAPCombineTests.swift @@ -62,7 +62,7 @@ class ParseLDAPCombineTests: XCTestCase { // swiftlint:disable:this type_body_le } override func setUpWithError() throws { - super.setUp() + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -75,7 +75,7 @@ class ParseLDAPCombineTests: XCTestCase { // swiftlint:disable:this type_body_le } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() diff --git a/Tests/ParseSwiftTests/ParseObjectBatchTests.swift b/Tests/ParseSwiftTests/ParseObjectBatchTests.swift index 8461f18ce..b7f9b9988 100644 --- a/Tests/ParseSwiftTests/ParseObjectBatchTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectBatchTests.swift @@ -32,8 +32,8 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le } } - override func setUp() { - super.setUp() + override func setUpWithError() throws { + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -45,13 +45,13 @@ class ParseObjectBatchTests: XCTestCase { // swiftlint:disable:this type_body_le testing: true) } - override func tearDown() { - super.tearDown() + override func tearDownWithError() throws { + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) - try? KeychainStore.shared.deleteAll() + try KeychainStore.shared.deleteAll() #endif - try? ParseStorage.shared.deleteAll() + try ParseStorage.shared.deleteAll() } //COREY: Linux decodes this differently for some reason diff --git a/Tests/ParseSwiftTests/ParseObjectCombine.swift b/Tests/ParseSwiftTests/ParseObjectCombine.swift index 2d70b0666..47480c9b7 100644 --- a/Tests/ParseSwiftTests/ParseObjectCombine.swift +++ b/Tests/ParseSwiftTests/ParseObjectCombine.swift @@ -42,7 +42,7 @@ class ParseObjectCombineTests: XCTestCase { // swiftlint:disable:this type_body_ } override func setUpWithError() throws { - super.setUp() + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -55,7 +55,7 @@ class ParseObjectCombineTests: XCTestCase { // swiftlint:disable:this type_body_ } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() diff --git a/Tests/ParseSwiftTests/ParseObjectTests.swift b/Tests/ParseSwiftTests/ParseObjectTests.swift index 7db966848..8afca1e53 100644 --- a/Tests/ParseSwiftTests/ParseObjectTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectTests.swift @@ -186,8 +186,8 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length } } - override func setUp() { - super.setUp() + override func setUpWithError() throws { + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -200,7 +200,7 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() diff --git a/Tests/ParseSwiftTests/ParseOperationCombineTests.swift b/Tests/ParseSwiftTests/ParseOperationCombineTests.swift index 8067b6437..15ca4bd29 100644 --- a/Tests/ParseSwiftTests/ParseOperationCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseOperationCombineTests.swift @@ -42,7 +42,7 @@ class ParseOperationCombineTests: XCTestCase { // swiftlint:disable:this type_bo } override func setUpWithError() throws { - super.setUp() + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -55,7 +55,7 @@ class ParseOperationCombineTests: XCTestCase { // swiftlint:disable:this type_bo } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() diff --git a/Tests/ParseSwiftTests/ParseQueryCombineTests.swift b/Tests/ParseSwiftTests/ParseQueryCombineTests.swift index bed7a5df4..7f822ff68 100644 --- a/Tests/ParseSwiftTests/ParseQueryCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseQueryCombineTests.swift @@ -41,8 +41,16 @@ class ParseQueryCombineTests: XCTestCase { // swiftlint:disable:this type_body_l } } + struct AnyResultResponse: Codable { + let result: U + } + + struct AnyResultsResponse: Codable { + let results: U + } + override func setUpWithError() throws { - super.setUp() + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -55,7 +63,7 @@ class ParseQueryCombineTests: XCTestCase { // swiftlint:disable:this type_body_l } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() @@ -111,7 +119,7 @@ class ParseQueryCombineTests: XCTestCase { // swiftlint:disable:this type_body_l var subscriptions = Set() let expectation1 = XCTestExpectation(description: "Save") - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -135,14 +143,8 @@ class ParseQueryCombineTests: XCTestCase { // swiftlint:disable:this type_body_l } expectation1.fulfill() - }, receiveValue: { queryResult in - - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - return - } - XCTAssertEqual(response, expected) + }, receiveValue: { (queryResult: [[String: String]]) in + XCTAssertEqual(queryResult, json.results) }) publisher.store(in: &subscriptions) @@ -193,7 +195,7 @@ class ParseQueryCombineTests: XCTestCase { // swiftlint:disable:this type_body_l var subscriptions = Set() let expectation1 = XCTestExpectation(description: "Save") - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -217,14 +219,8 @@ class ParseQueryCombineTests: XCTestCase { // swiftlint:disable:this type_body_l } expectation1.fulfill() - }, receiveValue: { queryResult in - - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - return - } - XCTAssertEqual(response, expected) + }, receiveValue: { (queryResult: [String: String]) in + XCTAssertEqual(queryResult, json.results.first) }) publisher.store(in: &subscriptions) @@ -275,7 +271,7 @@ class ParseQueryCombineTests: XCTestCase { // swiftlint:disable:this type_body_l var subscriptions = Set() let expectation1 = XCTestExpectation(description: "Save") - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -299,14 +295,8 @@ class ParseQueryCombineTests: XCTestCase { // swiftlint:disable:this type_body_l } expectation1.fulfill() - }, receiveValue: { queryResult in - - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - return - } - XCTAssertEqual(response, expected) + }, receiveValue: { (queryResult: [String: String]) in + XCTAssertEqual(queryResult, json.results.first) }) publisher.store(in: &subscriptions) diff --git a/Tests/ParseSwiftTests/ParseQueryTests.swift b/Tests/ParseSwiftTests/ParseQueryTests.swift index fc91a9b71..fd212ecaa 100644 --- a/Tests/ParseSwiftTests/ParseQueryTests.swift +++ b/Tests/ParseSwiftTests/ParseQueryTests.swift @@ -28,16 +28,26 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length } } - struct GameType: ParseObject { + struct GameScoreBroken: ParseObject { //: Those are required for Object var objectId: String? var createdAt: Date? var updatedAt: Date? var ACL: ParseACL? + + var score: Int? //Left as non-optional to throw error on pointer + } + + struct AnyResultResponse: Codable { + let result: U + } + + struct AnyResultsResponse: Codable { + let results: [U] } - override func setUp() { - super.setUp() + override func setUpWithError() throws { + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -49,13 +59,13 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length testing: true) } - override func tearDown() { - super.tearDown() + override func tearDownWithError() throws { + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) - try? KeychainStore.shared.deleteAll() + try KeychainStore.shared.deleteAll() #endif - try? ParseStorage.shared.deleteAll() + try ParseStorage.shared.deleteAll() } // MARK: Initialization @@ -385,16 +395,32 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let query = GameScore.query() do { - - guard let score = try query.first(options: []) else { - XCTFail("Should unwrap first object found") - return - } + let score = try query.first(options: []) XCTAssert(score.hasSameObjectId(as: scoreOnServer)) } catch { XCTFail(error.localizedDescription) } + } + + func testFirstThrowDecodingError() { + var scoreOnServer = GameScoreBroken() + scoreOnServer.objectId = "yarr" + scoreOnServer.createdAt = Date() + scoreOnServer.updatedAt = scoreOnServer.createdAt + scoreOnServer.ACL = nil + let results = QueryResponse(results: [scoreOnServer], count: 1) + MockURLProtocol.mockRequests { _ in + do { + let encoded = try ParseCoding.jsonEncoder().encode(results) + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } catch { + return nil + } + } + + let query = GameScore.query() + XCTAssertThrowsError(try query.first(options: [])) } func testFirstNoObjectFound() { @@ -411,11 +437,8 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let query = GameScore.query() do { - - guard try query.first(options: []) == nil else { - XCTFail("Should have thrown error") - return - } + _ = try query.first(options: []) + XCTFail("Should have thrown error") } catch { guard let error = error as? ParseError else { XCTFail("Should have casted as ParseError") @@ -1912,7 +1935,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length // MARK: JSON Responses func testExplainFindSynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -1928,20 +1951,15 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let query = GameScore.query() do { - let queryResult = try query.find(explain: true) - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - return - } - XCTAssertEqual(response, expected) + let queryResult: [[String: String]] = try query.find(explain: true) + XCTAssertEqual(queryResult, json.results) } catch { XCTFail("Error: \(error)") } } func testExplainFindAsynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -1957,17 +1975,11 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let expectation = XCTestExpectation(description: "Fetch object") let query = GameScore.query() - query.find(explain: true, callbackQueue: .main) { result in + query.find(explain: true, callbackQueue: .main) { (result: Result<[[String: String]], ParseError>) in switch result { case .success(let queryResult): - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - expectation.fulfill() - return - } - XCTAssertEqual(response, expected) + XCTAssertEqual(queryResult, json.results) case .failure(let error): XCTFail("Error: \(error)") } @@ -1977,7 +1989,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length } func testExplainFirstSynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -1993,20 +2005,15 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let query = GameScore.query() do { - let queryResult = try query.first(explain: true) - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - return - } - XCTAssertEqual(response, expected) + let queryResult: [String: String] = try query.first(explain: true) + XCTAssertEqual(queryResult, json.results.first) } catch { XCTFail("Error: \(error)") } } func testExplainFirstAsynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -2022,17 +2029,11 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let expectation = XCTestExpectation(description: "Fetch object") let query = GameScore.query() - query.first(explain: true, callbackQueue: .main) { result in + query.first(explain: true, callbackQueue: .main) { (result: Result<[String: String], ParseError>) in switch result { case .success(let queryResult): - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - expectation.fulfill() - return - } - XCTAssertEqual(response, expected) + XCTAssertEqual(queryResult, json.results.first) case .failure(let error): XCTFail("Error: \(error)") } @@ -2042,7 +2043,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length } func testExplainCountSynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -2058,20 +2059,15 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let query = GameScore.query() do { - let queryResult = try query.count(explain: true) - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - return - } - XCTAssertEqual(response, expected) + let queryResult: [String: String] = try query.count(explain: true) + XCTAssertEqual(queryResult, json.results.first) } catch { XCTFail("Error: \(error)") } } func testExplainCountAsynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -2087,17 +2083,11 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let expectation = XCTestExpectation(description: "Fetch object") let query = GameScore.query() - query.count(explain: true, callbackQueue: .main) { result in + query.count(explain: true, callbackQueue: .main) { (result: Result<[String: String], ParseError>) in switch result { case .success(let queryResult): - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - expectation.fulfill() - return - } - XCTAssertEqual(response, expected) + XCTAssertEqual(queryResult, json.results.first) case .failure(let error): XCTFail("Error: \(error)") } @@ -2107,7 +2097,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length } func testHintFindSynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -2123,20 +2113,15 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let query = GameScore.query() do { - let queryResult = try query.find(explain: false, hint: "_id_") - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - return - } - XCTAssertEqual(response, expected) + let queryResult: [[String: String]] = try query.find(explain: false, hint: "_id_") + XCTAssertEqual(queryResult, json.results) } catch { XCTFail("Error: \(error)") } } func testHintFindAsynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -2152,17 +2137,13 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let expectation = XCTestExpectation(description: "Fetch object") let query = GameScore.query() - query.find(explain: false, hint: "_id_", callbackQueue: .main) { result in + query.find(explain: false, + hint: "_id_", + callbackQueue: .main) { (result: Result<[[String: String]], ParseError>) in switch result { case .success(let queryResult): - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - expectation.fulfill() - return - } - XCTAssertEqual(response, expected) + XCTAssertEqual(queryResult, json.results) case .failure(let error): XCTFail("Error: \(error)") } @@ -2172,7 +2153,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length } func testHintFirstSynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -2188,20 +2169,15 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let query = GameScore.query() do { - let queryResult = try query.first(explain: false, hint: "_id_") - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - return - } - XCTAssertEqual(response, expected) + let queryResult: [String: String] = try query.first(explain: false, hint: "_id_") + XCTAssertEqual(queryResult, json.results.first) } catch { XCTFail("Error: \(error)") } } func testHintFirstAsynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -2217,17 +2193,13 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let expectation = XCTestExpectation(description: "Fetch object") let query = GameScore.query() - query.first(explain: false, hint: "_id_", callbackQueue: .main) { result in + query.first(explain: false, + hint: "_id_", + callbackQueue: .main) { (result: Result<[String: String], ParseError>) in switch result { case .success(let queryResult): - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - expectation.fulfill() - return - } - XCTAssertEqual(response, expected) + XCTAssertEqual(queryResult, json.results.first) case .failure(let error): XCTFail("Error: \(error)") } @@ -2237,7 +2209,7 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length } func testHintCountSynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -2253,20 +2225,15 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let query = GameScore.query() do { - let queryResult = try query.count(explain: false, hint: "_id_") - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - return - } - XCTAssertEqual(response, expected) + let queryResult: [String: String] = try query.count(explain: false, hint: "_id_") + XCTAssertEqual(queryResult, json.results.first) } catch { XCTFail("Error: \(error)") } } func testHintCountAsynchronous() { - let json = AnyResultsResponse(results: ["yolo": "yarr"]) + let json = AnyResultsResponse(results: [["yolo": "yarr"]]) let encoded: Data! do { @@ -2282,17 +2249,12 @@ class ParseQueryTests: XCTestCase { // swiftlint:disable:this type_body_length let expectation = XCTestExpectation(description: "Fetch object") let query = GameScore.query() - query.count(explain: false, hint: "_id_", callbackQueue: .main) { result in + query.count(explain: false, hint: "_id_", + callbackQueue: .main) { (result: Result<[String: String], ParseError>) in switch result { case .success(let queryResult): - guard let response = queryResult.value as? [String: String], - let expected = json.results?.value as? [String: String] else { - XCTFail("Error: Should cast to string") - expectation.fulfill() - return - } - XCTAssertEqual(response, expected) + XCTAssertEqual(queryResult, json.results.first) case .failure(let error): XCTFail("Error: \(error)") } diff --git a/Tests/ParseSwiftTests/ParseUserCombineTests.swift b/Tests/ParseSwiftTests/ParseUserCombineTests.swift index 480316ddc..b01dd0109 100644 --- a/Tests/ParseSwiftTests/ParseUserCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseUserCombineTests.swift @@ -68,7 +68,7 @@ class ParseUserCombineTests: XCTestCase { // swiftlint:disable:this type_body_le let loginPassword = "world" override func setUpWithError() throws { - super.setUp() + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -81,7 +81,7 @@ class ParseUserCombineTests: XCTestCase { // swiftlint:disable:this type_body_le } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll() diff --git a/Tests/ParseSwiftTests/ParseUserTests.swift b/Tests/ParseSwiftTests/ParseUserTests.swift index 9e235ee41..45846b226 100644 --- a/Tests/ParseSwiftTests/ParseUserTests.swift +++ b/Tests/ParseSwiftTests/ParseUserTests.swift @@ -63,8 +63,8 @@ class ParseUserTests: XCTestCase { // swiftlint:disable:this type_body_length let loginUserName = "hello10" let loginPassword = "world" - override func setUp() { - super.setUp() + override func setUpWithError() throws { + try super.setUpWithError() guard let url = URL(string: "http://localhost:1337/1") else { XCTFail("Should create valid URL") return @@ -77,7 +77,7 @@ class ParseUserTests: XCTestCase { // swiftlint:disable:this type_body_length } override func tearDownWithError() throws { - super.tearDown() + try super.tearDownWithError() MockURLProtocol.removeAll() #if !os(Linux) try KeychainStore.shared.deleteAll()