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.