你好,我遇到了一个非常奇怪的问题,iOS密钥链在更新存储在密钥链中的登录信息.因此,如果没有保存的凭据,则运行保存功能会正确地保存日志(log)信息.如果登录信息已经存在,并且用户更新了他们的密码,则更新功能将正确地仅更新密码.但如果登录信息存在,并且我try 更改邮箱(同时保留或更改密码),则第一次更新不会成功.我必须手动点击两次更新登录信息才能更新登录信息.我try 了下面的代码,只是强制DELETE和SAVE函数运行两次,同时在两次之间添加一个延迟,但这不起作用.唯一有效的方法是按两次"更新"键.我很感激你们的帮助.谢谢.

 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密钥链是一个安全的数据存储,它的行为是同步的,它将在执行一个请求之前执行和完成另一个请求.因此,快速的连续请求可能并不总是像预期的那样工作,特别是在没有判断错误的情况下(我确实建议判断错误in my previous answer).

If you want to update an existing Keychain entry, you will need to delete the old entry and then add a new entry with the updated data.
You can also see an illustration of common operations with "Enhancing User Data Security on iOS: A Closer Look at Keychain" from Kamil Makowski

如上所述,始终判断密钥链函数(SecItemAddSecItemUpdateSecItemDelete)的返回值.它们将为您提供有关操作是否成功或可能发生了什么错误的信息.

您可以重写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函数时,它要么更新现有条目,要么添加新条目,从而确保密钥链始终与提供的数据同步.

Ios相关问答推荐

StackView UIKit间距似乎非常小的编程式编程

为什么Reaction-Native-Vision-Camera不能与闪光灯一起使用?

AVFoundation Camera推出变焦SWIFT

由于已存在同名项目,因此无法将Mapbox.xcframework-ios.sign复制到Signature中

在将Cocoapods更新到1.13.0之后,它抛出错误

许多文本字段的性能问题

如何在@AppStorage中存储字体?

OBJC @selector 在另一个实现中不起作用

在左侧显示多行值时会出现 SwiftUI 错误,这会 destruct 堆栈中右侧的对齐方式

我无法使用 Swift 将文件上传到 firebase

转换在 NavigationView 中不起作用

UIButton在iOS7中没有显示突出显示

当键盘出现在 iOS 中时,将 UIView 上移

如何将 iPhone OSStatus 代码转换为有用的东西?

如何在react 导航中获取当前路由名称?

命令 /Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang 失败,退出代码为 1

Select 器touchesBegan:withEvent:的覆盖方法具有不兼容的类型(NSSet,UIEvent)->()

检测应用程序何时进入我的视图背景的最佳方法是什么?

为给定的 UIColor 获取更亮和更暗的 colored颜色 变化

在 SKScene 中设置按钮