Repository: parse-community/Parse-Swift (swift)

Issue:
Make it possible to easily compare errors
### New Feature / Enhancement Checklist
<!--
    Check all of the following boxes [x] before submitting your issue.
    Click the "Preview" tab for better readability.
    Thanks for contributing to Parse Swift!
-->

- [x] I am not disclosing a [vulnerability](https://github.com/parse-community/Parse-Swift/security/policy).
- [x] I am not just asking a [question](https://github.com/parse-community/.github/blob/main/SUPPORT.md).
- [x] I have searched through [existing issues](https://github.com/parse-community/Parse-Swift/issues?q=is%3Aissue).

### Current Limitation
Currently it's not really easy to know how to compare errors. It would be nice if there is some example in the Playground.

### Feature / Enhancement Description
It will be used as an extension.

### Example Use Case
```
if error.containedInParseError([.objectNotFound, .invalidQuery]) {
      assertionFailure("The query is invalid or the object is not found.")
}
```

### Alternatives / Workarounds
```
guard let parseError = error as? ParseError else {
      //Do stuff for different error
      return
}

switch parseError.code {
      case .objectNotFound: 
            // Handle object not found
      default:
            // There was a different issue that we don't know how to handle
}
```

Pull request:
### New Pull Request Checklist
<!--
    Please check the following boxes [x] before submitting your issue.
    Click the "Preview" tab for better readability.
    Thanks for contributing to Parse Platform!
-->

- [x] I am not disclosing a [vulnerability](https://github.com/parse-community/Parse-Swift/security/policy).
- [x] I am creating this PR in reference to an [issue](https://github.com/parse-community/Parse-Swift/issues?q=is%3Aissue).

### Issue Description
<!-- Add a brief description of the issue this PR solves. -->

Related issue: #251

### Approach
Add the ability to easily compare Swift Errors with ParseErrors.

### TODOs before merging
<!--
    Add TODOs that need to be completed before merging this PR.
    Delete TODOs that do not apply to this PR.
-->

- [x] Add changes to documentation (guides, repository pages, in-code descriptions)
- [x] Replaced SwiftLint autocorrect with `--fix` 
- [x] Add test cases
- [x] Add change log entry

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 31269cc35..dad77d49b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@
 [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.10.1...main)
 * _Contributing to this repo? Add info about your change here to be included in the next release_
 
+__Improvements__
+- Added an extension to compare a Swift Error with a single ParseError or multiple ParseErrors ([#250](https://github.com/parse-community/Parse-Swift/pull/250)), thanks to [Damian Van de Kauter](https://github.com/novemTeam).
+
 ### 1.10.1
 [Full Changelog](https://github.com/parse-community/Parse-Swift/compare/1.10.0...1.10.1)
 
diff --git a/ParseSwift.playground/Pages/2 - Finding Objects.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/2 - Finding Objects.xcplaygroundpage/Contents.swift
index 59e99f4f9..3a6dcdacc 100644
--- a/ParseSwift.playground/Pages/2 - Finding Objects.xcplaygroundpage/Contents.swift
+++ b/ParseSwift.playground/Pages/2 - Finding Objects.xcplaygroundpage/Contents.swift
@@ -54,7 +54,11 @@ query.limit(2).find(callbackQueue: .main) { results in
         }
 
     case .failure(let error):
-        assertionFailure("Error querying: \(error)")
+        if error.equalsTo(.objectNotFound) {
+            assertionFailure("Object not found for this query")
+        } else {
+            assertionFailure("Error querying: \(error)")
+        }
     }
 }
 
@@ -80,7 +84,11 @@ query.first { results in
         print("Found score: \(score)")
 
     case .failure(let error):
-        assertionFailure("Error querying: \(error)")
+        if error.containedIn([.objectNotFound, .invalidQuery]) {
+            assertionFailure("The query is invalid or the object is not found.")
+        } else {
+            assertionFailure("Error querying: \(error)")
+        }
     }
 }
 
@@ -109,7 +117,11 @@ querySelect.first { results in
         print("Found score using select: \(score)")
 
     case .failure(let error):
-        assertionFailure("Error querying: \(error)")
+        if let parseError = error.equalsTo(.objectNotFound) {
+            assertionFailure("Object not found: \(parseError)")
+        } else {
+            assertionFailure("Error querying: \(error)")
+        }
     }
 }
 
@@ -124,7 +136,11 @@ queryExclude.first { results in
         print("Found score using exclude: \(score)")
 
     case .failure(let error):
-        assertionFailure("Error querying: \(error)")
+        if let parseError = error.containedIn(.objectNotFound, .invalidQuery) {
+            assertionFailure("Matching error found: \(parseError)")
+        } else {
+            assertionFailure("Error querying: \(error)")
+        }
     }
 }
 
diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj
index ad9ffcd19..3f3685124 100644
--- a/ParseSwift.xcodeproj/project.pbxproj
+++ b/ParseSwift.xcodeproj/project.pbxproj
@@ -1723,7 +1723,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "if which swiftlint >/dev/null; then\n    swiftlint autocorrect && swiftlint --strict\nelse\n    echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
+			shellScript = "if which swiftlint >/dev/null; then\n    swiftlint --fix --strict\nelse\n    echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
 		};
 		918CED61268A23A700CFDC83 /* SwiftLint */ = {
 			isa = PBXShellScriptBuildPhase;
diff --git a/Sources/ParseSwift/Types/ParseError.swift b/Sources/ParseSwift/Types/ParseError.swift
index 3d093b236..97d1eee96 100644
--- a/Sources/ParseSwift/Types/ParseError.swift
+++ b/Sources/ParseSwift/Types/ParseError.swift
@@ -406,3 +406,123 @@ extension ParseError: CustomStringConvertible {
         debugDescription
     }
 }
+
+// MARK: Compare Errors
+public extension Error {
+
+    /**
+     Returns the respective `ParseError` if the given `ParseError` code is equal to the error.
+     
+    **Example use case:**
+    ~~~
+    if let parseError = error.equalsTo(.objectNotFound)  {
+        print(parseError.description)
+    }
+    ~~~
+     - parameter errorCode: A `ParseError` code to compare to.
+     
+     - returns: Returns the `ParseError` with respect to the `Error`. If the error is not a `ParseError`, returns nil.
+     */
+    func equalsTo(_ errorCode: ParseError.Code) -> ParseError? {
+        guard let error = self as? ParseError,
+                error.code == errorCode else {
+            return nil
+        }
+        return error
+    }
+
+    /**
+     Validates if the given `ParseError` code is equal to the error.
+     
+    **Example use case:**
+    ~~~
+    if error.equalsTo(.objectNotFound)  {
+        //Do stuff
+    }
+    ~~~
+     - parameter errorCode: A `ParseError` code to compare to.
+     
+     - returns: A boolean indicating whether or not the `Error` is the `errorCode`.
+     */
+    func equalsTo(_ errorCode: ParseError.Code) -> Bool {
+        guard equalsTo(errorCode) != nil else {
+            return false
+        }
+        return true
+    }
+
+    /**
+     Returns the respective `ParseError` if the `Error` is contained in the array of `ParseError` codes.
+     
+    **Example use case:**
+    ~~~
+    if let parseError = error.containedIn([.objectNotFound, .invalidQuery])  {
+        print(parseError.description)
+    }
+    ~~~
+     - parameter errorCodes: An array of zero or more of `ParseError` codes to compare to.
+     
+     - returns: Returns the `ParseError` with respect to the `Error`. If the error is not a `ParseError`, returns nil.
+     */
+    func containedIn(_ errorCodes: [ParseError.Code]) -> ParseError? {
+        guard let error = self as? ParseError,
+              errorCodes.contains(error.code) == true else {
+            return nil
+        }
+        return error
+    }
+
+    /**
+     Returns the respective `ParseError` if the `Error` is contained in the list of `ParseError` codes.
+     
+    **Example use case:**
+    ~~~
+    if let parseError = error.containedIn(.objectNotFound, .invalidQuery)  {
+        print(parseError.description)
+    }
+    ~~~
+     - parameter errorCodes: A variadic amount of zero or more of `ParseError` codes to compare to.
+     
+     - returns: Returns the `ParseError` with respect to the `Error`. If the error is not a `ParseError`, returns nil.
+     */
+    func containedIn(_ errorCodes: ParseError.Code...) -> ParseError? {
+        containedIn(errorCodes)
+    }
+
+    /**
+     Validates if the given `ParseError` codes contains the error.
+     
+    **Example use case:**
+    ~~~
+    if error.containedIn([.objectNotFound, .invalidQuery])  {
+        //Do stuff
+    }
+    ~~~
+     - parameter errorCodes: An array of zero or more of `ParseError` codes to compare to.
+     
+     - returns: A boolean indicating whether or not the `Error` is contained in the `errorCodes`.
+     */
+    func containedIn(_ errorCodes: [ParseError.Code]) -> Bool {
+        guard containedIn(errorCodes) != nil else {
+            return false
+        }
+        return true
+    }
+
+    /**
+     Validates if the given `ParseError` codes contains the error.
+     
+    **Example use case:**
+    ~~~
+    if error.containedIn(.objectNotFound, .invalidQuery)  {
+        //Do stuff
+    }
+    ~~~
+     - parameter errorCodes: A variadic amount of zero or more of `ParseError` codes to compare to.
+     
+     - returns: A boolean indicating whether or not the `Error` is contained in the `errorCodes`.
+     */
+    func containedIn(_ errorCodes: ParseError.Code...) -> Bool {
+        containedIn(errorCodes)
+    }
+}


Test patch:
diff --git a/Tests/ParseSwiftTests/ParseErrorTests.swift b/Tests/ParseSwiftTests/ParseErrorTests.swift
index 013e26187..f091fd102 100644
--- a/Tests/ParseSwiftTests/ParseErrorTests.swift
+++ b/Tests/ParseSwiftTests/ParseErrorTests.swift
@@ -62,4 +62,34 @@ class ParseErrorTests: XCTestCase {
                        "ParseError code=\(ParseError.Code.other.rawValue) error=\(message) otherCode=\(code)")
         XCTAssertEqual(decoded.otherCode, code)
     }
+
+    func testCompare() throws {
+        let code = ParseError.Code.objectNotFound.rawValue
+        let message = "testing ParseError"
+        guard let encoded = "{\"error\":\"\(message)\",\"code\":\(code)}".data(using: .utf8) else {
+            XCTFail("Should have unwrapped")
+            return
+        }
+        let decoded = try ParseCoding.jsonDecoder().decode(ParseError.self, from: encoded)
+
+        let error: Error = decoded
+
+        XCTAssertTrue(error.equalsTo(.objectNotFound))
+        XCTAssertFalse(error.equalsTo(.invalidQuery))
+
+        XCTAssertTrue(error.containedIn(.objectNotFound, .invalidQuery))
+        XCTAssertFalse(error.containedIn(.operationForbidden, .invalidQuery))
+
+        XCTAssertTrue(error.containedIn([.objectNotFound, .invalidQuery]))
+        XCTAssertFalse(error.containedIn([.operationForbidden, .invalidQuery]))
+
+        XCTAssertNotNil(error.equalsTo(.objectNotFound))
+        XCTAssertNil(error.equalsTo(.invalidQuery))
+
+        XCTAssertNotNil(error.containedIn(.objectNotFound, .invalidQuery))
+        XCTAssertNil(error.containedIn(.operationForbidden, .invalidQuery))
+
+        XCTAssertNotNil(error.containedIn([.objectNotFound, .invalidQuery]))
+        XCTAssertNil(error.containedIn([.operationForbidden, .invalidQuery]))
+    }
 }