Getting Optional(“”) when trying to get value from KeyChain

一世执手 提交于 2019-11-29 01:32:49
Jack Chorley

You get the Optional("") because the optional value is not unwrapped. You need to put a ! after the object and you won't get the Optional("") bit any more. I would show you the code but you haven't shown us the print() statement. I made some sample ones below that I think would replicate the problem, though I haven't tried them.

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"

Im hoping this answers your question or at least points you in the right direction, just ask if you need more information or a better example.

Here is a Swift 2 example implementation:

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

Output:

userName: 'TestUser'
userValue: 'TestValue'
Keychain Query [acct: TestUser, v_Data: <54657374 56616c75 65>, svce: Service, class: genp]
Recovered Value: 'TestValue'

You can use the Swift wrapper over the Keychain C API, and avoid the above problems altogether. https://github.com/deniskr/KeychainSwiftAPI

You will get the Optional("") because the optional value is not unwrapped and if you want to unwrap the optional value to get the string value, do

yourValue.unsafelyUnwrapped

You actually don't even need to do anything. The "Optional" string isn't in the actual data. That is just something Swift seems to place on the output on the console when it is an optional value that isn't unwrapped. IE The data itself doesn't contain the string Optional.

Still, good to unwrap it if you know it contains data.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!