Sign In with Apple Tutorial
Introduction
Sign In with Apple enables users to sign in to Apps using their Apple ID.
This feature is available on iOS 13 and later, and Parse 3.5 and later.
Prerequisites
To begin with this tutorial, you will need:
- An app created at Back4App.
- See the Create New App tutorial to learn how to create an app at Back4App.
- Set up a Subdomain for your Back4app app
- See Activating your Web Hosting and Live Query to learn how to create an subdomain in Back4App.
- An Apple Developer account.
Step 1 - Create a New Back4App App
First of all, it’s necessary to make sure that you have an existing app created at Back4App. However, if you are a new user, you can check this tutorial to learn how to create one.
Step 2 - Add the Sign In with Apple capability to your XCode project
In your XCode Project, click on the Target (1) and go to the Signing & Capatbilities tab (2).
Click the + Capability
button (3) and add the Sign In with Apple
capability (4).
While there, choose your Bundle Identifier
(5) and hold that information because we will need it later.
Step 3 - Create a new Service ID
Log into your Apple Developer account and go to the Identifiers
section. Check if your created Bundle Identifier
is there
Click the Bundle Identifier
and scroll down. Check if the Sign In with Apple
is selected
Click Edit
and make sure the Enable as a primary App ID
is selected
If everything is right, save and exit.
Step 4 - Set up Parse Auth for Apple
Go to Back4App website, log in and then find your app. After that, click on Server Settings
and search for the Apple Login
block and select Settings
.
The Apple Login
section looks like this:
Now, you just need to paste your Bundle ID
in the field below and click on the button to save.
In case you face any trouble while integrating Apple Login, please contact our team via chat!
Step 5 - Option 1 - Download our Template
There is some coding involved in making Sign in With Apple to work, so we created this template that you can download, change the Bundle Identifier
, the App Id
and Client Key
.
The code is fully documented so it is a good starting point.
If you prefer to read through this doc, please go on to the next step.
Step 6 - Option 2 - Manually write code
Inside your view, add the AuthenticationServices framework and create the AuthDelegate that will handle the PFUserAuthenticationDelegate:
1
2
3
4
5
6
7
8
9
10
11
import AuthenticationServices
class AuthDelegate:NSObject, PFUserAuthenticationDelegate {
func restoreAuthentication(withAuthData authData: [String : String]?) -> Bool {
return true
}
func restoreAuthenticationWithAuthData(authData: [String : String]?) -> Bool {
return true
}
}
Step 7 - Implement your Delegates for the ViewController
Implement the ASAuthorizationControllerDelegate and ASAuthorizationControllerPresentationContextProviding for the ViewController:
1
class ViewController: UIViewController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding
Step 8 - Add the Sign In with Apple button
The ViewDidAppear is a good place for it. If you choose other place, remember to call it just once:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Sign In with Apple button
let signInWithAppleButton = ASAuthorizationAppleIDButton()
// set this so the button will use auto layout constraint
signInWithAppleButton.translatesAutoresizingMaskIntoConstraints = false
// add the button to the view controller root view
self.view.addSubview(signInWithAppleButton)
// set constraint
NSLayoutConstraint.activate([
signInWithAppleButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 50.0),
signInWithAppleButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -50.0),
signInWithAppleButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -70.0),
signInWithAppleButton.heightAnchor.constraint(equalToConstant: 50.0)
])
// the function that will be executed when user tap the button
signInWithAppleButton.addTarget(self, action: #selector(appleSignInTapped), for: .touchUpInside)
}
The appleSignInTapped in the last line must also be defined inside the ViewController class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// This is the function that will be executed when user taps the button
@objc func appleSignInTapped() {
let provider = ASAuthorizationAppleIDProvider()
let request = provider.createRequest()
// request full name and email from the user's Apple ID
request.requestedScopes = [.fullName, .email]
// pass the request to the initializer of the controller
let authController = ASAuthorizationController(authorizationRequests: [request])
// similar to delegate, this will ask the view controller
// which window to present the ASAuthorizationController
authController.presentationContextProvider = self
// delegate functions will be called when user data is
// successfully retrieved or error occured
authController.delegate = self
// show the Sign-in with Apple dialog
authController.performRequests()
}
Step 9 - The presentationContextProvider
The presentationContextProvider (ASAuthorizationControllerPresentationContextProviding) will ask for which window should display the Authorization dialog. As we are going to display it in the same window, we must return self.view.window:
1
2
3
4
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
// return the current view window
return self.view.window!
}
Step 10 - Handling the delegate ASAuthorizationControllerDelegate
There are a few options that we must handle when the delegate is called, so let’s add some code to handle those options distinctly:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
print("authorization error")
guard let error = error as? ASAuthorizationError else {
return
}
switch error.code {
case .canceled:
// user press "cancel" during the login prompt
print("Canceled")
case .unknown:
// user didn't login their Apple ID on the device
print("Unknown")
case .invalidResponse:
// invalid response received from the login
print("Invalid Respone")
case .notHandled:
// authorization request not handled, maybe internet failure during login
print("Not handled")
case .failed:
// authorization failed
print("Failed")
@unknown default:
print("Default")
}
}
Step 11 - Handling the delegate for didCompleteWithAuthorization
When we successfuly authenticate, we can retrieve the authorized information:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
// unique ID for each user, this uniqueID will always be returned
let userID = appleIDCredential.user
print("UserID: " + userID)
// if needed, save it to user defaults by uncommenting the line below
//UserDefaults.standard.set(appleIDCredential.user, forKey: "userID")
// optional, might be nil
let email = appleIDCredential.email
print("Email: " + (email ?? "no email") )
// optional, might be nil
let givenName = appleIDCredential.fullName?.givenName
print("Given Name: " + (givenName ?? "no given name") )
// optional, might be nil
let familyName = appleIDCredential.fullName?.familyName
print("Family Name: " + (familyName ?? "no family name") )
// optional, might be nil
let nickName = appleIDCredential.fullName?.nickname
print("Nick Name: " + (nickName ?? "no nick name") )
/*
useful for server side, the app can send identityToken and authorizationCode
to the server for verification purpose
*/
var identityToken : String?
if let token = appleIDCredential.identityToken {
identityToken = String(bytes: token, encoding: .utf8)
print("Identity Token: " + (identityToken ?? "no identity token"))
}
var authorizationCode : String?
if let code = appleIDCredential.authorizationCode {
authorizationCode = String(bytes: code, encoding: .utf8)
print("Authorization Code: " + (authorizationCode ?? "no auth code") )
}
// do what you want with the data here
}
}
That’s the place where we can also add code for logging in Parse. So right after the do what you want with the data here
comment, let’s add:
1
2
3
4
5
6
7
8
9
10
11
PFUser.logInWithAuthType(inBackground: "apple", authData: ["token": String(identityToken!), "id": userID]).continueWith { task -> Any? in
if ((task.error) != nil){
//DispatchQueue.main.async {
print("Could not login.\nPlease try again.")
print("Error with parse login after SIWA: \(task.error!.localizedDescription)")
//}
return task
}
print("Successfuly signed in with Apple")
return nil
}
And of course add the Parse framework:
1
import Parse