Build an iOS Application
Create a simple iOS application using AWS Amplify
Module 3: Add Authentication
In this module, you will use the Amplify CLI and libraries to configure and add authentication to your app
Overview
Using a hosted UI means the application uses the Amazon Cognito web pages for the sign-in and sign-up UI flows. The user of the app is redirected to a web page hosted by Amazon Cognito and redirected back to the app after sign-in.
Amplify also offers a native UI component for authentication flows. You can follow these workshop instructions to learn more.
What you will accomplish
- Create and deploy an authentication service
- Configure your iOS app to include Amazon Cognito hosted UI authentication
Key concepts
Amplify libraries – Using Amplify libraries you can interact with AWS services from a web or mobile application.
Authentication – In software, authentication is the process of verifying and managing the identity of a user using an authentication service or API.
Time to complete
10 minutes
Services used
Implementation
-
Create the authentication service
To create the authentication service, open Terminal and run the following command in your project root directory:
amplify add auth
Select the following options, when prompted:
Do you want to use the default authentication and security configuration? ❯ Default configuration with Social Provider (Federation) How do you want users to be able to sign in? ❯ Username Do you want to configure advanced settings? ❯ No, I am done. What domain name prefix do you want to use? «default value» Enter your redirect signin URI: gettingstarted:// Do you want to add another redirect signin URI? (y/N) N Enter your redirect signout URI: gettingstarted:// Do you want to add another redirect signout URI? (y/N) N Select the social providers you want to configure for your user pool: «Do not select any and just press enter»
Note: Do not forget to type the redirect URIs. They are needed for the redirection for Amazon Cognito Hosted UI to work.
Run the following command to deploy the service:
amplify push
Press Enter (Y) when asked to continue.
-
Add the Amplify Authentication library to the project
To add the Amplify Authentication library to the dependencies of your project, navigate to the General tab of your Target application (Your Project > Targets > General) and select the plus (+) in the Frameworks, Libraries, and Embedded Content section.
Select AWSCognitoAuthPlugin, and choose Add.
You will now see AWSCognitoAuthPlugin as a dependency for your project.
-
Configure the URL Schemes for your app
Navigate to the Info tab of your target application (Your Project > Targets > Info), and select the plus (+) in the URL Types section.
Enter gettingstarted in the URL Schemes field. Leave the default selections in the remaining fields.
-
Configure the Amplify Authentication library at runtime
Navigate back to Xcode, and open the GettingStartedApp.swift file. To configure Amplify Authentication, you will need to:
- Add the import AWSCognitoAuthPlugin statement.
- Create the AWSCognitoAuthPlugin plugin and register it with.
Your code sould look like the following:
import Amplify import AWSCognitoAuthPlugin import SwiftUI @main struct GettingStartedApp: App { init() { do { try Amplify.add(plugin: AWSCognitoAuthPlugin()) try Amplify.configure() print("Initialized Amplify"); } catch { print("Could not initialize Amplify: \(error)") } } var body: some Scene { WindowGroup { NotesView() } } }
-
Create a class to support authentication operations
Create a new Swift file named AuthenticationService.swift with the following content:
import Amplify import AuthenticationServices import AWSCognitoAuthPlugin import SwiftUI @MainActor class AuthenticationService: ObservableObject { @Published var isSignedIn = false func fetchSession() async { do { let result = try await Amplify.Auth.fetchAuthSession() isSignedIn = result.isSignedIn print("Fetch session completed. isSignedIn = \(isSignedIn)") } catch { print("Fetch Session failed with error: \(error)") } } func signIn(presentationAnchor: ASPresentationAnchor) async { do { let result = try await Amplify.Auth.signInWithWebUI( presentationAnchor: presentationAnchor, options: .preferPrivateSession() ) isSignedIn = result.isSignedIn print("Sign In completed. isSignedIn = \(isSignedIn)") } catch { print("Sign In failed with error: \(error)") } } func signOut() async { guard let result = await Amplify.Auth.signOut() as? AWSCognitoSignOutResult else { return } switch result { case .complete, .partial: isSignedIn = false case .failed: break } print("Sign Out completed. isSignedIn = \(isSignedIn)") } }
This class takes care of handling authentication by relying on Amplify's HostedUI capabilities, while also informing whether a user is signed in or not.
-
Update the UI with authentication
Signing in
Create a new Swift file named LandingView.swift with the following content:
import AuthenticationServices import SwiftUI struct LandingView: View { @EnvironmentObject private var authenticationService: AuthenticationService @State private var isLoading = true var body: some View { ZStack { if isLoading { ProgressView() } Group { if authenticationService.isSignedIn { NotesView() } else { Button("Sign in") { Task { await authenticationService.signIn(presentationAnchor: window) } } } } .opacity(isLoading ? 0.5 : 1) .disabled(isLoading) } .task { isLoading = true await authenticationService.fetchSession() if !authenticationService.isSignedIn { await authenticationService.signIn(presentationAnchor: window) } isLoading = false } } private var window: ASPresentationAnchor { if let delegate = UIApplication.shared.connectedScenes.first?.delegate as? UIWindowSceneDelegate, let window = delegate.window as? UIWindow { return window } return ASPresentationAnchor() } }
This view takes care of the following:
- Before the view first appears, it will fetch the current user status using the authenticationService property. This is done in the view's task(priority:_:) method.
- If the user is signed in, it will display the NotesView.
- If the user is not signed in, it will automatically invoke the HostedUI workflow. If the user does not sign in and closes that workflow, a "Sign In" button will be displayed that can be used to start the authentication workflow again.
We've marked the authenticationService variable with a @EnvironmentObject property wrapper annotation, meaning we need to set it using the environmentObject(_:) view modifier on an ancestor view.
Update the GettingStartedApp.swift file body to create this view instead, and to set the AuthenticationService object with the following:
var body: some Scene { WindowGroup { LandingView() .environmentObject(AuthenticationService()) } }
Signing out
Open the NotesView.swift file and replace its contents with the following:
struct NotesView: View { @EnvironmentObject private var authenticationService: AuthenticationService @State var notes: [Note] = [] var body: some View { NavigationStack{ List { if notes.isEmpty { Text("No notes") } ForEach(notes, id: \.id) { note in NoteView(note: note) } } .navigationTitle("Notes") .toolbar { Button("Sign Out") { Task { await authenticationService.signOut() } } } } } }
We've added a Sign Out button in the toolbar that calls authenticationService.signOut(). As this object has already been added when creating the LandingView ancestor, we don't need to do anything else.
-
Build and test
Build and launch the app in the simulator by pressing the ► button in the toolbar. Alternatively, you can also do it by going to Product -> Run, or by pressing Cmd + R.
The iOS simulator will open and the app should prompt you to sign in first.
Once you've finished that process, the Notes view will display with a Sign Out button at the top right. If you choose it, you should land in the Sign in view again.
Landing View
Hosted UI: Sign in
Hosted UI: Sign up
Hosted UI Confirmation
List of Notes
Conclusion
You have now added user authentication to your app with just a few lines of code. In the next module we will add a GraphQL API to your app.