Querying iOS Keychain using Swift
I'm stuck converting the Keychain query result using Swift.
My request seems to be working:
let queryAttributes = NSDictionary(objects: [kSecClassGenericPassword, "MyService", "MyAccount", true],
forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData])
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
var dataTypeRef : Unmanaged<AnyObject>?
let status = SecItemCopyMatching(queryAttributes, &dataTypeRef);
let retrievedData : NSData = dataTypeRef!.takeRetainedValue() as NSData
*** ^^^^can't compile this line^^^^
})
My problem is, code won't compile:
Bitcast requires both operands to be pointer or neither
%114 = bitcast %objc_object* %113 to %PSs9AnyObject_, !dbg !487
Bitcast requires both operands to be pointer or neither
%115 = bitcast %PSs9AnyObject_ %114 to i8*, !dbg !487
LLVM ERROR: Broken function found, compilation aborted!
I don't know how to convert Unmanaged<AnyObject>
to NSData
.
Any ideas?
Solution 1:
Use withUnsafeMutablePointer
function and UnsafeMutablePointer
struct to retrieving the data, such as the following:
var result: AnyObject?
var status = withUnsafeMutablePointer(&result) { SecItemCopyMatching(queryAttributes, UnsafeMutablePointer($0)) }
if status == errSecSuccess {
if let data = result as NSData? {
if let string = NSString(data: data, encoding: NSUTF8StringEncoding) {
// ...
}
}
}
it works fine with release (Fastest [-O]) build.
Solution 2:
Looks like you have hit a compiler bug, which you should report. You can take a different path to retrieving the value, such as the following:
var dataTypeRef :Unmanaged<AnyObject>?
let status = SecItemCopyMatching(queryAttributes, &dataTypeRef);
let opaque = dataTypeRef?.toOpaque()
if let op = opaque? {
let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
}
The error manifests itself when using AnyObject
as a type parameter T
of Unmanaged<T>
. The following code snippet compiles without issue, which uses a more specific type, CFError
:
let url = NSURL(string:"dummy")
var errorRef: Unmanaged<CFError>?
let succeeded = CTFontManagerRegisterFontsForURL(url, .Process, &errorRef)
if errorRef {
let error = errorRef!.takeRetainedValue()
}
As the Keychain API returns a different result depending of the query attributes, the use of AnyObject
is required.