👋 들어가기 전
드디어 우리 서비스에 Authorization 기능이 도입된다.
iOS 개발이면 처음은 무조건 apple login이다.
현재는 swiftUI 기반이지만, uikit에
서 다른 절차가 필요하면 추후 따로 구분하자.
🍎 애플 로그인
1. Signing & Capabilities
다음과 같이 Capability에 Sign in with Apple을 추가한다.
2. Auth Manager 객체 설계
먼저 AuthManager에 필요한 다양한 변수화 함수를 프로토콜로 정의한다.
구글도 함께 테스트할테니 구글 관련된 것도 한꺼번에 정의
protocol Authable {
func buildAppleAuth() // appleAuth를 위한 build
func buildGoogleAuth() // googleAuth를 위한 build
func buildAuth() // 모든 소셜 auth build
func loginWithApple() // 애플 로그인
func loginWithGoogle() // 구글 로그인
}
3 . apple auth 빌드
인증과 관련된 스코프, 델리게이트, context 제공자를 명시한다.
func buildAppleAuth() {
let appleIdProvider = ASAuthorizationAppleIDProvider()
let request: ASAuthorizationAppleIDRequest = appleIdProvider.createRequest()
request.requestedScopes = [.fullName, .email] // auth로 부터 받아올 scope 정의
let auth = ASAuthorizationController(authorizationRequests: [request]) // auth 객체 생성
auth.delegate = self // 델리게이터 지정
auth.presentationContextProvider = self // 애플 로그인 창을 띄울 컨텍스트 제공
self._appleAuth = auth
}
4. delegate 및 PresentationContextProviding 정의
인증 성공과 실패에 대한 동작을 정의하고, 로그인 화면을 앱 최상단에 보여준다.
extension AuthManager: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first ?? .init() // 로그인 창 위치 지정 (최상단)
}
// 성공
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let credential = authorization.credential as? ASAuthorizationAppleIDCredential,
let rawData = credential.identityToken {
let token = String(decoding: rawData, as: UTF8.self)
print("Email: \(credential), name: \(credential.fullName), token: \(token)")
}
}
// 실패
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: any Error) {
print(error.localizedDescription)
}
}
5. 로그인 실행 함수 정의 및 뷰에서 호출
뷰에서 로그인을 호출할 수 있는 함수를 명시해 로그인 기능을 실행한다.
// AuthManager.swift
func loginWithApple() {
_appleAuth?.performRequests()
}
// ContentView.swift
struct ContentView: View {
private var authManager = AuthManager()
var body: some View {
VStack {
Button {
authManager.loginWithApple()
} label: {
Text("Login")
.foregroundStyle(.white)
}
.frame(maxWidth: .infinity, maxHeight: 56)
.background(
RoundedRectangle(cornerRadius: 20)
.foregroundStyle(.blue)
)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding(.horizontal, 20)
}
}
6. 결과 화면
보안을 위해 화면을 좀 짤라지만 토큰과 정보들이 잘 내려오는 것을 확인할 수 있다.
🛜 구글 로그인
1. 패키지 다운로드
애플과 달리 구글은 ThirdParty 이므로 패키지 설치부터 진행한다.
https://github.com/google/GoogleSignIn-iOS
2. client ID 발급 받기
https://developers.google.com/identity/sign-in/ios/start-integrating
구글 개발자 사이트에 들어가서 client ID를 발급 받거나 기존에 있다면 가져오자.
다음과 같이 Bundle ID는 앱의 Bundle ID를 넣어준다.
위 과정이 끝나면 다음과 같이 Client ID를 발급받는데 꼭 복사해 놓자.
만약 깜빡했다면 create 밑에 있는 하얀색 버튼을 누르면 Google Cloud Platform에 접속되는데
거기서 확인할 수 있다.
3. GIDClientID 및 URL 스키마 추가하기
다음과 같이 info.plist에 GIDClientID를 설정한다.
Key: GIDClientID, value는 String 타입으로 발급 받은 키를 넣어준다.
이전 내 기억으로는 clientID를 코드로 넣었던 기억이 있는데 지금은
plist에 등록하면 알아서 읽어오는 듯
이후 발급 받은 clientID값을 역순으로 URL Types의 URL Schemes에 넣어준다.
// Client ID: 1234567890-abcdefg.apps.googleusercontent.com 라면
com.googleusercontent.apps.1234567890-abcdefg // 이렇게 적용시키면 된다.
4. Appdelegate 정의 (UIKit) / openURL 설정 (SwiftUI)
UIKit에서는 Appdelegate에 다음과 같이 정의 해야한다.
// Appdelegate.swift
import Foundation
import UIKit
import GoogleSignIn
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return GIDSignIn.sharedInstance.handle(url) // 구글 로그인 요청이 들어왔을 때 해당 함수를 통하여 로그인 화면이 로드된다.
}
}
swiftUI에는 보다 간편하게 설정할 수 있다.
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
// ...
.onOpenURL { url in
GIDSignIn.sharedInstance.handle(url)
}
}
}
}
5. 로그인 함수 정의
// AuthManager.swift
func loginWithGoogle() {
GIDSignIn.sharedInstance.signIn(withPresenting: _window.rootViewController ?? UIViewController()) { result, error in
print("Result: \(result)")
print("Error: \(error)")
}
}
👍전체 코드
// AuthManager.swift
import SwiftUI
import AuthenticationServices
import GoogleSignIn
protocol Authable {
func buildAppleAuth() // appleAuth를 위한 build
func buildAuth() // 모든 소셜 auth build
func loginWithApple() // 애플 로그인
func loginWithGoogle() // 구글 로그인
}
final class AuthManager: NSObject, Authable {
private var _appleAuth: ASAuthorizationController?
private var _googleConfiguration: GIDConfiguration?
private let _window: UIWindow = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first ?? .init()
override init() {
super.init()
buildAuth()
}
func buildAppleAuth() {
let appleIdProvider = ASAuthorizationAppleIDProvider()
let request: ASAuthorizationAppleIDRequest = appleIdProvider.createRequest()
request.requestedScopes = [.fullName, .email] // auth로 부터 받아올 scope 정의
let auth = ASAuthorizationController(authorizationRequests: [request]) // auth 객체 생성
auth.delegate = self // 델리게이터 지정
auth.presentationContextProvider = self // 애플 로그인 창을 띄울 컨텍스트 제공
self._appleAuth = auth
}
func buildAuth() {
buildAppleAuth()
}
func loginWithApple() {
_appleAuth?.performRequests()
}
func loginWithGoogle() {
GIDSignIn.sharedInstance.signIn(withPresenting: _window.rootViewController ?? UIViewController()) { result, error in
print("Result: \(result)")
print("Error: \(error)")
}
}
}
extension AuthManager: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return _window // 로그인 창 위치 지정 (최상단)
}
// 성공
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let credential = authorization.credential as? ASAuthorizationAppleIDCredential,
let rawData = credential.identityToken {
let token = String(decoding: rawData, as: UTF8.self)
print("Email: \(credential), name: \(credential.fullName), token: \(token)")
}
}
// 실패
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: any Error) {
print(error.localizedDescription)
}
}
😀 소감 및 마무리
서비스가 커질수록 다양한 소셜 로그인을 사용할 것 같다.
앞으로 다양한 소셜 로그인도 계속 이 포스팅에 정리하여 추후 로그인 기능을 구현할 때
시간을 많이 절약해보자.
출처
'iOS > Framework' 카테고리의 다른 글
AVFoundation (4) [AVAudioSession] (3) | 2024.10.22 |
---|---|
AVFoundation (3) [AVPlayerLayer] (1) | 2024.10.22 |
AVFoundation (2) [AVPlayer, AVPlayerItem] (0) | 2024.10.21 |
AVKit (4) | 2024.10.21 |
AVFoundation (1) [AVAsset] (1) | 2024.10.21 |