4

I am trying to implement the CFDictionaryGetValue() as shown in this appledoc listing 3:

server = CFDictionaryGetValue(credentialDict, kSecAttrServer);
userName = CFDictionaryGetValue(credentialDict, kSecAttrAccount);
password = CFDictionaryGetValue(credentialDict, kSecSharedPassword);

and I implemented like this

let userName = CFDictionaryGetValue(credentialDict, kSecAttrAccount)
let password = CFDictionaryGetValue(credentialDict, kSecSharedPassword)

but I get the error "Cannot invoke "CFDictionaryGetValue" with argument list of type "(CFDictionaryRef, CFStringRef)"

I dont understand how this is different from how the apple doc implemented it.

thanks

tnek316
  • 630
  • 1
  • 5
  • 16

2 Answers2

4

Yes there is an issue with that code mentioned in radar here

I found a workaround for you:

  let unsafeCred = CFArrayGetValueAtIndex(credentials, 0)
  let credential: CFDictionaryRef = unsafeBitCast(unsafeCred, CFDictionaryRef.self)
  let dict: Dictionary<String, String> = credential as! Dictionary<String, String>
  let username = dict[kSecAttrAccount as String]
  let password = dict[kSecSharedPassword.takeRetainedValue() as! String]

UPDATE

SecRequestSharedWebCredential("www.reddit.com", .None)  {
  credentials, error in 
    if CFArrayGetCount(credentials) > 0 {
      let dict = unsafeBitCast(
               CFArrayGetValueAtIndex(credentials, 0), 
               CFDictionaryRef.self) as Dictionary
    let username = dict[kSecAttrAccount as String]
    let password = dict[kSecSharedPassword as String]
    login(username, password)
  }
}

Please mind the difference here:

iOS8

var kSecSharedPassword: Unmanaged<AnyObject>!
func SecRequestSharedWebCredential(_ fqdn: CFString!,
                                   _ account: CFString!,
                                   _ completionHandler: ((CFArray!,
                                            CFError!) -> Void)!)

iOS9

let kSecSharedPassword: CFString
func SecRequestSharedWebCredential(_ fqdn: CFString?, 
                                   _ account: CFString?, 
                                   _ completionHandler: (CFArray?, CFError?) -> Void)

Here are all the changes for swift2/iOS9/xcode7

Marek H
  • 5,173
  • 3
  • 31
  • 42
  • 1
    Upon upgrading to Swift 2 and Xcode 7, started receiving this error: `Value of type 'CFString' has no member 'takeRetainedValue'` on this code: `let password = dict[kSecSharedPassword.takeRetainedValue() as! String]`. It looks like you can safely change this to just: `let password = dict[kSecSharedPassword as String]` because in Swift 2, the definition of kSecSharedPassword changed from `var kSecSharedPassword: Unmanaged!` to `var kSecSharedPassword: CFString` – Mason G. Zhwiti Sep 10 '15 at 19:14
  • It's the iOS9 API that has changed (this is triggering the error). Anyway, I updated the comment. – Marek H Sep 10 '15 at 19:42
  • Are you sure it's iOS 9 and not Swift 2? As I'm building an app that runs on iOS 8+ in Xcode 7 GM, and the newer code is working. – Mason G. Zhwiti Sep 10 '15 at 23:17
  • Another issue I'm encountering now (at least when running this code on iOS 9) is an EXC_BAD_INSTRUCTION when this line is hit: `let dict: Dictionary = credential as! Dictionary` – Mason G. Zhwiti Sep 11 '15 at 00:46
  • I've now worked past the above issue by changing my code to: `let unsafeCred = CFArrayGetValueAtIndex(credentials, 0) let credential: CFDictionaryRef = unsafeBitCast(unsafeCred, CFDictionaryRef.self) let username = unsafeBitCast(CFDictionaryGetValue(credential, unsafeAddressOf(kSecAttrAccount)), NSString.self) as String let password = unsafeBitCast(CFDictionaryGetValue(credential, unsafeAddressOf(kSecSharedPassword)), NSString.self) as String ` – Mason G. Zhwiti Sep 11 '15 at 01:03
0

Updated answer for XCode 7.3 and Swift 2.2

      if CFArrayGetCount(credentials) > 0 {
            let dict = unsafeBitCast(CFArrayGetValueAtIndex(credentials, 0), CFDictionaryRef.self) as NSDictionary
            let username = dict[kSecAttrAccount as String] as! String
            let password = dict[kSecSharedPassword as String] as! String
            dispatch_async(dispatch_get_main_queue()) {
                completion(error: nil, username: username, password: password)
            }

Remember to be careful when returning the values for username and password as it has to be on the main thread

cherbear
  • 253
  • 2
  • 3
  • 11