我正在尝试使用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);
尝试在Swift中执行如下操作:
var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
var query: NSDictionary = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "MyService",
kSecAttrAccount: "Some account",
kSecValueData: secret
]
产生错误“无法将表达式的类型'Dictionary'转换为'DictionaryLiteralConvertible'。
我采用的另一种方法是在Dictionary上使用Swift和- setObject:forKey:
方法,使用密钥kSecClass添加kSecClassGenericPassword。
在Objective-C中:
NSMutableDictionary *searchDictionary = [NSMutableDictionary dictionary];
[searchDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
在Objective-C代码中,使用id桥接各种keychain项类键的CFTypeRef。在Swift documentation中,它提到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)
要使用Keychain Item Attribute键作为字典键,您必须使用takeRetainedValue或takeUnretainedValue(视情况而定)将其解包。然后你可以将它们转换为NSCopying。这是因为它们在标题中是CFTypeRefs,它们都不是可复制的。
然而,从Xcode 6 Beta 2开始,这会导致Xcode崩溃。
答案 0 :(得分:13)
你只需要向下翻译文字:
let dict = ["hi": "Pasan"] as NSDictionary
现在dict
是一个NSDictionary。为了制作一个可变的,它与Objective-C非常相似:
let mDict = dict.mutableCopy() as NSMutableDictionary
mDict["hola"] = "Ben"
答案 1 :(得分: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)
答案 2 :(得分:6)
也许事情有所改善。在Xcode 7 beta 4上,除了处理结果hidden_field
之外,似乎不需要强制转换。具体来说,以下似乎有效:
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)
}
需要指出的一些事项:您的初始问题可能是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...
返回Optional。添加'!'或者更好的是,使用str.dataUsingEncoding
来处理nil返回,可能会使你的代码工作。打印错误代码并在文档中查找它将有助于隔离问题(我得到错误-50 =错误的参数,直到我发现我的kSecClass有问题,与数据类型或强制转换无关!)
答案 3 :(得分:4)
这似乎工作得很好,或者至少编译器没有小猫 - 没有那个 似乎在Beta 2中正常工作 var secret: NSData = "Top Secret".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
var array1 = NSArray(objects:"\(kSecClassGenericPassword)", "MyService", "Some account", secret)
var array2 = NSArray(objects:"\(kSecClass)","\(kSecAttrService)", "\(kSecAttrAccount)","\(kSecValueData)")
let query = NSDictionary(objects: array1, forKeys: array2)
println(query)
let status = SecItemAdd(query as CFDictionaryRef, nil)
答案 4 :(得分:1)
为了使其工作,您需要访问钥匙串常量的保留值。例如:
let kSecClassValue = kSecClass.takeRetainedValue() as NSString
let kSecAttrAccountValue = kSecAttrAccount.takeRetainedValue() as NSString
let kSecValueDataValue = kSecValueData.takeRetainedValue() as NSString
let kSecClassGenericPasswordValue = kSecClassGenericPassword.takeRetainedValue() as NSString
let kSecAttrServiceValue = kSecAttrService.takeRetainedValue() as NSString
let kSecMatchLimitValue = kSecMatchLimit.takeRetainedValue() as NSString
let kSecReturnDataValue = kSecReturnData.takeRetainedValue() as NSString
let kSecMatchLimitOneValue = kSecMatchLimitOne.takeRetainedValue() as NSString
然后,您可以像这样引用MSMutableDictionary中的值:
var keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
我写了一篇关于它的博客文章: http://rshelby.com/2014/08/using-swift-to-save-and-query-ios-keychain-in-xcode-beta-4/
希望这有帮助!
rshelby
答案 5 :(得分:1)
Swift 3
let kSecClassKey = String(kSecClass)
let kSecAttrAccountKey = String(kSecAttrAccount)
let kSecValueDataKey = String(kSecValueData)
let kSecClassGenericPasswordKey = String(kSecClassGenericPassword)
let kSecAttrServiceKey = String(kSecAttrService)
let kSecMatchLimitKey = String(kSecMatchLimit)
let kSecReturnDataKey = String(kSecReturnData)
let kSecMatchLimitOneKey = String(kSecMatchLimitOne)
你也可以在字典本身内完成它:
var query: [String: Any] = [
String(kSecClass): kSecClassGenericPassword,
String(kSecAttrService): "MyService",
String(kSecAttrAccount): "Some account",
String(kSecValueData): secret
]
然而,这对于编译器来说更昂贵,更是如此,因为您可能在多个地方使用查询。
答案 6 :(得分:0)
+ (NSArray *)allAccounts;
+ (NSArray *)accountsForService:(NSString *)serviceName;
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;