钥匙扣课程不适用于iPhone 4s / 5和iPad

时间:2014-10-18 10:29:43

标签: ios iphone ipad swift keychain

我将userId作为令牌keychain保存在我的iOS应用中的设备上。这适用于iPhone 5s / 6/6 Plus,但不适用于iPhone 4s / 5和iPad。在说明我的应用时,我会检查钥匙串中是否存在userIdfunc tokenExists()。但后来我收到了一个错误:

fatal error: unexpectedly found nil while unwrapping an Optional value 

如图所示:

error message

这是我的钥匙串类,用于保存,加载和删除令牌以及检查是否存在令牌:

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

我的问题有解决方法吗?

2 个答案:

答案 0 :(得分:1)

截至XCode 6.1中包含的更新,我现在必须将状态与 errSecItemNotFound 进行比较:

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

在此次更新之前,我还在进行if let op = opaque? {,它正确地捕获它nil,但后来我开始尝试解包nil值时出错。

希望这可以帮助其他人在同一问题上偶然发现这个答案。

答案 1 :(得分:0)

您的问题是您的tokenExists()方法未检查令牌是否存在。相反,它假设tokenExists(由!末尾的let token:NSString = loadToken()!指出),然后仅检查它是否为空字符串。

试试这个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
}