작업 내역

백로그

로그인 기능을 구현한다.

환경설정

Supabase 환경 설정은 아래 글에서 확인할 수 있습니다. 이 글에서는 그외 환경설정을 다룹니다.

Supabase 로그인 with idToken

Xcode Signing & Capabilities > + Capability 에서 Sign In with Apple을 활성화합니다.

image.png

만약 Automatically manage signing이 아닌 프로비저닝 파일을 따로 사용하고 있는 경우에는

Apple Developer > Identifiers 에서 해당 앱 번들 ID에서 Sign In with Apple을 활성화한 후 프로비저닝 파일을 재다운로드, import 해야 정상적으로 컴파일됩니다.

코드

로그인에 대한 결과는 델리게이트 패턴으로 받아볼 수 있습니다. 사용의 편의를 위해 Swift Concurrency 방식으로 래핑하는 작업을 거쳤습니다.

import AuthenticationServices

final class AppleAuthManager: NSObject, AppleAuthManageable {
/// ,,, 중략 
#if DEBUG
    private var continuation: CheckedContinuation<String, Error>?
#else
    private var continuation: UnsafeContinuation<String, Error>?
#endif 
 
 func signIn() async throws -> String {
        let request = provider.createRequest()
        authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController?.delegate = self
        authorizationController?.presentationContextProvider = self
        authorizationController?.performRequests()
#if DEBUG
        let idToken = try await withCheckedThrowingContinuation { [weak self] continuation in
            self?.continuation = continuation
        }
#else
        let idToken = try await withUnsafeThrowingContinuation { [weak self] continuation in
            self?.continuation = continuation
        }
#endif
        continuation = nil
        return idToken
    }
}

extension AppleAuthManager: ASAuthorizationControllerDelegate {
    func authorizationController(
        controller: ASAuthorizationController,
        didCompleteWithAuthorization authorization: ASAuthorization
    ) {
        guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential,
           let idTokenData = credential.identityToken,
           let idToken = String(data: idTokenData, encoding: .utf8) else {
            continuation?.resume(throwing: AppleAuthError.idTokenNotFound)
            return
        }
        continuation?.resume(returning: idToken)
    }
    func authorizationController(
        controller: ASAuthorizationController,
        didCompleteWithError error: any Error
    ) {
        continuation?.resume(throwing: AppleAuthError.signInFailed)
    }
}  

DEBUG 모드 에서는 CheckedContinuation, RELEASE 모드에서는 UnsafeContinuation을 사용하고 있습니다. CheckedContinuation은 런타임시 resume이 여러번 호출되는지 확인합니다.

UnsafeContinuation avoids enforcing these invariants at runtime because it aims to be a low-overhead mechanism for interfacing Swift tasks with event loops, delegate methods, callbacks, and other non-async scheduling mechanisms. However, during development, the ability to verify that the invariants are being upheld in testing is important.

CheckedContinuationresume이 두번 이상 호출될 시 앱 크래시를 발생시킵니다. 이에 비해 UnsafeContinuation은 오버헤드가 적습니다. 리소스 낭비로 이어질 수는 있지만, 앱 크래시가 발생하지는 않기 때문에 해당 코드를 모드에 따라 분기처리했습니다.