Created
November 3, 2024 09:12
-
-
Save Codelaby/bc623b1df0829cd9b433408a07fc26c7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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