Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Error type initializer

Tags:

swift

Let me prefix with why I think this is not a duplicate of How to provide a localized description with an Error type in Swift?

The answers provided will still result in some static/class function call and not an initializer style or require casting to NSError (which I like to avoid).


A brief summery of the reasons:

A Swift function that throws does not declare the error type. We cannot enforce the catch to pass a custom type that simply conforms to the Error protocol. Knowing that, on the do-catch side, we get no help from the compiler as to what type (if custom) of error we get and by default we'll expect the known NSError properties. Otherwise, we need to simply rely on textual documentations explaining the type of errors we can catch, as the catch will simply pass an Error type.

Now, unlike NSError, Error is a protocol where the properties we get in userInfo are read only. So we resort to constructing an NSError type, and casting it as Error.


I am trying to create a simple clean struct that return an Error type (not NSError) where I can throw like:

throw MYError(domain: "com.somthing.error", code: 0, userInfo: [NSLocalizedDescriptionKey : "Something bad happened"])

Main issue is that the only way to set the NSLocalizedDescriptionKey, is by initializing an NSError object. Doing so will require casting to Error type (which is something I'm trying to avoid).

I first tried to use extension Error {..., but cannot use an initializer.

If I use a struct conforming to Error protocol (struct MyError: Error {...), I still have the problem of localizedDescription is get only.

What I use is actually something like:

struct MYError: Error {

    static func with(domain: String = "com.somthing.error", code: Int = 0, localizedDescription: String) -> Error {
        return NSError(domain: domain, code: code, userInfo: [NSLocalizedDescriptionKey : localizedDescription]) as Error
    }
}

Which I can use like:

throw MYError.with(localizedDescription: "Some Error has happened")

Intuitively, I would expect a type like MYError to just use an initializer MYError(domain:..., not a static function.


The more Swifty way would be something like:

enum ErrorType: Error {
    case one
    case two
    case three(with: String)
}

...

// In some function:
throw ErrorThrown.three(with: "Three!")

...

// Catch like:
catch ErrorType.three(let str) {
    print("Error three: \(str)")
}

It is not clear if we're there yet. It seems that NSError is still much at play where I know I can expect to get a localizedDescription an optional localizedFailureReason and the familiar NSError properties.

like image 713
bauerMusic Avatar asked Nov 28 '22 22:11

bauerMusic


2 Answers

Similarly as in How to provide a localized description with an Error type in Swift? you can define a custom error type adopting the LocalizedError protocol:

public struct MyError: Error {
    let msg: String

}

extension MyError: LocalizedError {
    public var errorDescription: String? {
            return NSLocalizedString(msg, comment: "")
    }
}

Example:

do {
    throw MyError(msg: "Something happened")
} catch let error {
    print(error.localizedDescription)
}

This prints the localized version of the given message. Note that error in the catch-clause is a general Error, so the caller does not need to cast it to the concrete error type (or even know which error type is thrown).

like image 70
Martin R Avatar answered Dec 17 '22 01:12

Martin R


Error is a protocol, you can throw anything which conforms to that protocol

For example

struct MYError : Error {
    let description : String
    let domain : String

    var localizedDescription: String {
        return NSLocalizedString(description, comment: "")
    }
}

And you can use it:

func test() throws
{
  throw MYError(description: "Some Error has happened", domain: "com.somthing.error")
}

do {
    try test()
} catch let error as MYError{
    print("error: ", error.domain, error.localizedDescription)
}
like image 29
vadian Avatar answered Dec 17 '22 00:12

vadian