Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 820
cyanide Avatar asked Aug 12 '18 04:08

cyanide


Video Answer


2 Answers

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

like image 113
Martin R Avatar answered Oct 09 '22 17:10

Martin R


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?

like image 23
vadian Avatar answered Oct 09 '22 16:10

vadian