简体   繁体   English

钥匙串类不适用于iPhone 4s / 5和iPad

[英]Keychain class doesn't work for iPhone 4s/5 and iPad

I save a userId as token with keychain on the device in my iOS app. 我在iOS应用程序的设备上使用keychainuserId保存为令牌。 This works fine for iPhone 5s/6/6 Plus, but not for iPhone 4s/5 and iPad. 此功能适用于iPhone 5s / 6/6 Plus,但不适用于iPhone 4s / 5和iPad。 On stating my app I check if a userId exists in keychain or not with func tokenExists() . 在陈述我的应用程序时,我会使用func tokenExists()检查钥匙串中是否存在userId But then I get an error: 但是然后我得到一个错误:

fatal error: unexpectedly found nil while unwrapping an Optional value 

as seen here: 如此处所示:

错误信息

And here is my keychain class to save, load and delete a token and to check if a token exists: 这是我的钥匙串类,用于保存,加载和删除令牌并检查令牌是否存在:

import UIKit
import Security

// Identifiers
let userAccount:String = "userAccount"

class KeychainService: NSObject {

/*
save token to keychain
*/
func deleteToken() {
    self.delete("userId")
}

/*
save token to keychain
*/
func saveToken(token: NSString) {
    self.save("userId", data: token)
}

/*
load keychain token
*/
func loadToken() -> NSString? {
    var token: AnyObject? = self.load("userId")

    return token as? NSString
}

func tokenExists() -> Bool {

    let token:NSString = loadToken()!

    if (token == "") {
        return false
    }
    else {
        return true
    }
}

private func delete(service: NSString) {
    //var dataFromString: NSData = data.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
    // Instantiate a new default keychain query
    var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount])

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

    // Check that it worked ok
    //println("Token deleted")
}

private 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: [kSecClassGenericPassword, service, userAccount, dataFromString], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecValueData])

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

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

    // Check that it worked ok
    //println("Saving status code is: \(status)")
}

private func load(service: NSString) -> AnyObject? {
    // Instantiate a new default keychain query
    // Tell the query to return a result
    // Limit our results to one item
    var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPassword, service, userAccount, kCFBooleanTrue, kSecMatchLimitOne], forKeys: [kSecClass, kSecAttrService, kSecAttrAccount, kSecReturnData, kSecMatchLimit])

    // I'm not too sure what's happening here...
    var dataTypeRef :Unmanaged<AnyObject>?

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

    //println("Loading status code is: \(status)")

    // I'm not too sure what's happening here...
    let opaque = dataTypeRef?.toOpaque()
    var contentsOfKeychain: NSString?

    if let op = opaque? {
        let retrievedData = Unmanaged<NSData>.fromOpaque(op).takeUnretainedValue()
        //println("Retrieved the following data from the keychain: \(retrievedData)")
        var str = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
        contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
        //println("The decoded string is \(str)")
    } else {
        //println("Nothing was retrieved from the keychain.")
    }

    return contentsOfKeychain
}
}

Are there any solutions for my problem? 我的问题有解决方案吗?

As of updates included in XCode 6.1, I now have to compare the status to errSecItemNotFound : 从XCode 6.1中包含的更新开始,我现在必须将状态与errSecItemNotFound进行比较:

var dataTypeRef: Unmanaged<AnyObject>?
let status: OSStatus = SecItemCopyMatching(keychainQuery as CFDictionaryRef, &dataTypeRef)
if (status != errSecItemNotFound) {
    if let dataType = dataTypeRef? {
        ...
    }
}

Up until this update, I was also doing if let op = opaque? { 在此更新之前, if let op = opaque? { if let op = opaque? { , and it was properly catching it was nil , but then I started getting errors for attempting to unwrap a nil value. if let op = opaque? { ,并且正确捕获它为nil ,但随后由于尝试解开nil值而开始出现错误。

Hope this helps anyone else that stumbles upon this answer with the same issue. 希望这对遇到相同问题的其他人有所帮助。

Your problem is that your tokenExists() method doesn't check to see if the token exists. 您的问题是您的tokenExists()方法不检查令牌是否存在。 Instead it assumes the tokenExists (as noted by the ! at the end of let token:NSString = loadToken()! ) and then checks only if it is an empty string. 相反,它假定tokenExists(由注意到!在年底let token:NSString = loadToken()! ),然后只检查,如果它是一个空字符串。

Try this tokenExists() method: 试试这个tokenExists()方法:

func tokenExists() -> Bool {
    var result = false
    if let token = loadToken() {
        // the token exists, but is it blank?
        if token != "" {
            // no, the token is not blank
            result = true
        }
    }
    return result
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM