티스토리 뷰

가이드

SwiftUI에서 구글로그인 띄우기

Sunghyun Kim 2022. 7. 30. 10:09

SwiftUI에서 구글로그인 띄우기

구글 로그인은 API로 제공되며 UIKit 프레임워크에서 쉽게 작동시킬 수 있도록 설계되어 있다. 이제 SwiftUI가 점점 보편화 됨에 따라 API도 전환이 되겠지만 현재까지는 업데이트 되지 않은 상황이라 방법을 공유하고자 한다.

구글 로그인 화면 띄우기

구글 로그인은 로그인 뷰를 띄울 뷰 컨트롤러를 제공해주어야 한다.

signIn(with: GIDConfiguration, presenting: UIViewController, callback: GIDSignInCallback?)

아무 뷰 컨트롤러나 제공한다고 해서 표시되는 것이 아니고 반드시 현재 화면에 띄워져 있어야 하고 다른 뷰 컨트롤러를 프레젠트 할 수 있어야 한다. 그렇기 때문에 SwiftUI와 같이 현재 뷰컨트롤러에 바로 접근이 불가능할 경우, 표시중인 UIWindow의 rootViewController를 제공하여야 한다.

UIWindow가 하나뿐인 경우 강제로 가져올 수 있는 방법이 있다.

guard let presentingViewController = (
    UIApplication.shared.connectedScenes.first as? UIWindowScene
)?.windows.first?.rootViewController else {
    fatalError()
}

이 방법의 경우 아이폰의 경우 문제가 없지만 아이패드의 경우 여러 윈도우를 띄우고 있을 수 있어 예상치 못한 동작이 나타날 수 있다. 그리고 보기에도 좋지 않다.

SwiftUI에서 사용중인 UIWindow 가져오기

현재 뷰의 맨 뒤에 빈 뷰를 삽입하여 해당 빈 뷰의 현재 UIWindow를 가져와 사용할 수 있다.

먼저 구글 로그인을 표시할 객체를 표현하는 GoogleSignInPresentaionContextProviding 프로토콜을 작성한다.

public protocol GoogleSignInPresentaionContextProviding: AnyObject {
    func presentationAnchor() -> UIWindow
}

이 프로토콜은 구글 로그인을 처리하는 객체에서 프로퍼티로 보유하게 된다.

final class GoogleAuthManager {
    weak var presentationContextProvider: GoogleSignInPresentaionContextProviding?
}

GoogleSignInPresentaionContextProviding 프로토콜을 따르는 UIView 객체를 만든다.

class ContextProvidingView: UIView, GoogleSignInPresentaionContextProviding {
    func presentationAnchor() -> UIWindow {
        guard let window = window else {
            fatalError("UIWindow를 찾을 수 없음")
        }
        return window
    }
}

ContextProvidingView를 SwiftUI에서 표시하기 위해 UIViewRepresentable로 감싼다.

struct PresentationContextProviderView: UIViewRepresentable {
    func makeUIView(context: Context) -> ContextProvidingView {
        return ContextProvidingView()
    }

    func updateUIView(_ uiView: ContextProvidingView, context: Context) {}
}

이제 이 뷰를 최상위뷰의 뒤에 삽입하여 UIWindow를 가져올 수 있도록 할 것이다. ZStack을 사용하여 ContentView의 뒤에 배치할 수 있다.

@main
struct DoongsterApp: App {
    var body: some Scene {
        WindowGroup {
            ZStack {
                PresentationContextProviderView()
                ContentView()
            }
        }
    }
}

최상위뷰에 PresentationContextProviderView를 삽입하는것 까지는 완료되었으니 이제 로그인 처리 객체에 GoogleSignInPresentaionContextProviding를 제공해주어야 한다. 이를 위해 클로저를 통해 ContextProvidingView를 전달할 수 있도록 변경할 것이다.

로그인 처리 객체에 ContextProvidingView 전달하기

ContextProvidingView의 이니셜라이저로 클로저를 전달받고 ContextProvidingView 자기 자신을 넘겨주도록 한다.

class ContextProvidingView: UIView, GoogleSignInPresentaionContextProviding {

init(onProviderCreated: (GoogleSignInPresentaionContextProviding) -> Void) {
    super.init(frame: .zero)
    onProviderCreated(self)
}

}

ContextProvidingView를 감싼 PresentationContextProviderView에 클로저를 저장한다.

struct PresentationContextProviderView: UIViewRepresentable {

var onProviderCreated: (GoogleSignInPresentaionContextProviding) -> Void

}

이제 최상위 뷰에서 클로저를 통해 로그인 처리 객체에 (PresentationContextProviding 프로토콜을 준수하는) ContextProvidingView를 전달하면 된다.

@main
struct DoongsterApp: App {
    @StateObject var googleLoginManager = GoogleLoginManager()

    var body: some Scene {
        WindowGroup {
            ZStack {
                PresentationContextProviderView { provider in
                    googleLoginManager.presentationContextProvider = provider
                }
                ContentView()
            }
        }
    }
}

뷰 모디파이어 + 뷰 익스텐션을 사용하여 아래와 같은 문법으로 제작하여 사용하면 더욱 직관적이고 편리하다. ZStack도 없앨 수 있고 어떤 역할을 하는지도 명확하게 보인다.

ContentView()
    .googleLoginManagerPresentationContext { provider in
        googleLoginManager.presentationContextProvider = provider
    }
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
글 보관함