I am trying to create a custom SignInWithApple
button with firebase using swiftUI
custom button style. in the documentation they are using a ViewController
for handling the request and callback.
I am using MVVM architecture and i want to know if you can even create a custom style SignInWithApple
button like the following picture Custom Button Style
My ButtonView
struct AuthProviderButton: View {
var type: AuthButtonType
var action: () -> Void
private var authTextType: String {
switch type {
case .google:
return "Continue With Google"
case .facebook:
return "Continue With Facebook"
case .apple:
return "Continue With Apple"
}
}
var body: some View {
Button(action: {
HapticManager.instance.impact(style: .medium)
action()
}, label: {
HStack {
Image(type.rawValue)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.padding(.horizontal, 2)
Text("\(authTextType)")
.font(.title2)
.foregroundColor(ColorManager.black)
Spacer()
}
.padding()
.background {
RoundedRectangle(cornerRadius: 12)
.fill(ColorManager.White)
.overlay {
RoundedRectangle(cornerRadius: 16)
.stroke(ColorManager.BluerpleMain, lineWidth: 2)
}
}
})
}
}
and for the default apple signInButton in my ViewModel:
//MARK: Apple provider login
extension AuthViewModel {
func handleAppleLogin(credential: ASAuthorizationAppleIDCredential) {
guard let token = credential.identityToken else {
print("DEBUG: Error apple Auth with firebase")
return
}
guard let tokenString = String(data: token, encoding: .utf8) else {
print("DEBUG: ERROR With Token")
return
}
let firebaseCredential = OAuthProvider.credential(withProviderID: "apple.com", idToken: tokenString, rawNonce: nonce)
Auth.auth().signIn(with: firebaseCredential, completion: { result, error in
if let error = error {
self.handleError(error: error.localizedDescription)
return
}
guard let user = result?.user else { return }
//MARK: Check if user number exist in Firestore
self.checkIfUserExistInDatabase(user: user, provider: .apple)
self.userSession = user
self.saveSignInStatus()
})
}
}
//HELPER METHOD FOR APPLE LOGIN
func randomNonceString(length: Int = 32) -> String {
precondition(length > 0)
let charset: [Character] =
Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
var result = ""
var remainingLength = length
while remainingLength > 0 {
let randoms: [UInt8] = (0 ..< 16).map { _ in
var random: UInt8 = 0
let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
if errorCode != errSecSuccess {
fatalError(
"Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)"
)
}
return random
}
randoms.forEach { random in
if remainingLength == 0 {
return
}
if random < charset.count {
result.append(charset[Int(random)])
remainingLength -= 1
}
}
}
return result
}
func sha256(_ input: String) -> String {
let inputData = Data(input.utf8)
let hashedData = SHA256.hash(data: inputData)
let hashString = hashedData.compactMap {
String(format: "x", $0)
}.joined()
return hashString
}
CodePudding user response:
In your button action, call the following function:
func signInWithApple() {
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
request.nonce = randomNonceString()
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.performRequests()
}
You can put the above function in your viewModel
& call in when pressing the button.
Conform your ViewModel
to ASAuthorisationControllerDelegate
, & add this method to it:
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
switch authorization.credential {
case let appleIDCredential as ASAuthorizationAppleIDCredential:
handleAppleLogin(credential: appleIDCredential)
default:
handle
}
}