Thrown error gets caught in the wrong catch - ios

I'm using Realm in my app. I'm trying a implement a unified error handling interface for all types of errors. For example, I have a class that handles all User related tasks. So I have an enum called UserError.
import Foundation
import RealmSwift
enum UserError: Error {
case doesNotExist
case alreadyExists
case databaseError(error: Error)
}
class UserHelper {
/// Fetch user object for the given email if exists.
func getUser(forEmail email: String) throws -> User {
do {
guard let user = try Realm().object(ofType: User.self, forPrimaryKey: email) else {
throw UserError.doesNotExist
}
return user
} catch {
throw UserError.databaseError(error: error)
}
}
}
I have a separate catch-all databaseError enum value to catch all Realm related errors.
The problem with my method is, when I throw the doesNotExist error inside the do-catch where I query the database, that error gets caught inside this method's catch and gets re-thrown as a databaseError. I want the error to arrive as the original type, doesNotExist.
How do I accomplish this?

Your catch clause is unqualified and therefore catches every error thrown within the do clause, whether or not it's a Realm error or a UserError. You can try making the catch clause conditional, since you only want to throw a databaseError() if the error is indeed a Realm error:
do {
guard let user = try Realm().object(ofType: User.self, forPrimaryKey: email) else {
throw UserError.doesNotExist
}
return user
} catch let error as Realm.Error {
throw UserError.databaseError(error: error)
}
If you do this, you can also change your UserError.databaseError case to take a Realm.Error instead of a plain Error, giving whatever code catches your error more information about the specific database error.

Related

Catching vs. not catching specific error types in dart - any difference in behavior?

My understanding of catching errors was that if a certain error is 'caught', the code in a method following the catch block would not execute unless you have a finally statement afterwards.
However, if I catch a certain error type, but don't have a return or a throw/rethrow statement at the end, it does seem to keep going with the further code in the method.
I am catching a certain exception type, with the statement on CustomException catch (error) but I'm not planning to rethrow the error, and I don't need to do anything with the actual error. I don't have a return or a rethrow statement, as I assumed the catch block. I have two questions to make sure I'm understanding this behavior properly:
First question - if I don't plan to use the specific error inside my catch block, does adding catch (error) after on CustomException actually make a difference? It seems like it's ok to do it with or without it.
Second question - does my catch block need to have a return/throw/rethrow statement at the end in all cases to prevent the method from performing any further code?
Code example 1: With the catch (error) statement:
try {
...
} on CustomException catch (error) {
...
} catch (error) {
rethrow;
}
print('Is this method still running?');
Code example 2: without the catch (error) statement:
try {
...
} on CustomException {
...
} catch (error) {
rethrow;
}
print('Is this method still running?');
Your two examples aren't the same; one catches CustomException and the other catches PlatformException. Presuming that you meant them to be the same, then the difference between on CustomException and on CustomException catch (error) is that the second way allows you to do something with the thrown object (or if you use on CustomException catch (error, stacktrace), to do something with the stacktrace too).
The Dart Language Tour covers this:
Use on when you need to specify the exception type. Use catch when your exception handler needs the exception object.
Additionally, doing:
catch (error) {
rethrow;
}
is unnecessary noise. Just omit it and let the thrown exception go uncaught.

Swift: catch error case with parameters but with access to the error object

