您好,我在更新存储在钥匙串中的登录信息方面遇到了 iOS 钥匙串的一个非常奇怪的问题。因此,如果没有保存的凭据,则正确运行保存函数会保存登录信息。如果登录信息已存在并且用户更新了密码,则更新功能仅正确更新密码。但是,如果登录信息存在并且我尝试更改电子邮件(同时保留或更改密码),则第一次更新将失败。我必须手动单击更新登录两次才能更新登录信息。我尝试了下面的代码,只是强制删除和保存函数运行两次,同时在之间添加延迟,但这不起作用。唯一有效的是按两次“更新”。我很感激任何帮助。谢谢。
delete(email: result.0)
save(email: email, password: password)
Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { _ in
delete(email: result.0)
save(email: email, password: password)
}
func save(email: String, password: String) {
let passwordData = password.data(using: .utf8)!
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "https://hustle.page",
kSecAttrAccount as String: email,
kSecValueData as String: passwordData
]
let saveStatus = SecItemAdd(query as CFDictionary, nil)
if saveStatus == errSecDuplicateItem {
update(email: email, password: password)
}
}
func update(email: String, password: String) {
if let result = read(service: "https://hustle.page"){
if result.0 == email {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "https://hustle.page",
kSecAttrAccount as String: email
]
let passwordData = password.data(using: .utf8)!
let updatedData: [String: Any] = [
kSecValueData as String: passwordData
]
SecItemUpdate(query as CFDictionary, updatedData as CFDictionary)
} else {
delete(email: result.0)
save(email: email, password: password)
Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { _ in
delete(email: result.0)
save(email: email, password: password)
}
}
}
}
func delete(email: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "https://hustle.page",
kSecAttrAccount as String: email
]
SecItemDelete(query as CFDictionary)
}
func read(service: String) -> (String, String)? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecReturnAttributes as String: true,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == errSecSuccess, let item = result as? [String: Any] {
if let account = item[kSecAttrAccount as String] as? String,
let passwordData = item[kSecValueData as String] as? Data,
let password = String(data: passwordData, encoding: .utf8) {
return (account, password)
}
}
return nil
}
in the view:
Button {
save(email: email, password: password)
} label: {
Text("Update")
}
iOS 钥匙串是一种安全的数据存储,其行为是同步的,因为它将在执行另一个请求之前执行并完成一个请求。因此,快速连续的请求可能并不总是按预期工作,特别是在不检查错误的情况下(我确实建议检查错误在我之前的答案中)。
如果您想更新现有的钥匙串条目,您需要删除旧条目,然后添加包含更新数据的新条目。
您还可以通过 Kamil Makowski 的“增强 iOS 上的用户数据安全:仔细查看钥匙串
SecItemAdd
、SecItemUpdate
和SecItemDelete
)。他们将为您提供有关操作是否成功或可能发生什么错误的信息。
您可以重写
save
函数以检查包含所提供电子邮件的条目是否已存在。
例如:
func save(email: String, password: String) {
// Check if there's an existing entry with the provided email
if let existing = read(service: "https://hustle.page"), existing.0 == email {
// Delete the old entry
let deleteStatus = delete(email: email)
if deleteStatus != errSecSuccess {
// Handle the delete error
print("Error deleting data: \(deleteStatus)")
return
}
}
// Now, save the new data
let passwordData = password.data(using: .utf8)!
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "https://hustle.page",
kSecAttrAccount as String: email,
kSecValueData as String: passwordData
]
let saveStatus = SecItemAdd(query as CFDictionary, nil)
if saveStatus != errSecSuccess {
// Handle the save error
print("Error saving data: \(saveStatus)")
}
}
func delete(email: String) -> OSStatus {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "https://hustle.page",
kSecAttrAccount as String: email
]
let status = SecItemDelete(query as CFDictionary)
return status
}
在你看来:
Button {
save(email: email, password: password)
} label: {
Text("Update")
}
这样,每次调用
save
函数时,它都会更新现有条目或添加新条目,确保钥匙串始终与提供的数据同步。