web-dev-qa-db-fra.com

Obtenir une option ("") lorsque vous essayez d'obtenir de la valeur de KeyChain

Lorsque j'essaie d'obtenir ma valeur keyChain, elle renvoie une chaîne contenant: 

Optional("[thing in the KeyChain]")

alors, j'ai essayé de supprimer "facultatif" en utilisant une boucle:

var str = KeychainService.loadToken()

for(var i = 0; i < 9 ; i++)
{
    str[i] = ""
}

Mais je reçois une erreur: NSString n'a pas de membre nommé 'indice'

La classe KeychainService:

import Foundation
import Security

let serviceIdentifier = "MySerivice"
let userAccount = "authenticatedUser"
let accessGroup = "MySerivice"

// Arguments for the keychain queries
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

class KeychainService: NSObject {

/**
* Exposed methods to perform queries.
* Note: feel free to play around with the arguments
* for these if you want to be able to customise the
* service identifier, user accounts, access groups, etc.
*/
internal class func saveToken(token: NSString) {
    self.save(serviceIdentifier, data: token)
}

internal class func loadToken() -> NSString? {
    var token = self.load(serviceIdentifier)

    return token
}

/**
* Internal methods for querying the keychain.
*/
private class func save(service: NSString, data: NSString) {
    var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)

    // Instantiate a new default keychain query
    var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue])

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

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

private class func load(service: NSString) -> String? {
    // 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, userAccount, 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: String?

    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 {
        println("Nothing was retrieved from the keychain. Status code \(status)")
    }

    return contentsOfKeychain
    }
}

Je ne veux tout simplement pas supprimer l'option en option autour de la chaîne .__ Ou y a-t-il une meilleure façon de le faire?

J'ai pris ce code de:

http://matthewpalmer.net/blog/2014/06/21/example-ios-keychain-Swift-save-query/

14
Clément Bisaillon

Vous obtenez la Optional("") car la valeur facultative n'est pas décompressée. Vous devez mettre un ! après l'objet et vous n'obtiendrez plus le bit Optional(""). Je voudrais vous montrer le code mais vous ne nous avez pas montré la déclaration print(). J'ai créé quelques exemples ci-dessous qui, je pense, reproduiraient le problème, bien que je ne les aie pas essayés.

var value:String?
value = "Hello, World"

print("The Value Is \(value)") // Prints "The Value Is Optional(Hello, World)"
print("The Value Is \(value!)")// Prints "The Value Is Hello, World"

J'espère que cela répond à votre question ou du moins vous oriente dans la bonne direction. Demandez juste si vous avez besoin de plus d'informations ou d'un meilleur exemple.

27
Jack Chorley

Voici un exemple d'implémentation de Swift 2:

import Security

class ZLKeychainService: NSObject {

    var service = "Service"
    var keychainQuery :[NSString: AnyObject]! = nil

    func save(name name: NSString, value: NSString) -> OSStatus? {
        let statusAdd :OSStatus?

        guard let dataFromString: NSData = value.dataUsingEncoding(NSUTF8StringEncoding) else {
            return nil
        }

        keychainQuery = [
            kSecClass       : kSecClassGenericPassword,
            kSecAttrService : service,
            kSecAttrAccount : name,
            kSecValueData   : dataFromString]
        if keychainQuery == nil {
            return nil
        }

        SecItemDelete(keychainQuery as CFDictionaryRef)

        statusAdd = SecItemAdd(keychainQuery! as CFDictionaryRef, nil)

        return statusAdd;
    }

    func load(name name: NSString) -> String? {
        var contentsOfKeychain :String?

        keychainQuery = [
            kSecClass       : kSecClassGenericPassword,
            kSecAttrService : service,
            kSecAttrAccount : name,
            kSecReturnData  : kCFBooleanTrue,
            kSecMatchLimit  : kSecMatchLimitOne]
        if keychainQuery == nil {
            return nil
        }

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

        if (status == errSecSuccess) {
            let retrievedData: NSData? = dataTypeRef as? NSData
            if let result = NSString(data: retrievedData!, encoding: NSUTF8StringEncoding) {
                contentsOfKeychain = result as String
            }
        }
        else {
            print("Nothing was retrieved from the keychain. Status code \(status)")
        }

        return contentsOfKeychain
    }
}

//Test:
let userName = "TestUser"
let userValue: NSString = "TestValue"
print("userName: '\(userName)'")
print("userValue: '\(userValue)'")

let kcs = ZLKeychainService()

kcs.save(name:userName, value: userValue)
print("Keychain Query \(kcs.keychainQuery)")

if let recoveredToken = kcs.load(name:userName) {
    print("Recovered Value: '\(recoveredToken)'")
}

Sortie:

userName: 'TestUser'
userValue: 'TestValue'
Requête de trousseau [acct: TestUser, v_Data: <54657374 56616c75 65>, svce: Service, classe: genp]
Valeur récupérée: 'TestValue' 

2
zaph

Vous pouvez utiliser l'encapsuleur Swift sur l'API Keychain C et éviter les problèmes ci-dessus. https://github.com/deniskr/KeychainSwiftAPI

1
Denis Krivitski

Vous obtiendrez facultatif ("") car la valeur facultative n'est pas décompressée et si vous souhaitez décompresser la valeur facultative pour obtenir la valeur de chaîne, 

yourValue.unsafelyUnwrapped

0
Ranjith