Доброго времени суток, друзья!
Возможно, у вас уже есть приложение с регистрацией / логином и вы задумались, как можно улучшить безопасность ваших пользователей?
На помощь придет биометрическая аутентификация (Touch ID/Face ID). Она обеспечит пользователям безопасность и легкий доступ в приложение. Звучит круто, не правда ли? Давайте посмотрим, как мы можем добавить Touch ID и FaceID в приложение.
С этой задачей нам поможет справиться фреймворк от Apple под названием LocalAuthentication, который, прошу заметить, работает прямо из коробки. Поэтому можно обойтись без сторонних библиотек.
Чтобы начать работать с фреймворком, для начала его нужно импортировать в контроллер, который будет выполнять эту функцию (import LocalAuthentication).
Хочу предложить вам сделать этот PasscodeViewController не в storyboard, а xib файлом, потому что в конце статьи вам будет ожидать приятный бонус! =)
Итак, создаем в PasscodeViewController кнопку и делаем для нее @IBAction, где и произойдет наша “магия”. Для начала нужно создать экземпляр класса context, он является механизмом оценки политик аутентификации и контроля доступа (let context = LAContext()).
Далее нам нужно проверить может ли аутентификация продолжаться для данного приложения. Например, приложение, которое требует биометрических данных, не может аутентифицироваться, если Touch ID или Face ID отключены. Context накладывает определенные требования, которые должны быть выполнены, прежде чем аутентификация может продолжаться. Например, действие, которое требует биометрических данных, не может аутентифицироваться, если Touch ID или Face ID отключены. Поэтому сначала делаем проверку (if context.canEvaluatePolicy(_ policy: LAPolicy, error: NSErrorPointer)). Где в графе policy можно выбрать одно из значений:
- deviceOwnerAuthenticationWithBiometrics - аутентификация пользователя с помощью биометрии (это и выберем);
- deviceOwnerAuthenticationWithWatch - аутентификация пользователя с помощью Apple Watch;
- deviceOwnerAuthenticationWithBiometricsOrWatch - аутентификация пользователя с помощью биометрии или Apple Watch;
- deviceOwnerAuthentication - аутентификация пользователя с помощью биометрии, Apple Watch или пароля устройства.
После того, как мы проверили возможность аутентификации нам нужно вызвать метод (context.evaluatePolicy(_ policy: LAPolicy, localizedReason: String, reply: @escaping (Bool, Error?) -> Void)). Этот метод асинхронно оценивает политику аутентификации. Оценка политики может включать запрос пользователя на различные виды взаимодействия или аутентификации. Фактическое поведение зависит от оцениваемой политики и типа устройства. На поведение также могут влиять установленные профили конфигурации.
В localizedReason, которую вы предоставляете пользователю в диалоговом окне аутентификации, указываете причину запроса аутентификации, например: “Please authenticate to proceed.”
И дальше в closure мы работаем с 2 значениями (success, error).
Не предполагайте, что предыдущая успешная оценка политики означает, что будущие оценки также будут успешными. Оценка политики может быть неудачной по разным причинам, включая отмену пользователем или системой. Поэтому обрабатывать error нужно в любом случае, чтобы пользователь понимал причину ошибки.
В этом поможет свойство error.localizedDescription, которое возвращает локализованное описание для этой ошибки.
И нам остается сделать проверку success аутентификации (if success { открываем доступ к приложению } else { указываем причину отказа }).
Если, после успешной аутентификации, вы собираетесь обновить часть пользовательского интерфейса, не забывайте всегда использовать DispatchQueue.main.async для запуска этих задач. Изменения пользовательского интерфейса должны выполняться в основном потоке.
Вот код, который у нас получился.
@IBAction func useBiometrics(sender: UIButton) {
let context = LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Please authenticate to proceed.") { (success, error) in
if success {
DispatchQueue.main.async {
// Что-то сделать
self.dismiss(animated: true, completion: nil)
}
} else {
guard let error = error else { return }
print(error.localizedDescription)
}
}
}
}
Попробуйте протестировать Вашу работу на реальном устройстве. Если у вас нет реального устройства, не беспокойтесь, используйте опции “Hardware” -> “Touch ID”/”Face ID”.
Для тестирования на симуляторе сначала поставьте галочку на “Enrolled”, а потом “Matching Touch/Matching Face” для виртуального сканирования.
P.S. Для работы Face ID в приложении, вам нужно добавить в info.plist дополнительную информацию. Где ключом (key) будет Privacy - Face ID Usage Description, а значение (value) будет описание причины, по которой ваше приложение использует Face ID.
БОНУС
Здесь я бы хотел поделиться дополнительной реализацией данного экрана в приложении.
Что если вам нужно проверять пользователя каждый раз, когда он заходит в приложение прям как во “взрослых” приложениях? Сейчас я покажу, как быстро это сделать =)
Для ios 13 (xcode 11) нам поможет метод в SceneDelegate sceneWillEnterForeground, который вызывается при переходе из фона на передний план.
Для ios 12 (xcode 10 и меньше) используем AppDelegate и метод applicationDidBecomeActive.
Именно здесь как раз и понадобится наш xib файл.
Создаем наш passcodeVC и инициализируем его с помощью nibName. После этого устанавливаем modalPresentationStyle = .overFullScreen (чтобы свайпом нельзя было убрать экран). И презентуем наш экран поверх всех экранов self.window?.rootViewController.present(passcodeVC, animated: true, completion: nil)
После успешной аутентификации останется лишь добавить метод self.dismiss(animated: true, completion: nil). И готов! Теперь при каждом входе в приложение, не важно выгружено оно из памяти или нет, будет появляться экран с аутентификацией пользователя!
func sceneWillEnterForeground(_ scene: UIScene) {
let passcodeVC = PasscodeViewController(nibName: "PasscodeViewController", bundle: nil)
passcodeVC.modalPresentationStyle = .overFullScreen
self.window?.rootViewController?.present(passcodeVC, animated: true, completion: nil)
}
Статью подготовил: Михаил Цейтлин
Конечный проект урока