Как я подключил Sign in with Apple — Apple авторизацию. Подробный гайд

Салют, меня зовут Макс Нечаев, я занимаюсь продуктовой разработкой (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/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *