web-dev-qa-db-ja.com

KeyChainから値を取得するときにOptional( "")を取得する

KeyChain値を取得しようとすると、次を含む文字列が返されます。

Optional("[thing in the KeyChain]")

だから、私はループを使用して「オプション」を削除しようとしました:

var str = KeychainService.loadToken()

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

しかし、エラーが発生します:NSStringには 'subscript'という名前のメンバーがありません

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

Strの周りのオプションのものを削除したくないですか、それともそれを行うためのより良い方法はありますか?

私はこのコードを以下から取得しました:

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

14

オプションの値がアンラップされていないため、Optional("")を取得します。オブジェクトの後に_!_を配置する必要があり、Optional("")ビットを取得できなくなります。コードを紹介しますが、print()ステートメントはまだ示していません。試したことはありませんが、問題を再現できると思われるサンプルを以下にいくつか作成しました。

_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"
_

これがあなたの質問に答えるか、少なくともあなたを正しい方向に向けることを願っています、あなたがより多くの情報またはより良い例が必要かどうか尋ねてください。

27
Jack Chorley

これが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)'")
}

出力:

userName: 'TestUser'
userValue: 'TestValue'
キーチェーンクエリ[acct:TestUser、v_Data:<54657374 56616c75 65>、svce:Service、class:genp]
回復された値: 'TestValue'

2
zaph

キーチェーンCAPIのSwiftラッパーを使用して、上記の問題を完全に回避できます。 https://github.com/deniskr/KeychainSwiftAPI

1
Denis Krivitski

オプションの値はラップされていないため、Optional( "")を取得します。オプションの値をラップ解除して文字列値を取得するには、次のようにします。

yourValue.unsafelyUnwrapped

0
Ranjith