KeychainWrapper 保存数据并在同一功能中检索它不起作用

KeychainWrapper saving data and retrieving it in the same function isn't working

所以我有一个自定义函数,它向我的网络服务器发出 HTTP POST 请求以从我的数据库中获取一些字段,然后 returns 它到我的 iOS 将保存的应用程序该字段值作为钥匙串项,使用 KeychainWrapper。

我的 iOS 应用程序就像登录屏幕一样简单,它再次使用 HTTP POST 检查 mySQL 数据库中的用户凭据是否正确,如果正确,则将我带到一个用户应该显示使用 KeychainWrapper 抓取和显示的一些信息的页面。

现在我面临的问题是,每当我在登录页面上按下登录按钮并登陆用户页面时,所有字段都显示为空。但是如果我停止构建并重新构建,它将加载回那个页面,其中包含这次加载的字段。 就好像当我第一次进入用户页面时,这些字段还没有保存在钥匙串中,但是当我重建它时,它们是从那里加载的。

我将从我的代码中删除一些部分以使其简短并使其与这个问题相关。这是我的函数,它从 MYSQL 数据库请求元字段:

func requestMetaField(metaField:String) -> String {

        let userEmail: String = KeychainWrapper.standard.string(forKey: "email")!;
        let userPassword: String = KeychainWrapper.standard.string(forKey: "password")!;
        let meta = metaField;

        // Send data to server side
        // Classic HTTP POST Request using JSONSerialization here.

                    if(resultValue=="Success") {

                        // Save downloaded data to keychain
                        self.saveDataToKeychain(value: metaValue, key: meta);
                    }

        // End of the HTTP POST Request and else statements.

        if let metaValue: String = KeychainWrapper.standard.string(forKey: meta) {
        return metaValue;
        } else {
            return "N/A";
        }

    }

这是我的 saveDataToKeychain 函数:

func saveDataToKeychain(value:String, key:String) -> Bool {
        let saveSuccessful: Bool = KeychainWrapper.standard.set(value, forKey: key);
        return saveSuccessful;
    }

这里是我使用用户页面上的函数将其应用于标签的地方:

override func viewDidLoad() {
        super.viewDidLoad()

        logoutButton.layer.cornerRadius = 6;

        let firstName = requestMetaField(metaField: "first_name");
        let lastName = requestMetaField(metaField: "last_name");
        userFullName.text = "\(firstName) \(lastName)";

        let userMinuteBalance = requestMetaField(metaField: "mycred_default_total");
        userBalance.text = "\(userMinuteBalance) min";
    }

这里是我在使用登录按钮时将电子邮件和密码保存在钥匙串中的位置:

if(resultValue=="Success") {

                        // Login is successful
                        UserDefaults.standard.set(true, forKey: "isUserLoggedIn");
                        UserDefaults.standard.synchronize();

                        // Save email & password in keychain
                        self.saveDataToKeychain(value: userEmail!, key: "email");
                        self.saveDataToKeychain(value: userPassword!, key: "password");

                        isUserLoggedIn = true;
}

有谁知道我如何在登录时立即访问数据?

此外,我使用了钥匙串方法,因为我读到它是最安全的用户信息之一,但如果有人认为有比使用钥匙串更简单的方法,我愿意接受其他选择.

非常感谢您提供的任何帮助!!

我认为问题在于您将 requestMetaField 视为同步函数,而实际上它是异步的。您的 HTTP 请求将在 requestMetaField returns 之后的某个时间完成。

另一种方法是编写 requestMetaField 的一个版本,该版本采用请求完成时调用的回调函数。然后你可以从回调中更新你的UI。

下面是 requestMetaField 的样子:

func requestMetaField(metaField:String, callback: @escaping (String) -> Void) {

        let userEmail: String = KeychainWrapper.standard.string(forKey: "email")!;
        let userPassword: String = KeychainWrapper.standard.string(forKey: "password")!;
        let meta = metaField;

        // Send data to server side
        // Classic HTTP POST Request using JSONSerialization here.

                    if(resultValue=="Success") {

                        // Save downloaded data to keychain
                        self.saveDataToKeychain(value: metaValue, key: meta);

                        // call callback with value
                        callback(metaValue)
                    }

viewDidLoad 中的代码如下所示:

    requestMetaField(metaField: "first_name") { firstName in 
        requestMetaField(metaField: "last_name") { lastName in
            DispatchQueue.main.async {        
                userFullName.text = "\(firstName) \(lastName)"
            }
        }
    }

    requestMetaField(metaField: "mycred_default_total") { userMinuteBalance in
        DispatchQueue.main.async {        
            userBalance.text = "\(userMinuteBalance) min"
        }
    }