#ios #swift #keychain
#iOS #swift #брелок
Вопрос:
Итак, у меня есть пользовательская функция, которая отправляет HTTP POST-запрос на мой веб-сервер для получения некоторых полей из моей базы данных, а затем возвращает его в мое приложение iOS, которое сохранит значение этого поля как элемент KeychainWrapper, используя KeychainWrapper.
Мое приложение для iOS так же просто, как экран входа в систему, который проверяет правильность учетных данных пользователя в базе данных MySQL, снова используя HTTP POST, и, если они верны, выводит меня на страницу пользователя, где предполагается отобразить некоторую информацию, полученную и отображаемую с помощью KeychainWrapper.
Теперь проблема, с которой я сталкиваюсь, заключается в том, что всякий раз, когда я нажимаю кнопку входа на странице входа и попадаю на страницу пользователя, все поля отображаются как null. Но если я остановлю сборку и повторю сборку, она загрузится обратно на эту страницу с полями, загруженными на этот раз. Как будто, когда я впервые захожу на страницу пользователя, поля еще не сохранены в связке ключей, но когда я ее перестраиваю, они сохраняются и загружаются оттуда.
Я вырежу некоторые части из своего кода, чтобы сделать его коротким и сохранить его актуальность для этого вопроса. Вот моя функция, которая запрашивает мета-поля из базы данных 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";
}
И здесь я сохраняю адрес электронной почты и пароль в keychain при использовании кнопки входа :
if(resultValue=="Success") {
// Login is successful
UserDefaults.standard.set(true, forKey: "isUserLoggedIn");
UserDefaults.standard.synchronize();
// Save email amp; password in keychain
self.saveDataToKeychain(value: userEmail!, key: "email");
self.saveDataToKeychain(value: userPassword!, key: "password");
isUserLoggedIn = true;
}
У кого-нибудь есть идея о том, как я мог бы получить доступ к данным сразу после входа в систему?
Кроме того, я использовал метод Keychain, потому что я читал, что это один из самых безопасных способов получения пользовательской информации, но я открыт для других вариантов, если кто-то считает, что есть более простой способ сделать это, чем с помощью keychain.
Большое спасибо за любую помощь, которую вы можете оказать!!
Ответ №1:
Я думаю, проблема в том, что вы обрабатываете requestMetaField
как синхронную функцию, когда она асинхронна. Ваш HTTP-запрос завершится только через некоторое время после requestMetaField
возврата.
Другим подходом было бы написать версию requestMetaField
, которая принимает функцию обратного вызова, вызываемую при завершении запроса. Затем вы можете обновить свой пользовательский интерфейс с помощью обратного вызова.
Вот как это 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"
}
}
Комментарии:
1. Я вижу, да, должно быть, это так, я вроде как новичок в разработке swift, возможно, я это пропустил. Я не очень знаком с функциями обратного вызова. Знаете ли вы какую-либо документацию или руководство, которые я мог бы использовать для завершения вашего «другого подхода»? Большое спасибо за ваш ответ!
2. За вычетом нескольких изменений, которые мне пришлось внести в соответствии с Xcode (не смог
value
ввестиcallback:(value: String)
, это было именно то, что мне было нужно. Большое спасибо, что нашли время для написания этого!!!3. Также поставил
@escaping
послеcallback:
, если кто-нибудь окажется здесь, пытаясь заставить это работать!