Search code examples
swifterror-handlingtry-catchalamofire

Swift Error handling best practices


Im used to typical try catch blocks to handle exceptions of any function or block of code like objective C.

However, after making leap of faith to Swift and reading about Swift’s error handling; it’s not much what I expected. You must handle your exceptions by throwing errors and guard and checking if else everything.

I need general Swift error handling tips and best practices, and especially for the following cases:

  1. Im using Alamofire for calling services, which has closure, and calling it through another function with closure too. AFAIK I can’t throw error inside async code, so what is best practice for such case? will try to update with code sample

  2. Is it favourable to have every function in the app throw errors? Just in case ? Like checking every single value or result.

  3. Can I have a Singleton Error handling module?

Thanks in advance


Solution

  • You must handle your exceptions by throwing errors and guard and checking if else everything.

    This is an assumption that doesn't have to be true. If your code is structured correctly, you shouldn't have to check everything with if lets and throws.


    I need general Swift error handling tips and best practices ...

    Before you look at anything else, read the following pages in order. They should give you a good background on best practices.

    1. Error Protocol Apple Developer Documentation
    2. Error Handling - Apple's Swift Programming Language Guide
    3. Magical Error Handling in Swift - Ray Wenderlich

    Im using Alamofire for calling services, which has closure, and calling it through another function with closure too. AFAIK I can’t throw error inside async code, so what is best practice for such case?

    You can. A good practice is to pass a closure as a parameter to your service-calling function, then call the closure when the asynchronous operation is complete, like so:

    functionThatCallsAService(completion: @escaping (Data?, NetworkingErrors?) -> ()) {
        session.dataTask(with: request) { data, response, error in
            guard error == nil else {
                completion(nil, NetworkingErrors.returnedError(error!))
                return
            }
            completion(data, nil)
        }.resume()
    }
    
    enum NetworkingErrors: Error {
        case errorParsingJSON
        case noInternetConnection
        case dataReturnedNil
        case returnedError(Error)
        case invalidStatusCode(Int)
        case customError(String)
    }
    

    Is it favourable to have every function in the app throw errors? Just in case? Like checking every single value or result.

    If you know for sure that a function or value won't be nil/cause a runtime error/throw an error, then don't check for it! But generally, according to the web pages above, you should check for nil and/or errors from calling a web service, interacting with the file system, creating a complex object, etc.


    Can I have a Singleton Error handling module?

    You technically can, but I don't see any reason to do so besides logging.