Using Keychain Services in Swift 2

Has anyone found the/a recommending way to use Keychain Services since Swift 2. The following code from Stack Overflow is from Swift 1.2 and still seems to work, but it's nothing I'd even come up with without help.


  let query = [
  kSecClass as String: kSecClassGenericPassword as String,
  kSecAttrService as String: service,
  kSecAttrAccount as String: account,
  kSecReturnData as String: true
  ]

  var result: NSData?
  let status = withUnsafeMutablePointer (&result) { SecItemCopyMatching (query, UnsafeMutablePointer ($0)) }


I'm wondering if there's now a clearer way.

Accepted Answer

Your code shown above contains a workaround needed till Swift 1.1. (Seems to work in Swift 1.2 or 2, but not recommended.)


Here's a code I use in Swift 1.2 (works in Swift 2, I believe), though, I'm not sure you find this cleaner.

        let keychainQuery: [NSObject: AnyObject] =  [
            kSecClass : kSecClassGenericPassword,
            kSecAttrService : serviceName,
            kSecAttrAccount : userAccount,
            kSecReturnData : kCFBooleanTrue,
            kSecMatchLimit : kSecMatchLimitOne]
   
        var dataTypeRef: Unmanaged<AnyObject>?
        let status = SecItemCopyMatching(keychainQuery, &dataTypeRef)
        if status == errSecSuccess,
            let retrievedData = dataTypeRef?.takeRetainedValue() as! NSData? {
            //process retrivedData
        } else {
            //process error
        }


UPDATE for Swift 2.2 (or update was needed for former versions...)

I believe this compiles and works in Swift 2.2:

        let keychainQuery: [NSObject: AnyObject] =  [
            kSecClass : kSecClassGenericPassword,
            kSecAttrService : serviceName,
            kSecAttrAccount : userAccount,
            kSecReturnData : kCFBooleanTrue,
            kSecMatchLimit : kSecMatchLimitOne]
     
        var dataTypeRef: AnyObject?
        let status = SecItemCopyMatching(keychainQuery, &dataTypeRef)
        if status == errSecSuccess,
            let retrievedData = dataTypeRef as! NSData? {
            //process retrivedData
        } else {
            //process error
        }

Actually, that makes quite a lot of sense. I just would never have figured out the connection between '&' and 'UnsafeMutablePointer<…>' (which is the type of the second parameter) without someone pointing it out.

I wrote a blog post about keychain + swift 2 here: http://www.splinter.com.au/2015/06/21/swift-keychain-wrapper/


It takes advantage of the new 'throws' ability, i think it's worth a read.

I know it's been awhile, but it looks like this code


        var dataTypeRef: Unmanaged<AnyObject>?
        var status = SecItemCopyMatching(query, &dataTypeRef)


doesn't compile in swift 2.2. It says "Cannot convert value of type 'Unmanaged<AnyObject>? to expected argument type 'AnyObject?' For that reason I'm going to go with the strange way below:


  let status = withUnsafeMutablePointer (&result) { SecItemCopyMatching (query, UnsafeMutablePointer ($0)) }

Added an updated code for Swift 2.2.


`SecItemCopyMatching(_:_:)` has changed its signature to:

SecItemCopyMatching(query: CFDictionary, _ result: UnsafeMutablePointer<AnyObject?>) -> OSStatus

So you can write the core part as:

        var dataTypeRef: AnyObject?
        let status = SecItemCopyMatching(keychainQuery, &dataTypeRef)


Do you need an updated code for Swift 3?

It doesn't look like this code works with Swift 3 anymore, you receive a compilation error that you need to use withMemoryRebound() now. Does anyone know the proper way to call this in Swift 3? I've been playing with the syntax but I haven't been able to come up with it yet. This is as far as I've gotten, but I receive an error "Cannot convert value of type 'UnsafeMutablePointer<_>' to expected argument type 'UnsafeMutablePointer<CFTypeRef?>?'


_ = withUnsafeMutablePointer(to: &result) {

$0.withMemoryRebound(to: CFTypeRef.self, capacity: 1, {

SecItemCopyMatching(searchDictionary, $0)

})

}

The signature of `SecItemCopyMatching` has not much changed in Swift 3:

func SecItemCopyMatching(CFDictionary, UnsafeMutablePointer<CFTypeRef?>?)

func SecItemCopyMatching(_ query: CFDictionary,
                         _ result: UnsafeMutablePointer<CFTypeRef?>?) -> OSStatus

And `CFTypeRef` is a typealias of `AnyObject`.


So, this works in Swift 3:

        var dataTypeRef: CFTypeRef?
        let status = SecItemCopyMatching(keychainQuery as CFDictionary, &dataTypeRef)


Some parts depend on how you declared `searchDictionary` and `result`, but you usally have no need to use `withUnsafeSomething`.

Using Keychain Services in Swift 2
 
 
Q