Sign in with Apple

2 minute read Published: 2020-12-05

Apple SignIn feature를 DelegateProxy 를 이용하여 Rx Extension 구현

rxswift extension


import AuthenticationServices
import RxCocoa
import RxSwift
import UIKit

@available(iOS 13.0, *)
extension ASAuthorizationController: HasDelegate {
    public typealias Delegate = ASAuthorizationControllerDelegate
}

@available(iOS 13.0, *)
class ASAuthorizationControllerProxy: DelegateProxy<ASAuthorizationController, ASAuthorizationControllerDelegate>,
    DelegateProxyType,
    ASAuthorizationControllerDelegate,
ASAuthorizationControllerPresentationContextProviding {

    var presentationWindow: UIWindow = UIWindow()

    public init(controller: ASAuthorizationController) {
        super.init(parentObject: controller, delegateProxy: ASAuthorizationControllerProxy.self)
    }

    // MARK: - DelegateProxyType
    public static func registerKnownImplementations() {
        register { ASAuthorizationControllerProxy(controller: $0) }
    }

    // MARK: - Proxy Subject
    internal lazy var didComplete = PublishSubject<ASAuthorization>()

    // MARK: - ASAuthorizationControllerDelegate
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        didComplete.onNext(authorization)
        didComplete.onCompleted()
    }

    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        didComplete.onCompleted()
    }

    // MARK: - ASAuthorizationControllerPresentationContextProviding
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        return presentationWindow
    }

    // MARK: - Completed
    deinit {
        self.didComplete.onCompleted()
    }
}



@available(iOS 13.0, *)
extension Reactive where Base: ASAuthorizationAppleIDProvider {
    public func login(scope: [ASAuthorization.Scope]? = nil) -> Observable<ASAuthorization> {
        let request = base.createRequest()
        request.requestedScopes = scope

        let controller = ASAuthorizationController(authorizationRequests: [request])

        let proxy = ASAuthorizationControllerProxy.proxy(for: controller)

        controller.presentationContextProvider = proxy
        controller.performRequests()

        return proxy.didComplete
    }
}

@available(iOS 13.0, *)
extension Reactive where Base: ASAuthorizationAppleIDButton {
    public func loginOnTap(scope: [ASAuthorization.Scope]? = nil) -> Observable<ASAuthorization> {
        return controlEvent(.touchUpInside)
            .flatMap {
                ASAuthorizationAppleIDProvider().rx.login(scope: scope)
        }
    }

    public func login(scope: [ASAuthorization.Scope]? = nil) -> Observable<ASAuthorization> {
        return ASAuthorizationAppleIDProvider().rx.login(scope: scope)
    }
}
 if #available(iOS 13.0, *) {
            appleSignInButton.rx
                .loginOnTap(scope: [.fullName, .email])
                .map { $0.credential as? ASAuthorizationAppleIDCredential }
                .filterNil()
                .subscribe(onNext: { [weak self] credential in
                    guard let `self` = self else { return }
                    FireBaseAnalytics.shared.sendEvent(name: "login",
                                                       params: ["method":"apple"])

                    CommonSignInRoutines.onTouchConnectWithApple(self)
                    AppleAuthenticationService.shared.signInPublisher.onNext(credential)

                }, onError: { error in
                    logger.error("#### error \(error.localizedDescription)", context: LogCategory.appleLogin)
                }).disposed(by: self.disposeBag)
        }

resource