2014-06-10 3 views
17

Sono bloccato convertendo il risultato della query Portachiavi utilizzando Swift.Interrogare il portachiavi iOS utilizzando Swift

La mia richiesta sembra funzionare:

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^^^^ 
}) 

Il mio problema è, il codice non viene compilato:

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! 

non so come convertire Unmanaged<AnyObject>-NSData.

Qualche idea?

+0

Sto anche cercando di ottenere l'accesso al portachiavi iOS e ho visto il tuo post. Non riesco a capire come ottenere creato il dizionario di query. Ho persino copiato la tua prima riga nella mia applicazione e dice la stessa cosa. "Impossibile trovare un sovraccarico per" init "che accetta gli argomenti forniti". Ho perso qualcosa? – Rob

+0

Stesso problema con l'ultimo XCode del 3/11/2015 – a432511

+0

Se stai cercando un semplice wrapper portachiavi drop-in, puoi provare questo: http://github.com/ashleymills/Keychain.swift –

risposta

10

Sembra che tu abbia colpito un bug del compilatore, che dovresti segnalare. Si può prendere una strada diversa per recuperare il valore, come ad esempio il seguente:

var dataTypeRef :Unmanaged<AnyObject>? 
    let status = SecItemCopyMatching(queryAttributes, &dataTypeRef); 

    let opaque = dataTypeRef?.toOpaque() 

    if let op = opaque? { 
     let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue() 

    } 

L'errore si manifesta quando si utilizza AnyObject come un parametro di tipo T di Unmanaged<T>. Il seguente frammento di codice compilato senza problema, che utilizza un tipo più specifico, CFError:

let url = NSURL(string:"dummy") 
    var errorRef: Unmanaged<CFError>? 
    let succeeded = CTFontManagerRegisterFontsForURL(url, .Process, &errorRef) 

    if errorRef { 
     let error = errorRef!.takeRetainedValue() 
    } 

Poiché l'API portachiavi restituisce un risultato diverso a seconda della query attributi, è richiesto l'uso di AnyObject.

+0

Grazie mille per la risposta. Lo segnalerò allora e forse aspetterò di vedere se risolto nella prossima beta. In caso contrario, userò questo codice. Grazie ancora! – Damien

+7

Sembra che questo funzioni quando si imposta lo Swift Compiler - Optimization Level su -Onone. Ma quando lo si imposta su -O. Opaco sarà nulla. Build con Xcode 6.1 (6A1052d) – JorgeDeCorte

+1

Ricevo un'eccezione di accesso errata sulla riga 'let opaque = dataTypeRef? .toOpaque()' (Xcode 6.1 e 6.2b) –

0

Per far funzionare tutto questo è necessario prima accedere al valore mantenuto per ogni costante di portachiavi. Ad esempio:

let kSecClassValue = kSecClass.takeRetainedValue() as NSString 
let kSecAttrAccountValue = kSecAttrAccount.takeRetainedValue() as NSString 
let kSecValueDataValue = kSecValueData.takeRetainedValue() as NSString 
let kSecClassGenericPasswordValue = kSecClassGenericPassword.takeRetainedValue() as NSString 
let kSecAttrServiceValue = kSecAttrService.takeRetainedValue() as NSString 
let kSecMatchLimitValue = kSecMatchLimit.takeRetainedValue() as NSString 
let kSecReturnDataValue = kSecReturnData.takeRetainedValue() as NSString 
let kSecMatchLimitOneValue = kSecMatchLimitOne.takeRetainedValue() as NSString 

Avrai quindi bisogno di fare riferimento alla costante che hai creato nell'oggetto dizionario portachiavi in ​​questo modo.

var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) 

ho scritto un post su di esso a: http://rshelby.com/2014/08/using-swift-to-save-and-query-ios-keychain-in-xcode-beta-4/

Spero che questo aiuti!

rshelby

18

Usa withUnsafeMutablePointer funzione e UnsafeMutablePointer struttura per il recupero dei dati, come ad esempio il seguente:

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) { 
      // ... 
     } 
    } 
} 

funziona bene con il rilascio (più veloce [-O]) build.

+1

Questa è stata l'unica soluzione che ha funzionato per me durante il rilascio dell'applicazione (altre soluzioni hanno funzionato bene in xcode, ma non durante la distribuzione dell'app in una versione beta). – Barthelemy

+1

Questa dovrebbe essere la risposta corretta. Gli altri non funzioneranno in una build di rilascio. – brettsam

+1

Sei una stella, kishikawa. Sfortunatamente non posso usare il tuo wrapper in quanto richiede 8.0, ma questo mi differenzia. –