Why Runtime Polymophism on Error differs between Swift 5.0.1 and 5.3.2

Hi,
I got an issue while developing iOS app, and I thought to ask here too since the issue happens between different Swift versions, maybe someone can explain the cause.

I have this sample code:

protocol Proto { var value: String { get } }
extension URLError: Proto { var value: String { return "hello" } }

func value(_ error: Error) {
    if let error = error as? Proto {
        print("proto", error.value)
    } else {
        print(":(")
    }
}

I except with the runtime-polymorphism the casting should work, and following call should print proto hello when passing URLError object:

value(URLError(.badURL))

But here is what I got, in Swift 5.0 the casting works, in Swift 5.3 the casting does not work.

Here trying replit.com for personal testing and the casting works:

enter image description here

Their swift version is 5.0.x as shown:

Swift version 5.0.1 (swift-5.0.1-RELEASE)

When I tested the code on local playground, the casting doesn't work, and my swift version is:

Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)

Also someone tested it with Swift 5.4 on his machine, and the casting didn't work for him too.

I hope if there is an answer to this case to be explained as simple as possible for a beginner.

Thanks

Try adding this as the first line in value(): print(type(of: error))
You might be surprised what it prints out.

I do have a thought what it will print in both versions.

For version 5.0, it prints:

URLError
proto hello

In version 5.3, it prints:

NSError
:(

And I have an answer why following expression works:

let error = URLError(.badURL) as NSError
print(type(of: a)) // NSError

This works for both versions of Swift.

It relates to autobridging in swift, there are a range of types that exist in swift which have counterparts in objective-c .. NSString <--> String is another example.

So, I believe this is because the struct URLError conforms to Error protocol, and the class NSError conforms to Error protocol, so it is like Error protocol is the bridge for this casting .. I kinda feel sense for it.

But,
I am a URLError
Casted up to Error
Asked at runtime what type you actually are
Should I reply I am NSError?!! is NSError is in the between anyway?

Swift 5.0 does see the actual object which is URLError, but Swift 5.3 doesn't.

When I pass the object already casted to NSError:

value(URLError(.badURL) as NSError)

Then both of them see it as NSError at then end:

print(type(of: error)) // NSError

Which I feel sense about this case.

After I finished this comment, I feel was it like a buggy behavior in 5.0 and fixed in later versions? And once you cast up any type conforming to Error protocol, its actual type gone hidden from runtime check?

Terms of Service

Privacy Policy

Cookie Policy