swiftdowncastoptional-values

Swift error while downcasting 'Any'


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


Solution

  • 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).