На WWDC 2019 Apple в очередной раз нарушила покой iOS разработчиков — представила новую систему авторизации пользователей Sign in with Apple. Теперь все iOS приложения, которые используют сторонние системы авторизации (Facebook, Twitter, etc.), должны в обязательном порядке реализовать Sign in with Apple, иначе выгонят из AppStore. Мы решили не испытывать судьбу и побежали внедрять эту фичу. Как именно мы это сделали — узнаете под катом.
В своей работе мы используем архитектуру VIPER+SOA, поэтому авторизацию через Apple мы сделали как отдельный сервис. Сначала мы оборачиваем данные в enum, чтобы удобно расширять типы авторизации (фейсбук, вк, гугл и т.д.):
Результат наружу будем передавать с помощью Observable из RxSwift:
Реализация протокола:
Статья получилось небольшой, но надеемся что она была полезной для вас.
Благодарим за внимание!
Пишем сервис авторизации через Apple
В своей работе мы используем архитектуру VIPER+SOA, поэтому авторизацию через Apple мы сделали как отдельный сервис. Сначала мы оборачиваем данные в enum, чтобы удобно расширять типы авторизации (фейсбук, вк, гугл и т.д.):
enum AuthToken {
case apple(code: String, name: String)
}
Результат наружу будем передавать с помощью Observable из RxSwift:
protocol AuthProviderProtocol {
var authResult: Observable<AuthToken> { get }
func login()
func logout()
}
Реализация протокола:
import AuthenticationServices
import Foundation
import RxSwift
@available(iOS 13.0, *)
class AppleAuthService: AuthProviderProtocol {
private let authResultSubject = PublishSubject<AuthToken>()
var authResult: Observable<AuthToken> {
return authResultSubject.asObservable()
}
func login() {
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.performRequests()
}
}
@available(iOS 13.0, *)
extension AppleAuthService: ASAuthorizationControllerDelegate {
func authorizationController(
controller: ASAuthorizationController,
didCompleteWithAuthorization authorization: ASAuthorization
) {
guard
let credential = authorization.credential as? ASAuthorizationAppleIDCredential,
let tokenData = credential.authorizationCode,
let token = String(data: tokenData, encoding: .utf8)
else { return }
let firstName = credential.fullName?.givenName
let lastName = credential.fullName?.familyName
authResultSubject.onNext(.apple(code: token, name: firstName + lastName))
}
}
Нюансы, о которых нужно знать
- У Sign in with Apple нету функции logout в классическом понимании этого слова. Библиотека не хранит никакие данные, в отличие от других библиотек входа, поэтому нет необходимости стирать данные, полученные при логине.
- Sign in with Apple получает имя и фамилию пользователя только один раз при самом первом логине. У сервера нет доступа к этим данным. При последующих попытках входа вам будут приходить только authorizationCode из ASAuthorizationAppleIDCredential. Поэтому мы на клиентской стороне храним имя и фамилию пользователя до тех пор, пока регистрация на сервере не завершится успешно.
- Sign in with Apple позволяет пользователю подменить свой e-mail. На подмененный e-mail можно написать только с тех доменов, которые вы укажете в настройках на developer.apple.com
- В этой статье описано то, как мы реализовали back-end часть.
Статья получилось небольшой, но надеемся что она была полезной для вас.
Благодарим за внимание!