I would like to catch a custom error with parameters, but also get access to the error to call some function on it. How do you do that? (I don't want to just catch the CustomError and have a switch inside the catch block.)
enum CustomError: Error {
case error(code: Int)
func describe() -> String {
...
}
}
...
do {
try bad()
} catch let error as CustomError.error(let code) { // This doesn't work
print(error.describe())
} catch {
....
}
A bit repetitive, but after reading through the language reference, I don't think there is a way to elegantly do this:
do {
try bad()
} catch CustomError.error(let code) {
print(CustomError.error(code: code).describe())
} catch {
}
This makes use of the fact that code and the case .error are the only 2 pieces of state that CustomError has. This means that we can recreate the error object, and call describe on that.

How to use swift-4 Promises then, done, catch and other blocks

I would like to learn about promises in swift-4. How to use multiple then statements and done, catch blocks.
Here I am trying to get the value from the promise. But I'm getting errors. Could someone help me to understand promises?
Here is my code.
import UIKit
import PromiseKit
struct User {
var firstname : String?
var lastname : String?
}
struct APIError {
var message : String?
}
class ViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let userPromise : Promise = self.getUserDetails()
userPromise.then { user -> Void in
//print(user.f)
}
}
func getUserDetails()->Promise<User> {
return Promise<User> { resolve in
let user = User(firstname: "Scot", lastname: "dem")
if ((user.firstname?.count) != nil) {
resolve.fulfill(user)
} else {
let error = APIError(message: "User not valid")
resolve.reject(error as! Error)
}
}
}
}
Once I get the user details I want to make a full name, uppercase promises which are dependent on userPromise.
I would like to use multiple then, done, finally blocks. Just want to understand usage.
Why I'm getting an error here when we use userPromise.then { user -> Void in
what should I give inside the block
In PromiseKit 6, then can no longer return Void. This is mainly due to the tuplegate issue in Swift 4.
Quote from PromieKit 6 Release News
With PromiseKit our then did multiple things, and we relied on Swift
to infer the correct then from context. However with multiple line
thens it would fail to do this, and instead of telling you that the
situation was ambiguous it would invent some other error. Often the
dreaded cannot convert T to AnyPromise. We have a troubleshooting
guide to combat this but I believe in tools that just work, and when
you spend 4 years waiting for Swift to fix the issue and Swift doesn’t
fix the issue, what do you do? We chose to find a solution at the
higher level.
So we split then into then, done and map.
then is fed the previous promise value and requires you return a promise.
done is fed the previous promise value and returns a Void promise (which is 80% of chain usage)
map is fed the previous promise value and requires you return a non-promise, ie. a value.
Hence .then { (user) -> Void in is no longer valid and that's why you're getting an error.
It's now designed to return a Thenable like so:
userPromise.then { user -> Promise<User> in
//return yet another promise
}
The .then that used to return Void is now it's own .done.
i.e:
userPromise.done { (user) in
print(user)
}
So when you mix 'em up:
We get:
userPromise
.then { (user) -> Promise<User> in
//return another Promise
return self.uppercasePromise(on: user)
}
.done { (user) in
/*
Depending on your sequence, no more promises are left
and you should have a matured user object by now
*/
print(user)
}
.catch { (error) in
print(error)
}
.finally {
print("finally")
}
func uppercasePromise(on user: User) -> Promise<User> {
/*
Didn't understand your statement but do whatever you meant when you said:
"Once I get the user details I want to make a full name, "
uppercase promises which are dependent on userPromise."
*/
return Promise { seal in
seal.fulfill(user)
}
}

What kinds of errors does a func throw out in Swift?

I see some methods throw out errors in Apple's Documentation. But I can't find any information about what it throws.
Like this methods below. It's in FileManager class.
func moveItem(at srcURL: URL, to dstURL: URL) throws
I want to know what kinds of errors it throws. Where can I get related information?
Unlike Java, where the throws declaration requires a type, in Swift you will not know, what type of Error will be thrown. The only thing you know is that the object conforms to the Error-protocol.
If you know that a function throws a certein Error (because it's well documented), you will need to cast the caught object properly.
Example:
do {
try moveItem(from: someUrl, to: otherUrl)
} catch {
//there will automatically be a local variable called "error" in this block
// let's assume, the function throws a MoveItemError (such information should be in the documentation)
if error is MoveItemError {
let moveError = error as! MoveItemError //since you've already checked that error is an MoveItemError, you can force-cast
} else {
//some other error. Without casting it, you can only use the properties and functions declared in the "Error"-protocol
}
}

Simplest way to throw an error/exception with a custom message in Swift?

I want to do something in Swift that I'm used to doing in multiple other languages: throw a runtime exception with a custom message. For example (in Java):
throw new RuntimeException("A custom message here")
I understand that I can throw enum types that conform to the ErrorType protocol, but I don't want to have to define enums for every type of error I throw. Ideally, I'd like to be able mimic the example above as closely as possible. I looked into creating a custom class that implements the ErrorType protocol, but I can't even figure out that what that protocol requires. Ideas?
The simplest approach is probably to define one custom enum with just one case that has a String attached to it:
enum MyError: ErrorType {
case runtimeError(String)
}
Or, as of Swift 4:
enum MyError: Error {
case runtimeError(String)
}
Example usage would be something like:
func someFunction() throws {
throw MyError.runtimeError("some message")
}
do {
try someFunction()
} catch MyError.runtimeError(let errorMessage) {
print(errorMessage)
}
If you wish to use existing Error types, the most general one would be an NSError, and you could make a factory method to create and throw one with a custom message.
The simplest way is to make String conform to Error:
extension String: Error {}
Then you can just throw a string:
throw "Some Error"
To make the string itself be the localizedString of the error you can instead extend LocalizedError:
extension String: LocalizedError {
public var errorDescription: String? { return self }
}
#nick-keets's solution is most elegant, but it did break down for me in test target with the following compile time error:
Redundant conformance of 'String' to protocol 'Error'
Here's another approach:
struct RuntimeError: Error {
let message: String
init(_ message: String) {
self.message = message
}
public var localizedDescription: String {
return message
}
}
And to use:
throw RuntimeError("Error message.")
Swift 4:
As per:
https://developer.apple.com/documentation/foundation/nserror
if you don't want to define a custom exception, you could use a standard NSError object as follows:
import Foundation
do {
throw NSError(domain: "my error domain", code: 42, userInfo: ["ui1":12, "ui2":"val2"] )
}
catch let error as NSError {
print("Caught NSError: \(error.localizedDescription), \(error.domain), \(error.code)")
let uis = error.userInfo
print("\tUser info:")
for (key,value) in uis {
print("\t\tkey=\(key), value=\(value)")
}
}
Prints:
Caught NSError: The operation could not be completed, my error domain, 42
User info:
key=ui1, value=12
key=ui2, value=val2
This allows you to provide a custom string (the error domain), plus a numeric code and a dictionary with all the additional data you need, of any type.
N.B.: this was tested on OS=Linux (Ubuntu 16.04 LTS).
Check this cool version out. The idea is to implement both String and ErrorType protocols and use the error's rawValue.
enum UserValidationError: String, Error {
case noFirstNameProvided = "Please insert your first name."
case noLastNameProvided = "Please insert your last name."
case noAgeProvided = "Please insert your age."
case noEmailProvided = "Please insert your email."
}
Usage:
do {
try User.define(firstName,
lastName: lastName,
age: age,
email: email,
gender: gender,
location: location,
phone: phone)
}
catch let error as User.UserValidationError {
print(error.rawValue)
return
}
Simplest solution without extra extensions, enums, classes and etc.:
NSException(name:NSExceptionName(rawValue: "name"), reason:"reason", userInfo:nil).raise()
In case you don't need to catch the error and you want to immediately stop the application you can use a fatalError:
fatalError ("Custom message here")
Based on #Nick keets answer, here is a more complete example:
extension String: Error {} // Enables you to throw a string
extension String: LocalizedError { // Adds error.localizedDescription to Error instances
public var errorDescription: String? { return self }
}
func test(color: NSColor) throws{
if color == .red {
throw "I don't like red"
}else if color == .green {
throw "I'm not into green"
}else {
throw "I like all other colors"
}
}
do {
try test(color: .green)
} catch let error where error.localizedDescription == "I don't like red"{
Swift.print ("Error: \(error)") // "I don't like red"
}catch let error {
Swift.print ("Other cases: Error: \(error.localizedDescription)") // I like all other colors
}
Originally published on my swift blog: http://eon.codes/blog/2017/09/01/throwing-simple-errors/
First, let's see few usage examples, then how to make those samples work (Definition).
Usage
do {
throw MyError.Failure
} catch {
print(error.localizedDescription)
}
Or more specific style:
do {
try somethingThatThrows()
} catch MyError.Failure {
// Handle special case here.
} catch MyError.Rejected {
// Another special case...
} catch {
print(error.localizedDescription)
}
Also, categorization is possible:
do {
// ...
} catch is MyOtherErrorEnum {
// If you handle entire category equally.
} catch let error as MyError {
// Or handle few cases equally (without string-compare).
switch error {
case .Failure:
fallthrough;
case .Rejected:
myShowErrorDialog(error);
default:
break
}
}
Definition
public enum MyError: String, LocalizedError {
case Failure = "Connection fail - double check internet access."
case Rejected = "Invalid credentials, try again."
case Unknown = "Unexpected REST-API error."
public var errorDescription: String? { self.rawValue }
}
Pros and Cons
Swift defines error variable automatically, and a handler only needs to read localizedDescription property.
But that is vague, and we should use "catch MyError.Failure {}" style instead (to be clear about what-case we handle), although, categorization is possible as shown in usage example.
Teodor-Ciuraru's answer (which's almost equal) still needs a long manual cast (like "catch let error as User.UserValidationError { ... }").
The accepted categorization-enum approach's disadvantages:
Is too vague as he comments himself, so that catchers may need to compare String message!? (just to know exact error).
For throwing same more than once, needs copy/pasting message!!
Also, needs a long phrase as well, like "catch MyError.runtimeError(let errorMessage) { ... }".
The NSException approach has same disadvantages of categorization-enum approach (except maybe shorter catching paragraph), also, even if put in a factory method to create and throw, is quite complicated.
Conclusion
This completes other existing solutions, by simply using LocalizedError instead of Error, and hopefully saves someone from reading all other posts like me.
(My laziness sometimes causes me a lot of work.)
Testing
import Foundation
import XCTest
#testable import MyApp
class MyErrorTest: XCTestCase {
func testErrorDescription_beSameAfterThrow() {
let obj = MyError.Rejected;
let msg = "Invalid credentials, try again."
XCTAssertEqual(obj.rawValue, msg);
XCTAssertEqual(obj.localizedDescription, msg);
do {
throw obj;
} catch {
XCTAssertEqual(error.localizedDescription, msg);
}
}
func testThrow_triggersCorrectCatch() {
// Specific.
var caught = "None"
do {
throw MyError.Rejected;
} catch MyError.Failure {
caught = "Failure"
} catch MyError.Rejected {
caught = "Successful reject"
} catch {
caught = "Default"
}
XCTAssertEqual(caught, "Successful reject");
}
}
Other tools:
#1 If implementing errorDescription for each enum is a pain, then implement it once for all, like:
extension RawRepresentable where RawValue == String, Self: LocalizedError {
public var errorDescription: String? {
return self.rawValue;
}
}
Above only adds logic to enums that already extend LocalizedError (but one could remove "Self: LocalizedError" part, to make it apply to any string-enum).
#2 What if we need additional context, like FileNotFound with file-path associated? see my other post for that:
https://stackoverflow.com/a/70448052/8740349
Basically, copy and add LocalizedErrorEnum from above link into your project once, and reuse as many times as required with associative-enums.
I like #Alexander-Borisenko's answer, but the localized description was not returned when caught as an Error. It seems that you need to use LocalizedError instead:
struct RuntimeError: LocalizedError
{
let message: String
init(_ message: String)
{
self.message = message
}
public var errorDescription: String?
{
return message
}
}
See this answer for more details.
First, let's see LocalizedErrorEnum's usage examples, then how to make those samples work (in Sorce-code section).
Usage
do {
let path = "/my/path/to/file.txt";
throw MyErrorCategory.FileNotFound(
atPath: path
);
} catch {
print(error.localizedDescription);
}
Output:
Failed to find file. {
atPath: /my/path/to/file.txt
}
Definition:
public enum MyErrorCategory: LocalizedErrorEnum {
case FileNotFound(String = "Failed to find file.", atPath: String)
case Connection(String = "Connection fail - double check internet access.")
}
The first argument is treated as message (in LocalizedErrorEnum enum).
Features required (background)
#1 Firstly, I want messages without copy/pasting, and that with ability to catch a group of different error-cases, without listing each-case (solution, enum is pretty unique without copy/paste need, and each enum can be considered another group).
#2 Secondly, some errors like "FileNotFound" need to have variable context/details, like for file-path (but Raw-Value enum does not support instance-variables, and unlike #1, the built-in enum is not the solution).
#3 Lastly, I want to be able to catch each case separately, NOT catching entire struct and/or class then doing switch inside the catch, and want to avoid forgeting the rethrow of cases we don't handle.
Source-code (solution meeting requirements)
Simply, copy and add LocalizedErrorEnum from below into your project once, and reuse as many times as required with associative-enums.
public protocol LocalizedErrorEnum: LocalizedError {
var errorDescription: String? { get }
}
extension LocalizedErrorEnum {
public var errorDescription: String? {
if let current = Mirror(reflecting: self).children.first {
let mirror = Mirror(reflecting: current.value);
// Initial error description.
let message = mirror.children.first?.value as? String
?? current.label ?? "Unknown-case";
var context = "";
// Iterate additional context.
var i = 0;
for associated in mirror.children {
if i >= 1 {
if let text = associated.value as? String {
context += "\n ";
if let label: String = associated.label {
context += "\(label): "
}
context += text;
}
}
i += 1;
}
return context.isEmpty ? message : (
message + " {" + context + "\n}"
);
}
return "\(self)";
}
}
Note that as mentioned on my profile, using above code under Apache 2.0 license is allowed as well (without attribution need).
See also my other answer if you don't need additional context-variable with error (or for a comparison with other approaches).
Throwing code should make clear whether the error message is appropriate for display to end users or is only intended for developer debugging. To indicate a description is displayable to the user, I use a struct DisplayableError that implements the LocalizedError protocol.
struct DisplayableError: Error, LocalizedError {
let errorDescription: String?
init(_ description: String) {
errorDescription = description
}
}
Usage for throwing:
throw DisplayableError("Out of pixie dust.")
Usage for display:
let messageToDisplay = error.localizedDescription
I would like to suggest a variation of some of the proposed solutions:
public enum MyError: Error {
var localizedDescription: String {
get {
switch(self) {
case .network(let message, let code):
return "\(message) (\(code))"
case .invalidInput(message: let message):
return message
}
}
}
case network(message: String, code: Int)
case invalidInput(message: String)
}
It's a little more work to create but it provides the best of all worlds:
It's an enum so it can be used in a switch statement.
All the errors must be created with a message that can be a different one even for the same types of error (unlike enums that extend String)
It provides the message under the localizedDescription that every developer is expecting.
To reiterate #pj-finnegan's answer, several people's comments, and the footnote of the accepted answer…
I prefer several other answers provided here (if i'm looking for best practices). But if I'm answering the question as asked, the simplest way to do this (IFF you are are in iOS/macOS/…) is to used the bridged type NSError.
func myFunction(meNoLikey:Bool) throws {
guard meNoLikey == false else {
throw NSError(domain: "SubsystemOfMyApp", code: 99, userInfo: [NSLocalizedDescriptionKey: "My Message!"] )
}
// safe to carry on…
}
You can decide to have meaningful domains or codes, or not. The userInfo key NSLocalizedDescriptionKey is the only thing needed to deliver the message you request.
Look up NSError.UserInfoKey for any additional details you want to provide in the userInfo. You can also add anything you want, if you would like to deliver details to anyone catching the error.

Resources