- PVSM.RU - https://www.pvsm.ru -
Если в вашем приложении есть функция регистрации, включающая в себя возможность или необходимость ввода новых имени пользователя и пароля, скорее всего, вас заинтересует нововведение в «iOS 12» [1], которое я хотел бы обрисовать. Это сервис, который придумывает новые пароли для пользователя, автоматически подставляет их в нужные поля и безопасно хранит в «Keychain» [2].
Автоматически-сгенерированные системой пароли являются наиболее стойкими к подбору (будучи случайно-сгенерированными последовательностями символов – с поправкой на настраиваемые ограничения, но об этом позже), избавляют пользователей приложения от необходимости придумывать последовательность самостоятельно и гибко настраиваются под нужды конкретного приложения. Поддержка новой функциональности довольно легко обеспечивается, тем не менее не без особенностей. Но обо всем по порядку.
В первую очередь, приложение должно заявить о своем желании пользоваться этим функционалом. В список «Capabilities» [3] соответствующего «Target» [4] необходимо иметь, во-первых, домен в списке «Associated Domains» [5]. Как ни странно, приложение должно иметь «Associated Domain», чтобы иметь возможность пользоваться сгенерированными паролями и хранить их в «Keychain» пользователя (две эти функции взаимосвязаны, и генерация не может быть использована отдельно от хранения).
Если приложение уже поддерживает использование общих учетных записей с вашим сайтом (т.н. «Shared Credentials») [6], то этот шаг уже позади. Также он может быть уже позади, и если приложение поддерживает «Universal Links» [7] или другой механизм обработки внешних «URL» [8]-ссылок.
Так или иначе, после добавления этой совместимости у приложения появится новое «Entitlement» [9].
Помимо этой, более общей, совместимости приложение также должно иметь «Capatibility» «AutoFill Credential Provider» [10] – это дает возможность приложению, при наличии разрешения от пользователя, пользоваться предложенными системой логинами и паролями. Добавление этой совместимости повлечет появление разрешения «AutoFill Credential Provider Entitlement» [11].
К слову, добавление этой и других возможностей доступно только членам «Apple Developer Program» [12].
В используемый приложением «Provisioning Profile» также должны быть включены перечисленные две функции.
Добавление соответствующих совместимостей повлечет за собой появление фреймворка [13] «AuthenticationServices» [14] в списке «Linked Frameworks and Libraries» соответствующего «таргета». Этот момент обладает некоторыми особенностями, которые стоит упомянуть.
Во-первых, автоматическое добавление связанного фреймворка может не «сработать» с первого раза: при запуске приложения на реальном устройстве из моего экземпляра «Xcode» [15] версии 10.1, приложение сразу «падало» ввиду отсутствия «AuthenticationServices». Ручное удаление фреймворка и добавление его обратно в список связанных компонентов решило проблему.
Во-вторых, автоматическое добавление фреймворка помечает его как «Required». Если ваше приложение подразумевает возможность работы «под» «iOS» версий ниже 12, это также вызовет его падение на этапе запуска «из-под» операционных систем более низких версий. «AuthenticationServices» доступны только для систем версий от 12-ой. Эта проблема решается отмечанием фреймворка как «Optional» [16].
Для поддержки функционала текстовыми полями используется переменная textContentType
[17] протокола UITextInputTraits
[18]. Класс UITextField
[19], который, скорее всего, используется для ввода логина и пароля в приложении, уже реализует требования нужного нам протокола.
textContentType
– это поле типа UITextContentType
[20], содержащего лишь набор констант. Нужная нам в данный момент – newPassword
[21], используемая для ввода нового, придумываемого в данный момент, пароля (не путать с просто password
[22], используемой для ввода уже существующего пароля).
let passwordTextField = UITextField()
if #available(iOS 12, *) {
passwordTextField.textContentType = .newPassword
}
Установка значения textContentType
обернуто в проверку доступности «API» [23], потому что, как и общий функционал, эта константа доступна только начиная с «iOS 12».
Помимо типа содержимого, текстовое поле обязательно должно обеспечивать безопасный ввод данных:
passwordTextField.isSecureTextEntry = true
Хотя приложение вполне может обеспечивать популярный в наше время функционал отображения и скрытия введенного пароля, сгенерированный пароль будет предложен, только если в этот момент флаг имеет значение true
[24].
С типом содержимого текстового поля связан интересный момент: если на экране не будет присутствовать другое поле, с типом содержимого username
[25], автоматически-сгенерированный пароль предложен не будет. Это связано с тем, что функционал основан не просто на указанном типе контента текстоваого поля, а на эвристическом анализе содержимого экрана [26].
Кажется, генерация пароля «ломает» логику экранов, которые требуют ввести для проверки новый пароль дважды. По-крайней мере, я пока не нашел способ использовать эти две функциональности совместно. И, похоже, я такой не один [27].
Стоит упомянуть, что если логин семантически является адресом электронной почты (и, следовательно, очень хочется иметь соответствующий вид клавиатуры), типы клавиатуры [28] и содержимого допустимо сочетать:
let userNameTextField = UITextField()
userNameTextField.keyboardType = .emailAddress
userNameTextField.textContentType = .username
Часто пользовательские пароли должны соответствовать некоторым правилам (иметь определенную длину, включать в себя определенные символы и т.д.). Эти правила можно указать для того, чтобы система учитывала их при генерации паролей. Это делается через свойство passwordRules
[29] протокола UITextInputTraits
. Например:
if #available(iOS 12, *) {
passwordTextField.passwordRules = UITextInputPasswordRules(descriptor: "required: upper; required: lower; required: digit; minlength: 8;")
}
Свойство также доступно только начиная с «iOS 12».
Тип свойства – UITextInputPasswordRules
[30]. Инициализация – с помощью строки-дескриптора. Дескриптор имеет несложный синтаксис и состоит из простых требований к паролю, перечисленных через точку с запятой. Каждое требование – это пара «ключ-значение», разделенные двоеточием. Ключ представляет собой тип правила (например, «обязательно включает» – required
), а значение – элемент, который должен следовать этому правилу (например, цифры – digit
).
В примере выше дескриптор означает:
required: upper
– необходимо присутствие хотя бы одной прописной буквы;required: lower
– то же для хотя бы одной строчной буквы;required: digit
– то же для хотя бы одной строчной цифры;minlength: 8
– минимальная длина – восемь символов.Подробное перечисление возможных ключей и значений можно найти в неплохой статье, опубликованной на сайте «NSHipster» [31].
А «Apple» [32] предлагает довольно удобного помощника по составлению дескрипторов [33], который предоставляет не только удобный способ их конструирования, но и проверку составленных дескрипторов в виде неограниченного количества сгенерированных примеров. Там же можно посмотреть, какие правила применяются по-умолчанию.
На всякий случай следует уточнить, что механизм генерации паролей не обеспечивает валидацию введенных пользователем данных – об этом необходимо позаботиться самостоятельно. Что, конечно, вполне логично, т.к. пользователь приложения может отказаться от предложенного автоматически-сгенерированного пароля или даже запретить генерацию паролей и автозаполнение полей.
Что примечательно и в духе нашего времени, все перечисленные настройки текстовых полей можно выставить в «Interface Builder» [34], вплоть до «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.
Кажется это все, на что следует обратить внимание во время обеспечения поддержки нового функционала. Если кто-то в процессе работы над этим столкнулся с другими интересными особенностями, буду раз комментариям и, при необходимости, обязательно дополню статью.
Особенность довольно интересная и при правильном ее использовании вполне способна улучшить пользовательский опыт [35] вашего приложения!
Автор: Никита Лазарев-Зубов
Источник [36]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ios-development/307451
Ссылки в тексте:
[1] «iOS 12»: https://apple.com/ru/ios/ios-12/
[2] «Keychain»: https://developer.apple.com/documentation/security/keychain_services
[3] «Capabilities»: https://help.apple.com/xcode/mac/current/#/dev88ff319e7
[4] «Target»: https://developer.apple.com/library/archive/featuredarticles/XcodeConcepts/Concept-Targets.html
[5] «Associated Domains»: https://developer.apple.com/documentation/security/password_autofill/setting_up_an_app_s_associated_domains
[6] использование общих учетных записей с вашим сайтом (т.н. «Shared Credentials»): https://developer.apple.com/documentation/security/shared_web_credentials
[7] «Universal Links»: https://developer.apple.com/ios/universal-links/
[8] «URL»: https://en.wikipedia.org/wiki/URL
[9] «Entitlement»: https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AboutEntitlements.html
[10] «AutoFill Credential Provider»: https://help.apple.com/developer-account/#/deva6b71b1d0
[11] «AutoFill Credential Provider Entitlement»: https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_authentication-services_autofill-credential-provider
[12] «Apple Developer Program»: https://developer.apple.com/support/app-capabilities/
[13] фреймворка: https://en.wikipedia.org/wiki/Software_framework
[14] «AuthenticationServices»: https://developer.apple.com/documentation/authenticationservices
[15] «Xcode»: https://developer.apple.com/xcode/
[16] «Optional»: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WeakLinking.html
[17] textContentType
: https://developer.apple.com/documentation/uikit/uitextinputtraits/1649656-textcontenttype
[18] UITextInputTraits
: https://developer.apple.com/documentation/uikit/uitextinputtraits
[19] UITextField
: https://developer.apple.com/documentation/uikit/uitextfield
[20] UITextContentType
: https://developer.apple.com/documentation/uikit/uitextcontenttype
[21] newPassword
: https://developer.apple.com/documentation/uikit/uitextcontenttype/2980929-newpassword
[22] password
: https://developer.apple.com/documentation/uikit/uitextcontenttype/2865813-password
[23] «API»: https://en.wikipedia.org/wiki/Application_programming_interface
[24] true
: https://developer.apple.com/documentation/swift/true
[25] username
: https://developer.apple.com/documentation/uikit/uitextcontenttype/2866088-username
[26] эвристическом анализе содержимого экрана: https://developer.apple.com/documentation/security/password_autofill/enabling_password_autofill_on_a_text_input_view
[27] я такой не один: https://stackoverflow.com/questions/52850197/ios-12-swift-strong-password-autofill-doesnt-work-as-expected
[28] типы клавиатуры: https://developer.apple.com/documentation/uikit/uitextinputtraits/1624457-keyboardtype
[29] passwordRules
: https://developer.apple.com/documentation/uikit/uitextinputtraits/2980934-passwordrules
[30] UITextInputPasswordRules
: https://developer.apple.com/documentation/uikit/uitextinputpasswordrules
[31] неплохой статье, опубликованной на сайте «NSHipster»: https://nshipster.com/uitextinputpasswordrules/
[32] «Apple»: https://apple.com/
[33] помощника по составлению дескрипторов: https://developer.apple.com/password-rules/
[34] «Interface Builder»: https://developer.apple.com/xcode/interface-builder/
[35] пользовательский опыт: https://en.wikipedia.org/wiki/User_experience
[36] Источник: https://habr.com/ru/post/438580/?utm_source=habrahabr&utm_medium=rss&utm_campaign=438580
Нажмите здесь для печати.