Салют, меня зовут Макс Нечаев, я занимаюсь продуктовой разработкой (iOS Developer). Сегодня решил сделать небольшую статью, которая расскажет, как подключить Apple Sign In в ваше iOS приложение. Плюс расскажу некоторые edge кейсы технологии.
UI часть задачи
Заходим в наше View. Первое, что нам нужно, это импортировать сервисы авторизации.
import AuthenticationServices
Вопреки расхожему мифу, можно сделать кастомную кнопку авторизации Apple, а не использовать ASAuthorizationAppleIDButton. Так я и поступил. Я взял главную кнопку, которая используется в приложении и добавил ей Apple стилистику по дизайну. После чего накинул на нее классический таргет.
private let appleButton = PrimaryButton(style: .appleSignIn) // appleButton.addTarget(self, action: #selector(loginWithApple), for: .touchUpInside)
По дизайну в нашем приложении кнопка находится внизу логин страницы, поэтому я добавил на кнопку плавающий нижний констрейнт (верстка через SnapKit). При открытии и закрытии клавиатуры, я анимационно меняю нижний констрейнт, что позволяет кнопке всегда держаться над клавиатурой. Так, с точки зрения UX пользователь не теряет возможности использовать Apple для авторизации вне зависимости от состояния клавиатуры.
appleButton.snp.makeConstraints { $0.leading.equalToSuperview().offset(16) $0.trailing.equalToSuperview().offset(-16) $0.height.equalTo(56) appleButtonBottomConstraint = $0.bottom .equalTo(view.safeAreaLayoutGuide.snp.bottom) .offset(-16) .constraint }
Мы используем VIPER, следовательно всю логику Apple авторизации я решил вынести в отдельный сервис, доступ к этому сервису будет иметь Interactor. Вся задача View (в качестве View у нас ViewController), это поймать нажатие на кнопку и сказать Presenter об этом. Presenter, в свою очередь, оповещает Interactor, тот обращается к сервису, который уже обрабатывает всю логику. После чего приложение меняет свое состояние и выполняется обратная цепочка от Interactor к View.
Проще говоря, инкапсулируем всю логику авторизации в отдельный сервис. Сейчас я покажу, как реализовал его.
Реализация сервиса авторизации
В качестве доступа к сервису использовал синглтон. Многие скажут, что не стоит так делать, отчасти согласен. Но в нашем проекте некоторые сервисы работают через него, все в порядке. Вы можете реализовать другой тип зависимостей.
final class AppleAuthManager: NSObject { static let shared = AppleAuthManager()
Создаем структуру данных, которые нам понадобятся для отправки запроса авторизации на сервер.
struct AppleAuthData { let identityToken: String let authorizationCode: String let firstName: String? let lastName: String? }
Я пошел классическим путем обработки через completion handler. Соответственно создаем приватную переменную с enum Result, который будет возвращать либо нашу заполненную модель, либо ошибку.
// MARK: - Private properties private var completionHandler: ((Result<AppleAuthData, Error>) -> Void)?
Весь доступ к сервису осуществляется через публичный метод login, который принимает в себя такой же клоужер, как мы создали ранее. Ниже в комментариях к коду добавлю пояснения.
// MARK: - Public methods func login(completion: @escaping (Result<AppleAuthData, Error>) -> Void) { // сетим в наш хэндлер комплишн функции self.completionHandler = completion // создаем провайдер и запрос, говорим запросу тот скоуп данных, // которые он должен предоставить let appleIDProvider = ASAuthorizationAppleIDProvider() let request = appleIDProvider.createRequest() request.requestedScopes = [.fullName, .email] // сетим в контроллер авторизации наш запрос // подписываемся на делегат, который и даст нам то, что нужно, и делаем запрос let authorizationController = ASAuthorizationController(authorizationRequests: [request]) authorizationController.delegate = self authorizationController.performRequests() }
Далее самое главное, реализуем делегат ASAuthorizationControllerDelegate. Покажу сначала простую часть. Если авторизация по какой-либо причине закончилась с ошибкой, то просто передаем хэндлеру эту ошибку, её нам нужно будет показать в отдельном алерте.
func authorizationController( controller: ASAuthorizationController, didCompleteWithError error: Error ) { completionHandler?(.failure(error)) }
Наконец мы дошли до самого интересного метода делегата didCompleteWithAuthorization. Именно здесь мы должны сначала через guard let получить данные входа (credential), а также необходимый нам identityTokenData, который следует привести к String формату через (encoding: .utf8). На словах может быть непонятно, поэтому конечно же предоставляю сниппет кода.
func authorizationController( controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization ) { // получаем и кастим данные guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential, let authorizationCodeData = credential.authorizationCode, let identityTokenData = credential.identityToken, let authorizationCode = String(data: authorizationCodeData, encoding: .utf8), let identityToken = String(data: identityTokenData, encoding: .utf8) else { return } // приводим их к нашему типу let data = AppleAuthData( identityToken: identityToken, authorizationCode: authorizationCode, firstName: credential.fullName?.givenName, lastName: credential.fullName?.familyName ) // вызываем комплишн и передаем в него данные completionHandler?(.success(data)) }
Важные моменты
-
Apple предоставляет имя и фамилию пользователя только один раз. В самый первый раз, когда пользователь входит через Apple. Обязательно отлавливайте эти данные и передавайте их на сервер в базу данных.
-
При авторизации есть возможность скрыть свой email (”Hide my email”), в таком случае Apple сгенерирует кастомный email, который будет привязан к пользователю. То есть совсем без email вы не останетесь, просто будет выбор из двух: персональная почта или почта apple.
Концовка
Теперь вам останется только обратиться к сервису в Interactor (если используете VIPER), и в комплишн хэндлере обработать события. Если успешно — отправляете запрос на сервер с нужными данными авторизации, если нет, выводите алерт. Плюс ко всему этому можно подключить аналитику.
Я постарался подробно, шаг за шагом описать, как вы можете подключить Apple авторизацию в свой проект, надеюсь, что это кому-то поможет.
Спасибо за ваше время, оно бесценно. Если есть пожелания или вопросы, пишите здесь в комментарии или мне в телеграм @maxnech.
Хорошего дня!
ссылка на оригинал статьи https://habr.com/ru/post/696646/
Добавить комментарий