CNContactStore Save Error - swift

CNContactStore's executeSaveRequest(_:) method throws an error according to the documentation.
I am trying to catch this error in a do/catch, but I cannot figure out what error I need to catch.
do{
try store.executeSaveRequest(saveRequest)
} catch *???* {
//alert the user
}
What is supposed to replace the ???'s in the code above?

You have a few options actually.
Catch any error without knowing the error
catch {...}
Catch any error with the specific error message
catch let error { // Use error }
Use exhaustive catch clauses to handle specific errors using the CNErrorCode enum.
enum CNErrorCode : Int {
case CommunicationError
case DataAccessError
case AuthorizationDenied
case RecordDoesNotExist
case InsertedRecordAlreadyExists
case ContainmentCycle
case ContainmentScope
case ParentRecordDoesNotExist
case ValidationMultipleErrors
case ValidationTypeMismatch
case ValidationConfigurationError
case PredicateInvalid
case PolicyViolation
}

Related

Error handling in Swift: Error from one enum to another

I have this code in a ServiceClient. It handles service-level calls, like signIn(user, password, completion), listObjects(completion), addObject(objectID, content, completion), getObject(id, completion) etc. It contains (but doesn't subclass) an APIClient, which performs only basic HTTPS services like perform(request, completion).
I don't really want the controller that sits above this to deal with 404s as success, which means trapping the error in ServiceClient. So the idea is APIClient deals with networking errors whereas ServiceClient deals with unexpected HTTP results.
So I end up with this in ServiceClient, where errors like invalidURL are converted from an APIClient enum to a ServiceClient enum:
apiClient.perform(request) {result in
switch result {
case .success(let data):
guard data.statusCode == 200 else {
completion(.failure(.badResponse))
return
}
completion(.success(data))
case .failure(let error):
switch error {
case .invalidURL:
completion(.failure(.invalidURL))
case .requestFailed:
completion(.failure(.requestFailed))
case .decodingFailure:
completion(.failure(.decodingFailure))
}
}
}
I think in this case I'll just make APIClient handle invalid HTTP status codes, but what's the more general solution to this? At some point I'll want different error codes for different service clients, at which point this becomes a problem again.
I suggest using Int type enumeration for both ServiceClient and APIClient.
As I understood this is your custom enumerations.
So, assuming you have ServiceClientError and APIClientError you can implement them using this way:
enum ServiceClientError: Int {
case invalidURL, requestFailed, decodingFailure
}
enum APIClientError: Int {
case invalidURL, requestFailed, decodingFailure
}
You can create your custom conversion method:
extension ServiceClientError {
static func create(from apiClientError: APIClientError) -> ServiceClientError {
return ServiceClientError(rawValue: apiClientError.rawValue)
}
}
Wanted function:
apiClient.perform(request) {result in
switch result {
case .success(let data):
guard data.statusCode == 200 else {
completion(.failure(.badResponse))
return
}
completion(.success(data))
case .failure(let error):
guard let serviceClientError = ServiceClientError.create(from: error) else {
/// Handle incorrect behavior
fatalError("Wrong enumeration mapping")
return
}
completion(.failure(serviceClientError))
}
}

Extracting raw value of a Error enum case

I have created a Result enum to propagate the result of a REST API call.
enum Result {
case success([AnyObject])
case failure(APIError)
}
I also have a Error enum to handler errors
enum APIError: Error {
case requestFailed(String)
case invalidData(String)
case invalidQuery(String)
case invalidURL(String)
}
I send the error in a completion closure like
completion(.failure(.invalidURL("Invalid URL")))
How can I access this string in a if case scenario?
I am trying to do something like
if case .failure(let res) = result /*, case res.invalidQuery(let invalid) */ {
print(res)
}
How can I achieve this?
If what you're trying to do is having two cases on the same line, you can do it like so:
if case .failure(let error) = result, case .invalidQuery(let message) = error {
print(message)
}

how to print error in catch

catch let error as LocksmithError{
print(error)// it would print the case of the error.
}
However if I do
catch LocksmithError.Duplicate{
}
catch{
print (LocksmithError) // Obviously I would just print LocksmithError, it won't print the case
print (LocksmithError.rawValue) // prints nothing
}
My question is: Using the 2nd approach is there any that I can actually retrieve and the value/case of the error? Or if I don't get the value right at the entry point ie the catch, then I miss the chance of doing it!
The catch blocks are exclusive cases, evaluated in order. When a match succeeds, we stop.
So, let's just think about this structure:
catch LocksmithError.Duplicate {
// 1
print("duplicate")
}
catch {
// 2
print(error)
}
If we are at 1, then what is in scope is the LocksmithError.Duplicate.
If we are at 2, then what is in scope is every other kind of error that gets caught. There's no way you can get hold of the LocksmithError.Duplicate here, because ex hypothesi it would have been caught in 1 and we wouldn't be here.
Now, the way I would do it is like this:
catch let err as LocksmithError {
// 1
print(err)
}
catch {
// 2
print(error)
}
That may be the sort of thing you are after; it gives us a value err that carries the error into the curly braces in 1. (The automatic error value exists only the final catch-all catch block.)

Is it possible to combine catches?

I'm trying to do this:
catch LocksmithError.Duplicate, LocksmithError.Allocate {...}
But I get an error at , saying :
Expected '{' after 'catch' pattern
Does this mean you can't combine cases like case expression2, expression3 :? Any reason it's made this way?
No, it's currently not possible to combine multiple patterns in a catch clause – the grammar (as detailed by the Swift Language Guide) only allows for a single pattern to match against:
catch-clause → catch pattern­opt ­where-clause­opt ­code-block­
Another possible solution to the ones proposed already, as long as your error enum is trivial (which LocksmithError appears to be), is to use a where clause after a binding pattern:
catch let error as LocksmithError where error == .Duplicate || error == .Allocate {
// ...
}
Given that you let your LocksmithError have some rawvalue type (e.g. Int), you could, in a single catch statement, bind the error thrown and use its rawValue to test for inclusion into one of several error cases (using the where clause after binding). E.g.:
enum FooError: Int, Error {
case err1 = 1, err2, err3, err4, err5
}
func foo(_ bar: Int) throws {
if let err = FooError(rawValue: bar) { throw err }
}
func tryFoo(_ bar: Int) {
do {
try foo(bar)
} catch let err as FooError where (1...4).contains(err.rawValue) {
print("Any of 1st through 4th error!")
} catch FooError.err5 {
print("5th error!")
} catch {}
}
tryFoo(1) // Any of 1st through 4th error!
tryFoo(4) // Any of 1st through 4th error!
tryFoo(5) // 5th error!
As suggested by #user28434 (thanks!), there's really no need to apply the rawValue restraint as the same method above could be used to direcly see if the binded err is a member of an array of given cases.
enum FooError: Error {
case err1, err2, err3
}
func foo(_ bar: Int) throws {
guard bar != 1 else { throw FooError.err1 }
guard bar != 2 else { throw FooError.err2 }
guard bar != 3 else { throw FooError.err3 }
}
func tryFoo(_ bar: Int) {
do {
try foo(bar)
} catch let err as FooError where [.err1, .err2].contains(err) {
print("1st or 2nd error!")
} catch FooError.err3 {
print("3rd error!")
} catch {}
}
tryFoo(1) // 1st or 2nd error!
tryFoo(2) // 1st or 2nd error!
tryFoo(3) // 3rd error!
This reduces to basically just a variation of the accepted answer (possibly useful for catch blocks covering more than just two cases, but in such cases, possibly the error enum should consider refactoring).
It's just a syntactical problem, it may be improved in Swift 4+.
Right now you can use this:
catch let locksmithError as LocksmithError {
switch locksmithError {
case .Duplicate, .Allocate:
// your code
…
}
}

Get string message from catch error in do/try/catch with Swift

I have 2 classes, 1 is where view controller of form and the other is custom class to validate all forms in my app.
In the enum of the list of possible errors I have these:
enum ValidateErrors: ErrorType{
case Empty(desc: String)
case WrongFormat(desc: String)
}
and inside the validate method from the same class I have these:
guard email.characters.count > 0 else { throw ValidateErrors.Empty(desc:"Empty email.") }
When I do the do/try/catch in the view controller, I need to show an error message with "Empty email." but it show: Empty("Empty email.")
These is the code from the view controller where I make the do/try/catch
do{
try ValidateData.validateEmail(emailTextView.text!)
print("Campo Valido")
}catch let error{
print(error)
}
I found a solution here:
https://developer.apple.com/documentation/swift/error
And in Swift 4 try this for your example:
enum ValidateErrors: Error
{
case Empty(desc: String)
case WrongFormat(desc: String)
}
do
{
try ValidateData.validateEmail(emailTextView.text!)
print("Campo Valido")
}
catch ValidateErrors.Empty(desc: let error)
{
print(error)
}
You might try print(error.desc)