使用Swift将项添加到钥匙串

Pas*_*tne 13 keychain ios swift

我正在尝试使用Swift将项目添加到iOS钥匙串,但无法弄清楚如何正确输入强制转换.从WWDC 2013会议709开始,给出以下Objective-C代码:

NSData *secret = [@"top secret" dataWithEncoding:NSUTF8StringEncoding];
NSDictionary *query = @{
    (id)kSecClass: (id)kSecClassGenericPassword,
    (id)kSecAttrService: @"myservice",
    (id)kSecAttrAccount: @"account name here",
    (id)kSecValueData: secret,
};

OSStatus = SecItemAdd((CFDictionaryRef)query, NULL);
Run Code Online (Sandbox Code Playgroud)

尝试在Swift中执行如下操作:

var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
var query: NSDictionary = [
    kSecClass: kSecClassGenericPassword,
    kSecAttrService: "MyService",
    kSecAttrAccount: "Some account",
    kSecValueData: secret
]
Run Code Online (Sandbox Code Playgroud)

产生错误"无法将表达式的类型'Dictionary'转换为'DictionaryLiteralConvertible'.

我采用的另一种方法是使用Swift和- setObject:forKey:Dictionary上的方法来添加kSecClassGenericPassword和密钥kSecClass.

在Objective-C中:

NSMutableDictionary *searchDictionary = [NSMutableDictionary dictionary];
[searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
Run Code Online (Sandbox Code Playgroud)

在Objective-C代码中,使用id桥接各种keychain项类键的CFTypeRef.在Swift 文档中,提到Swift将id作为AnyObject导入.但是,当我试图将kSecClass转发为方法的AnyObject时,我得到的错误是"Type'AnyObject'不符合NSCopying.

任何帮助,无论是直接答案还是有关如何与Core Foundation类型交互的指导,都将受到赞赏.

编辑2

此解决方案从Xcode 6 Beta 2开始不再有效.如果您使用Beta 1,则以下代码可能有效.

var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let query = NSDictionary(objects: [kSecClassGenericPassword, "MyService", "Some account", secret], forKeys: [kSecClass,kSecAttrService, kSecAttrAccount, kSecValueData])

OSStatus status = SecItemAdd(query as CFDictionaryRef, NULL)
Run Code Online (Sandbox Code Playgroud)

要将Keychain Item Attribute键用作字典键,您必须使用takeRetainedValue或takeUnretainedValue(根据需要)将它们打开.然后你可以将它们转换为NSCopying.这是因为它们在头文件中是CFTypeRefs,它们都不是可复制的.

但是,从Xcode 6 Beta 2开始,这会导致Xcode崩溃.

Sam*_*fes 13

你只需要向下翻译文字:

let dict = ["hi": "Pasan"] as NSDictionary
Run Code Online (Sandbox Code Playgroud)

现在dict是一个NSDictionary.要制作一个可变的,它与Objective-C非常相似:

let mDict = dict.mutableCopy() as NSMutableDictionary
mDict["hola"] = "Ben"
Run Code Online (Sandbox Code Playgroud)


Bes*_*rov 7

在xcode 6.0.1中你必须这样做!!

let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)
Run Code Online (Sandbox Code Playgroud)

  • 你能解释一下你的答案吗? (2认同)

ɲeu*_*urɳ 6

也许事情有所改善.在Xcode 7 beta 4上,除了处理结果外,似乎没有必要进行转换AnyObject?.具体来说,以下似乎有效:

var query : [NSString : AnyObject] = [
    kSecClass : kSecClassGenericPassword,
    kSecAttrService : "MyAwesomeService",
    kSecReturnAttributes : true,   // return dictionary in result parameter
    kSecReturnData : true          // include the password value
]
var result : AnyObject?
let err = SecItemCopyMatching(query, &result)
if (err == errSecSuccess) {
    // on success cast the result to a dictionary and extract the
    // username and password from the dictionary.
    if let result = result as ? [NSString : AnyObject],
       let username = result[kSecAttrAccount] as? String,
       let passdata = result[kSecValueData] as? NSData,
       let password = NSString(data:passdata, encoding:NSUTF8StringEncoding) as? String {
        return (username, password)
    }
} else if (status == errSecItemNotFound) {
    return nil;
} else {
    // probably a program error,
    // print and lookup err code (e.g., -50 = bad parameter)
}
Run Code Online (Sandbox Code Playgroud)

如果缺少密钥,请添加密钥:

var query : [NSString : AnyObject] = [
    kSecClass : kSecClassGenericPassword,
    kSecAttrService : "MyAwesomeService",
    kSecAttrLabel : "MyAwesomeService Password",
    kSecAttrAccount : username,
    kSecValueData : password.dataUsingEncoding(NSUTF8StringEncoding)!
]
let result = SecItemAdd(query, nil)
// check that result is errSecSuccess, etc...
Run Code Online (Sandbox Code Playgroud)

需要指出的一些事项:您的初始问题可能是str.dataUsingEncoding返回Optional.添加'!' 或者更好的是,使用一个if let来处理nil返回,可能会使你的代码工作.打印出错误代码并在文档中查找它将有助于隔离问题(我得到错误-50 =错误的参数,直到我发现我的kSecClass有问题,与数据类型或强制转换无关!) .