Swift error while downcasting 'Any'
Asked Answered
Z

2

7

The following code is almost exact replica from Apple Documentation and compiles without errors:

guard let firstItem = (rawItems! as? Array<Dictionary<String, Any>>)?.first else {
    throw AnError()
}

let identityRef = firstItem[kSecImportItemIdentity as String] 
               as! SecIdentity?   // !!!

guard let identity = identityRef else {
    throw AnError()
}

The line marked with !!! contains forced downcast, while replacing as! with as quite obviously results in a compilation error 'Any?' is not convertible to 'SecIdentity?'... Indeed SecIdentity is a class while Any may not even be a class.

What I really cannot explain is the following. If I try to make the code safer, by using this

guard let idenity = firstItem[kSecImportItemIdentity as String] as? SecIdentity
else {
    throw AnError()
}

or this

guard let idenityRef = firstItem[kSecImportItemIdentity as String] as? SecIdentity?
else {
    throw AnError()
}

I get a compilation error: Conditional downcast to CoreFoundation type 'SecIdentity' will always succeed

Zippora answered 12/8, 2018 at 4:1 Comment(3)
Do you get the Conditional downcast... compilation error for both guards?--even for the first one with as? SecIdentity. The error makes sense for the last guard: every value will indeed succeed because even if firstItem[kSecImportItemIdentity as String] isn't a SecIdentity, it will become nil when downcast to SecIdentity?, which satisfies that last guard (because of the optional ?). This makes the guard useless, hence the compilation error. Did that make sense?Ascensive
Related: https://mcmap.net/q/1009610/-trouble-retrieving-a-cgcolor-from-a-swift-dictionary/2976878Higginbotham
Daniel. How about the first example?Zippora
Q
8

SecIdentity is “an abstract Core Foundation-type object representing an identity, ” and the type of Core Foundation types can be checked with CFGetTypeID(). So you can check the type ID first. If it matches the type ID of an SecIdentity then the forced cast is safe:

guard let cfIdentity = firstItem[kSecImportItemIdentity as String] as CFTypeRef?,
    CFGetTypeID(cfIdentity) == SecIdentityGetTypeID() else {
        throw AnError()
}
let identity = cfIdentity as! SecIdentity

See also the bug report SR-7015 The CoreFoundation conditional downcast diagnostic is not as helpful as it should be:

The diagnostic should be updated with a message that informs the developer to compare CFTypeIds (with a fixit if possible).

Quadrilateral answered 12/8, 2018 at 7:26 Comment(0)
B
3

CoreFoundation types behave a bit differently from Foundation types.

Don't conditional downcast the identity. If the optional binding succeeds you can force unwrap the identity

guard let idenity = firstItem[kSecImportItemIdentity as String] else { throw AnError() }
var privateKey : SecKey?
let status = SecIdentityCopyPrivateKey(identity as! SecIdentity, &privateKey)

Side note :

Please never write as? SecIdentity?.
Either it's conditional downcast as? SecIdentity or bridge cast an optional as SecIdentity?

Brahmaputra answered 12/8, 2018 at 6:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.