Skip to content

Instantly share code, notes, and snippets.

@Codelaby
Created November 3, 2024 09:12
Show Gist options
  • Save Codelaby/bc623b1df0829cd9b433408a07fc26c7 to your computer and use it in GitHub Desktop.
Save Codelaby/bc623b1df0829cd9b433408a07fc26c7 to your computer and use it in GitHub Desktop.
import SwiftUI
import AuthenticationServices
class AppleAuthViewModel: ObservableObject {
@Published var shouldShowAlert: Bool = false
@Published var alertTitle: String = ""
@Published var alertMessage: String = ""
//get notified when autherization state gets change
init() {
print("👂 init addObserver getAuthorizationState")
NotificationCenter.default.addObserver(self, selector: #selector(getAuthorizationState), name: ASAuthorizationAppleIDProvider.credentialRevokedNotification, object: nil)
}
//handle the authorization result that returned from the authorization request in Sign In with Apple button
func performSignIn(result: Result<ASAuthorization, Error>) {
print("performSignIn", result)
switch result {
case .success(let authorization):
print("✅ authorized")
guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential else {
shouldShowAlert = true
alertTitle = "Error"
alertMessage = "Something went wrong. Please try again later."
return
}
guard let idToken = credential.identityToken
.flatMap({ String(data: $0, encoding: .utf8) })
else {
return
}
print("identityToken:", idToken)
print("authorizedScopes:", credential.authorizedScopes)
print("email:", credential.email ?? "")
print("user:", credential.user)
saveUserData(name: credential.fullName?.givenName, email: credential.email, userId: credential.user)
case .failure(let error):
print("🚫 Prohibited authorization")
shouldShowAlert = true
alertTitle = "Error"
alertMessage = error.localizedDescription
}
}
//store the user information in UserDefaults
func saveUserData(name: String?, email: String?, userId: String?) {
print("💾 saveUserData", name ?? "", email ?? "", userId ?? "")
UserDefaults.standard.setValue(name, forKey: "name")
UserDefaults.standard.setValue(email, forKey: "email")
UserDefaults.standard.setValue(userId, forKey: "userId")
}
//store nil for all user information in USerDefaults
func deleteUserData(){
print("🗑️ delete user data")
UserDefaults.standard.setValue(nil, forKey: "name")
UserDefaults.standard.setValue(nil, forKey: "email")
UserDefaults.standard.setValue(nil, forKey: "userId")
}
//this method gets call when the credential revoked notification has been arised in NotificationCenter
@objc func getAuthorizationState() {
print("🚦 getAuthorizationState")
let provider = ASAuthorizationAppleIDProvider()
if let userId = UserDefaults.standard.value(forKey: "userId") as? String {
print("userId", userId) // need for sub consequents validations
provider.getCredentialState(forUserID: userId) { [self] (state, error) in
switch state {
case .authorized:
// Credential are still valid
print("👍 Credential are still valid")
break
case .revoked:
//Credential is revoked. It is similar to Logout. Show login screen.
print("✋ credential revoked")
//self.deleteUserData()
break
case .notFound:
//Credential was not found. Show login screen.
print("🤷🏼 Credential was not found. Show login screen.")
self.deleteUserData()
break
case .transferred:
//The app is transfeered from one development team to another development team. You need to login again so show login screen.
self.deleteUserData()
break
default:
break
}
}
} else {
print("⚠️ no found keychain data")
}
}
}
//https://medium.com/@daljeetseera9668/sign-in-with-apple-using-swiftui-c2cf57943208
//https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple
struct SignInAppleDemoView: View {
@ObservedObject var authenticationViewModel: AppleAuthViewModel = AppleAuthViewModel()
var body: some View {
VStack {
Button("test signi") {
AppleSignIn.sharedInstance.appleSignIn()
}
//44 19pt or 56 24pt
SignInWithAppleButton(.signIn) { request in
request.requestedScopes = [.fullName, .email]
} onCompletion: { result in
authenticationViewModel.performSignIn(result: result)
}
.signInWithAppleButtonStyle(.whiteOutline)
.frame(height: 44)
.padding(.horizontal)
// SignInWithGoogleButton(style: .whiteOutline)
// .padding(.horizontal)
Button("check restorePreviousSignIn") {
authenticationViewModel.getAuthorizationState()
}
}
.onAppear {
authenticationViewModel.getAuthorizationState()
}
.alert(isPresented: $authenticationViewModel.shouldShowAlert, content: {
Alert(title: Text(authenticationViewModel.alertTitle), message: Text(authenticationViewModel.alertMessage), dismissButton: .default(Text("OK")))
})
}
}
#Preview {
SignInAppleDemoView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment