시작
타 플랫폼 소셜로그인을 사용할 경우, 심사 지침에 따라 Apple 로그인을 필수적으로 구현을 해줘야 한다고 합니다.
그래서 오늘은 Apple 로그인을 구현하는 방법을 알아보려 합니다.
Capability 추가
Apple 로그인 기능을 사용하기 위해, 가장 먼저 해야 할 일은 Capability에서 Sign in wiht Apple을 추가해줘야 합니다.
이렇게 해주면 초기설정 완료
로그인 버튼 생성
기본적으로 애플에서 제공하는 로그인 버튼 클래스가 존재합니다.
당연히 커스텀으로도 구현할 수 있습니다.
여기서 중요한 게 디자인의 직관성을 위해, 애플이 로그인 버튼 관련 여러 가이드라인 을 만들어 놨습니다.
제공되는 버튼 및 커스텀 버튼을 사용하기 전에, 꼭 HIG를 숙지해서 작업해 주도록 합시다
오늘은 제공되는 버튼 클래스로 구현해 보겠습니다.
레이아웃 코드는 생략했습니다.
// 로그인 기능을 위한 프레임워크
import AuthenticationServices
class LoginViewController: UIViewController {
// 애플에서 기본적으로 제공하는 로그인 버튼 클래스
private let signInButton = ASAuthorizationAppleIDButton()
override func viewDidLoad() {
super.viewDidLoad()
// 버튼이기 때문에 addTarget을 통해 핸들러와 연결해 줍시다.
signInButton.addTarget(self, action: #selector(didTapSignIn), for: .touchUpInside)
}
}
ASAuthorizationController 생성해 주기
버튼은 만들어 줬고, 이제 버튼이 눌리면 실행될 동작들은 구현해 볼 차례입니다.
여기선 ASAuthorizationController를 사용해 줍니다.
로그인 정보 및 로그인 인증창을 요청할 수 있는 컨트롤러입니다.
@objc func didTapSignIn() {
print("Start sign in")
let provider = ASAuthorizationAppleIDProvider()
let requset = provider.createRequest()
// 사용자에게 제공받을 정보를 선택 (이름 및 이메일) -- 아래 이미지 참고
requset.requestedScopes = [.fullName, .email]
let controller = ASAuthorizationController(authorizationRequests: [requset])
// 로그인 정보 관련 대리자 설정
controller.delegate = self
// 인증창을 보여주기 위해 대리자 설정
controller.presentationContextProvider = self
// 요청
controller.performRequests()
}
하지만 현재 정상 동작은 안 되겠죠?
LoginViewContorller를 대리자로 선택해 줬으니, 마저 동작을 구현해줘야 합니다.
마저 코드를 구현해 봅시다.
로그인 인증 화면 요청
extension LoginViewController: ASAuthorizationControllerPresentationContextProviding {
// 인증창을 보여주기 위한 메서드 (인증창을 보여 줄 화면을 설정)
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
self.view.window ?? UIWindow()
}
}
델리게이트 설정 (사용자 정보 처리)
여기서 얻을 수 있는 정보는 크게 5개가 있습니다.
user, fullName, email, identityToken, authorizationCode
이름과 이메일 같은 경우는 개인정보이다 보니,
처음 로그인 하는 경우에만 제공되며 두 번째부터는 제공이 되지 않습니다.
나머지 변수들은 간단히 공식문서를 참고해 살펴보면
user
처음 로그인을 하게 되면, 유저에게 주어지는 고유 식별입니다.
identityToken
사용자에 대한 정보를 앱에 안전히 전달하는 JWT 입니다.
authorizationCode
앱이 서버와 상호 작용하는 데 사용하는 토큰입니다.
extension LoginViewController: ASAuthorizationControllerDelegate {
// 로그인 실패 시
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: any Error) {
print("로그인 실패", error.localizedDescription)
}
// Apple ID 로그인에 성공한 경우, 사용자의 인증 정보를 확인하고 필요한 작업을 수행합니다
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
switch authorization.credential {
case let appleIdCredential as ASAuthorizationAppleIDCredential:
let userIdentifier = appleIdCredential.user
let fullName = appleIdCredential.fullName
let email = appleIdCredential.email
let identityToken = appleIdCredential.identityToken
let authorizationCode = appleIdCredential.authorizationCode
print("Apple ID 로그인에 성공하였습니다.")
print("사용자 ID: \(userIdentifier)")
print("전체 이름: \(fullName?.givenName ?? "") \(fullName?.familyName ?? "")")
print("이메일: \(email ?? "")")
print("Token: \(identityToken!)")
print("authorizationCode: \(authorizationCode!)")
// 여기에 로그인 성공 후 수행할 작업을 추가하세요.
let mainVC = MainViewController()
mainVC.modalPresentationStyle = .fullScreen
present(mainVC, animated: true)
// 암호 기반 인증에 성공한 경우(iCloud), 사용자의 인증 정보를 확인하고 필요한 작업을 수행합니다
case let passwordCredential as ASPasswordCredential:
let userIdentifier = passwordCredential.user
let password = passwordCredential.password
print("암호 기반 인증에 성공하였습니다.")
print("사용자 이름: \(userIdentifier)")
print("비밀번호: \(password)")
// 여기에 로그인 성공 후 수행할 작업을 추가하세요.
let mainVC = MainViewController()
mainVC.modalPresentationStyle = .fullScreen
present(mainVC, animated: true)
default: break
}
}
}
모두 구현하면 이러한 로그인 요청창이 나타나게 될 겁니다.
사용자에게 요청할 정보 (requestedScopes)
좌: 이름 및 이메일, 우: 이메일
자동 로그인
이미 로그인을 해서 사용자 고유식별자(userIdentifier)를 알고 있을 때,
사용가능 합니다.
ASAuthorizationAppleIDProvider를 통해 현재 자격 증명 상태에 따라 분기처리를 해주면 됩니다.
아래 코드 같은 경우,
revoke 및 notFound 시점에, 다시 로그인창을 보여주게 구현했습니다.
let appleIDProvider = ASAuthorizationAppleIDProvider()
appleIDProvider.getCredentialState(forUserID: /*user의 고유 식별자 값(xxxxx.xxxxxxxxxx.xxxx)*/) { (credentialState, error) in
switch credentialState {
case .authorized:
print("해당 ID는 연동되어있습니다.")
case .revoked, .notFound :
print("해당 ID는 연동 실패 및 찾을 수 없습니다")
DispatchQueue.main.async {
self.window?.rootViewController = LoginViewController()
}
default:
break
}
}
AppDelegate 또는 SceneDelegate에서 적당한 곳에 사용해 주면 될 것 같습니다.
이 블로그 를 참고해 보니 SceneDelegate의 sceneDidBecomeActive 시점에 구현해도 좋아 보입니다.
이 시점은 씬이 비활성 상태에서 활성화로 전환되는 시점에 호출됩니다.
만약 계정을 revoke 했을 때, 다시 화면에 들어오는 순간 다시 로그인을 요청할 수 있을 것 같네요.
'UIKit > Swift' 카테고리의 다른 글
[iOS/Swift] Naver 로그인 구현 방법 (2) | 2024.07.10 |
---|---|
[iOS/Swift] Kakao 로그인 구현 방법 (0) | 2024.07.05 |
[iOS/Swift] TextField를 바인딩 시켜보자(MVVM, RxCocoa 원리) (0) | 2023.11.28 |
[iOS/Swift] sizeToFit, sizeThatFits 사용법 (0) | 2023.04.10 |
[iOS/Swift] NavigationController의 ToolBar를 코드로 만들어 봅시다 (0) | 2023.04.10 |