Swift protocol extension: fatal error: can't unsafeBitCast between types of different sizes

843 Views Asked by At

I've defined a protocol with a protocol extension to simplify working with NSError.

protocol NSErrorConvertible: RawRepresentable {
    var domain: String { get }
    var localizedDescription: String { get }
}

extension NSErrorConvertible where RawValue == Int {
    func generateError(parameters parameters: [String] = []) -> NSError {
        let error = self.generateError(format: self.localizedDescription, parameters: parameters)
        return error
    }

    func generateError(format format: String, parameters: [String] = []) -> NSError {
        let description = String(format: format, arguments: parameters) // <-- BREAK
        let error = NSError(domain: self.domain, code: self.rawValue, localizedDescription: description)
        return error
    }
}

extension NSError {
    convenience init(domain: String, code: Int, localizedDescription: String) {
        let userInfo = [NSLocalizedDescriptionKey : localizedDescription]
        self.init(domain: domain, code: code, userInfo: userInfo)
    }
}

Here's how the protocol is used:

enum DefaultEngineErrors: Int, NSErrorConvertible {
    case ImagesNotSupported
    case FooDoesNotHaveABar
    case NilBar

    var domain: String { return "DefaultEngineErrors" }

    var localizedDescription: String {
        switch self {
        case .ImagesNotSupported: return "%s: Images are not supported."
        case .FooDoesNotHaveABar: return "%s: Foo does not have a bar."
        case .NilBar:             return "%s: The bar is nil."
        }
    }
}

And here is how the protocol extension methods are called:

let error = DefaultEngineErrors.FooDoesNotHaveABar.generateError(parameters: ["\(foo.id)"])

When this code is run, the debugger breaks on the line that builds the description in the second generateError extension function and the following message is displayed in the debugger console:

fatal error: can't unsafeBitCast between types of different sizes

Note that if I replace the call to String(format:) with just format, everything works fine.

Q: What is wrong with what I've done here?

In addition to this, I can't examine the parameters in the protocol extension. The following message is displayed when I try:

(lldb) po format
error: <EXPR>:1:1: error: non-nominal type '$__lldb_context' (aka 'Self') cannot be extended
extension $__lldb_context {                            
^         ~~~~~~~~~~~~~~~
<EXPR>:15:5: error: value of type 'DefaultEngineErrors' has no member '$__lldb_wrapped_expr_54'
    $__lldb_injected_self.$__lldb_wrapped_expr_54(     
    ^~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
1

There are 1 best solutions below

1
On BEST ANSWER

Swift string Array is not compatible with CVarArgType... or [CVarArgType]

try using.

extension NSErrorConvertible where RawValue == Int {
    func generateError(format format: String) -> NSError {
        let error = NSError(domain: self.domain, code: self.rawValue, localizedDescription: format)
        return error
    }
    func generateError(args : CVarArgType...) -> NSError {
        let returnString = NSString(format: self.localizedDescription, arguments: getVaList(args)) as String
//        let returnString = withVaList(args) {
//            NSString(format: self.localizedDescription, arguments: $0)
//        } as String
        let error = generateError(format: returnString)
        return error
    }
}

enum DefaultEngineErrors: Int, NSErrorConvertible {
    case ImagesNotSupported
    case FooDoesNotHaveABar
    case FooWithMultiPar
    case NilBar

    var domain: String { return "DefaultEngineErrors" }

    var localizedDescription: String {
        switch self {
        case .ImagesNotSupported: return "%@: Images are not supported."
        case .FooDoesNotHaveABar: return "%@: Foo does not have a bar."
        case .FooWithMultiPar: return "%@:%@ Foo does not have a bar."
        case .NilBar:             return "%@: The bar is nil."
        }
    }
}

print(DefaultEngineErrors.FooDoesNotHaveABar.generateError("test"))
print(DefaultEngineErrors.FooWithMultiPar.generateError("test1","test2"))

Check this