2014-10-14 20 views
5

Wir haben eine App in Swift erstellt, die Schlüsselbund verwendet. Die App funktioniert problemlos, wenn sie auf einem Gerät oder im Simulator ausgeführt wird. Sie kann jedoch nicht auf den Schlüsselbund zugreifen, wenn sie über Testflight bereitgestellt wird, sofern sie nicht auf einem neuen Gerät bereitgestellt wird, auf dem die App zuvor nicht über Xcode 6.1 installiert wurde.Swift Schlüsselbund und Bereitstellungsprofile

Es folgt ein Auszug aus dem Schlüsselanhänger-Code:

import UIKit 
    import Security 

    let serviceIdentifier = "com.ourdomain" 

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

class KeychainManager { 

    class func setString(value: NSString, forKey: String) { 
     self.save(serviceIdentifier, key: forKey, data: value) 
    } 

    class func stringForKey(key: String) -> NSString? { 
     var token = self.load(serviceIdentifier, key: key) 

     return token 
    } 

    class func removeItemForKey(key: String) { 
     self.save(serviceIdentifier, key: key, data: "") 
    } 



    class func save(service: NSString, key: String, data: NSString) { 
     var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! 
     // Instantiate a new default keychain query 
     var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, key, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue]) 

     // Delete any existing items 
     SecItemDelete(keychainQuery as CFDictionaryRef) 

     if data == "" { return } 

     // Add the new keychain item 
     var status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil) 
    } 

    class func load(service: NSString, key: String) -> NSString? { 
     // Instantiate a new default keychain query 
     // Tell the query to return a result 
     // Limit our results to one item 
     var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, key, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue]) 

     var dataTypeRef :Unmanaged<AnyObject>? 

     // Search for the keychain items 
     let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef) 

     let opaque = dataTypeRef?.toOpaque() 

     var contentsOfKeychain: NSString? 

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

      // Convert the data retrieved from the keychain into a string 
      contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding) 
     } else { 
      return nil 
     } 

     return contentsOfKeychain 
    } 
    } 

Nachdem die App bereits auf dem Gerät über Xcode installiert wurde 6.1 Ich habe bemerkt, dass die "serviceIdentifier" - "com.ourdomain" war falsch und stimmt nicht mit der Paket-ID der App überein, wie dies bei der Bereitstellung erforderlich ist.

Ich änderte dann den „serviceIdentifier“ Wert die Paket-ID übereinstimmen - „com.ourdomain.appname“ aber die App wird einfach nicht an dem Gerät arbeiten, wenn sie über Testflug bereitgestellt. Ich bin sicher, das ist, weil das Gerät bereits den Schlüsselbund für die Bundle-ID mit der falschen Kennung installiert hat, aber ich kann nicht ergründen, wie dies umgehen, um entweder den Schlüsselbund zu entfernen, wenn die App entfernt wird oder das Bereitstellungsprofil zu verwenden der vorhandene Schlüsselbund (mit der falschen Kennung)

Jede Hilfe wäre sehr willkommen. Vielen Dank im Voraus

Antwort

5

Verwenden withUnsafeMutablePointer Funktion und UnsafeMutablePointer struct

var result: AnyObject? 
var status = withUnsafeMutablePointer(&result) { SecItemCopyMatching(keychainQuery, UnsafeMutablePointer($0)) } 

if status == errSecSuccess { 
    if let data = result as NSData? { 
     if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { 
      // ... 
     } 
    } 
} 

es funktioniert gut mit Release (Schnellste Optimierung) Build: statt, wie unten die Daten abruft.

+2

Dies ist eine tatsächliche Lösung für das Problem! Die akzeptierte Antwort der Deaktivierung der Compiler-Optimierung ist in vielen Fällen inakzeptabel. Für was es wert ist, sehe ich nur den ursprünglichen Fehler auf 64-Bit-Geräten. – alexkent

+1

Das funktionierte für mich nach vielen Stunden, in denen ich meinen Kopf gegen eine Wand schlug. Vielen Dank. – Nick

+0

Funktioniert nicht, iPhone 6, 6+ und iPad Air 2 zeigen immer noch das Problem bei der Verwendung von 'withUnsafeMutablePointer'. "-Onone" ist immer noch die einzig praktikable Option für alle Geräte, bis Swift 1.2 stabil ist. –

2

Ok, alles im Provisioning-Profil und Keychain-Code war in Ordnung. Das Problem war eine Einstellung im Swift-Compiler! Das Ändern des Optimierungslevels für "Release" von "Fastest" zu "None" schien das Problem zu beheben.

enter image description here

+2

Ich habe genau das gleiche Problem. Es sieht aus, als ob dataTypeRef? .toOpaque() null zurückgibt, wenn Sie die Optimierungsstufe auf -O setzen. Ich glaube jedoch nicht, dass das Setzen der Optimierungsstufe auf -Onone keine Lösung ist, da es den Code schneller macht. Ich habe Xcode 6.1 (6A1052d) – JorgeDeCorte

+0

Ich habe das gleiche Problem und habe diesen Fehler an Apple gemeldet (Bug 19003552, wenn das hilfreich ist). Jeder, der sich damit beschäftigt, sollte dasselbe tun wie Duplikate, um Probleme zu priorisieren. – kolizz

+0

@JorgeDeCorte - Danke. Vielleicht müssen wir versuchen, das dataTypeRef explizit auszupacken ... haben Sie das versucht?- Ich werde sehen, ob das vielleicht das Problem löst – Mark

0

Die Änderung des schnellen Optimierer funktioniert, aber kein guter Weg, um das Problem zu lösen. Wie in den Kommentaren erwähnt, scheint dies ein Fehler im schnellen Compiler mit der Optimierung zu sein.

Dies ist nicht nur iOS, das gleiche passiert auch auf OSX (vielleicht ein Tag zu der Frage hinzufügen). Der OSStatus ist 0 (Erfolg), aber der referenzierende Zeiger ist Null, sobald die Optimierung abgeschlossen ist.

Die beste Abhilfe zu implementieren Kommunikation in Objective-c zu Schlüsselbund oder einen Wrapper wie EMKeychain mit (OSX nur, sorry weiß nicht, einen iOS-Wrapper im Moment)

Verwandte Themen