Автоматически сгенерированные пароли в iOS 12
Если в вашем приложении есть функция регистрации, включающая в себя возможность или необходимость ввода новых имени пользователя и пароля, скорее всего, вас заинтересует нововведение в «iOS 12», которое я хотел бы обрисовать. Это сервис, который придумывает новые пароли для пользователя, автоматически подставляет их в нужные поля и безопасно хранит в «Keychain».
Автоматически-сгенерированные системой пароли являются наиболее стойкими к подбору (будучи случайно-сгенерированными последовательностями символов — с поправкой на настраиваемые ограничения, но об этом позже), избавляют пользователей приложения от необходимости придумывать последовательность самостоятельно и гибко настраиваются под нужды конкретного приложения. Поддержка новой функциональности довольно легко обеспечивается, тем не менее не без особенностей. Но обо всем по порядку.
Права и обязанности
В первую очередь, приложение должно заявить о своем желании пользоваться этим функционалом. В список «Capabilities» соответствующего «Target» необходимо иметь, во-первых, домен в списке «Associated Domains». Как ни странно, приложение должно иметь «Associated Domain», чтобы иметь возможность пользоваться сгенерированными паролями и хранить их в «Keychain» пользователя (две эти функции взаимосвязаны, и генерация не может быть использована отдельно от хранения).
Если приложение уже поддерживает использование общих учетных записей с вашим сайтом (т.н. «Shared Credentials»), то этот шаг уже позади. Также он может быть уже позади, и если приложение поддерживает «Universal Links» или другой механизм обработки внешних «URL»-ссылок.
Так или иначе, после добавления этой совместимости у приложения появится новое «Entitlement».
Помимо этой, более общей, совместимости приложение также должно иметь «Capatibility» «AutoFill Credential Provider» — это дает возможность приложению, при наличии разрешения от пользователя, пользоваться предложенными системой логинами и паролями. Добавление этой совместимости повлечет появление разрешения «AutoFill Credential Provider Entitlement».
К слову, добавление этой и других возможностей доступно только членам «Apple Developer Program».
В используемый приложением «Provisioning Profile» также должны быть включены перечисленные две функции.
Используемые зависимости
Добавление соответствующих совместимостей повлечет за собой появление фреймворка «AuthenticationServices» в списке «Linked Frameworks and Libraries» соответствующего «таргета». Этот момент обладает некоторыми особенностями, которые стоит упомянуть.
Во-первых, автоматическое добавление связанного фреймворка может не «сработать» с первого раза: при запуске приложения на реальном устройстве из моего экземпляра «Xcode» версии 10.1, приложение сразу «падало» ввиду отсутствия «AuthenticationServices». Ручное удаление фреймворка и добавление его обратно в список связанных компонентов решило проблему.
Во-вторых, автоматическое добавление фреймворка помечает его как «Required». Если ваше приложение подразумевает возможность работы «под» «iOS» версий ниже 12, это также вызовет его падение на этапе запуска «из-под» операционных систем более низких версий. «AuthenticationServices» доступны только для систем версий от 12-ой. Эта проблема решается отмечанием фреймворка как «Optional».
Поддержка в текстовых полях
Для поддержки функционала текстовыми полями используется переменная textContentType
протокола UITextInputTraits
. Класс UITextField
, который, скорее всего, используется для ввода логина и пароля в приложении, уже реализует требования нужного нам протокола.
textContentType
— это поле типа UITextContentType
, содержащего лишь набор констант. Нужная нам в данный момент — newPassword
, используемая для ввода нового, придумываемого в данный момент, пароля (не путать с просто password
, используемой для ввода уже существующего пароля).
let passwordTextField = UITextField()
if #available(iOS 12, *) {
passwordTextField.textContentType = .newPassword
}
Установка значения textContentType
обернуто в проверку доступности «API», потому что, как и общий функционал, эта константа доступна только начиная с «iOS 12».
Помимо типа содержимого, текстовое поле обязательно должно обеспечивать безопасный ввод данных:
passwordTextField.isSecureTextEntry = true
Хотя приложение вполне может обеспечивать популярный в наше время функционал отображения и скрытия введенного пароля, сгенерированный пароль будет предложен, только если в этот момент флаг имеет значение true
.
С типом содержимого текстового поля связан интересный момент: если на экране не будет присутствовать другое поле, с типом содержимого username
, автоматически-сгенерированный пароль предложен не будет. Это связано с тем, что функционал основан не просто на указанном типе контента текстоваого поля, а на эвристическом анализе содержимого экрана.
Кажется, генерация пароля «ломает» логику экранов, которые требуют ввести для проверки новый пароль дважды. По-крайней мере, я пока не нашел способ использовать эти две функциональности совместно. И, похоже, я такой не один.
Стоит упомянуть, что если логин семантически является адресом электронной почты (и, следовательно, очень хочется иметь соответствующий вид клавиатуры), типы клавиатуры и содержимого допустимо сочетать:
let userNameTextField = UITextField()
userNameTextField.keyboardType = .emailAddress
userNameTextField.textContentType = .username
Требования к паролям
Часто пользовательские пароли должны соответствовать некоторым правилам (иметь определенную длину, включать в себя определенные символы и т.д.). Эти правила можно указать для того, чтобы система учитывала их при генерации паролей. Это делается через свойство passwordRules
протокола UITextInputTraits
. Например:
if #available(iOS 12, *) {
passwordTextField.passwordRules = UITextInputPasswordRules(descriptor: "required: upper; required: lower; required: digit; minlength: 8;")
}
Свойство также доступно только начиная с «iOS 12».
Тип свойства — UITextInputPasswordRules
. Инициализация — с помощью строки-дескриптора. Дескриптор имеет несложный синтаксис и состоит из простых требований к паролю, перечисленных через точку с запятой. Каждое требование — это пара «ключ-значение», разделенные двоеточием. Ключ представляет собой тип правила (например, «обязательно включает» — required
), а значение — элемент, который должен следовать этому правилу (например, цифры — digit
).
В примере выше дескриптор означает:
required: upper
— необходимо присутствие хотя бы одной прописной буквы;required: lower
— то же для хотя бы одной строчной буквы;required: digit
— то же для хотя бы одной строчной цифры;minlength: 8
— минимальная длина — восемь символов.
Подробное перечисление возможных ключей и значений можно найти в неплохой статье, опубликованной на сайте «NSHipster».
А «Apple» предлагает довольно удобного помощника по составлению дескрипторов, который предоставляет не только удобный способ их конструирования, но и проверку составленных дескрипторов в виде неограниченного количества сгенерированных примеров. Там же можно посмотреть, какие правила применяются по-умолчанию.
Валидация
На всякий случай следует уточнить, что механизм генерации паролей не обеспечивает валидацию введенных пользователем данных — об этом необходимо позаботиться самостоятельно. Что, конечно, вполне логично, т.к. пользователь приложения может отказаться от предложенного автоматически-сгенерированного пароля или даже запретить генерацию паролей и автозаполнение полей.
Interface Builder
Что примечательно и в духе нашего времени, все перечисленные настройки текстовых полей можно выставить в «Interface Builder», вплоть до «Password Rule»:
Проверка функциональности
Функционал не сложный, но обладает рядом нюансов: при его настройке легко можно что-нибудь забыть. В этом случае в «debug»-сборках при активации соответствующего текстового поля в консоль будет выведена причина, по которой функциональность в данный момент не действует.
Например:
[AutoFill] Cannot show Automatic Strong Passwords for app bundleID: <...> due to error: Cannot save passwords for this app. Make sure you have set up Associated Domains for your app and AutoFill Passwords is enabled in Settings
Также всегда следует иметь в виду, что данный вид функциональности входит в число действий, на которые требуется разрешение пользователя. В данном случае требуется целых два:
1. iCloud Keychain;
2. AutoFill.
Заключение
Кажется это все, на что следует обратить внимание во время обеспечения поддержки нового функционала. Если кто-то в процессе работы над этим столкнулся с другими интересными особенностями, буду раз комментариям и, при необходимости, обязательно дополню статью.
Особенность довольно интересная и при правильном ее использовании вполне способна улучшить пользовательский опыт вашего приложения!