我将userId
作为令牌keychain
保存在我的iOS应用中的设备上。这适用于iPhone 5s / 6/6 Plus,但不适用于iPhone 4s / 5和iPad。在说明我的应用时,我会检查钥匙串中是否存在userId
或func tokenExists()
。但后来我收到了一个错误:
fatal error: unexpectedly found nil while unwrapping an Optional value
如图所示:
这是我的钥匙串类,用于保存,加载和删除令牌以及检查是否存在令牌:
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
}
}
我的问题有解决方法吗?
答案 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
}