SwiftUI — Building a Login View with JSON Web Token Authentication (JWT)

Kathryn Booth
6 min readNov 6, 2021

--

While SwiftUI is still in its infancy, each update has been exciting and releases new features that is making it easier and simpler to build iOS apps. In this article, I’ll be going over basic features and modifiers for a login view, as well as the inputs needed in that view. I’ll also be covering how to navigate programmatically in SwiftUI, and most importantly, how to implement JSON Web Tokens (JWTs) in client-side apps for authenticating users. A basic understanding of the Swift language from the reader is assumed for this article.

While I think many iOS apps today still utilise features in UIKit, this article will be purely SwiftUI. So to begin, create a new project in Xcode and be sure to select SwiftUI for the User Interface. From there, we can navigate to the ContentView— this is our first view where we will build our login page.

No worries if you are working with an existing project that uses UIKit, simply integrate SwiftUI with UIHostingController , which is a UIKit view controller that manages SwiftUI Views. Check out this article by maxcodes that explains further.

Let’s start with a basic view that allows the user to enter their username and password. Both of these inputs will use SwiftUI’s TextField which is essentially UITextField's counterpart, except it relies on binding to a state.

When using TextField you should pass in placeholder text, as well as the state value it’s going to bind to. A handy SwiftUI feature is assigning a .textContentType(._) which sets the TextField’s content type, offering input suggestions to the user. Apple’s documentation discusses more useful features like setting keyboard types, or disabling autocorrection. The appearance of TextField can easily be customised by using TextFieldStyle — to edit both at once, simply place the modifier outside the parent VStack.

We also need to wrap our view in a NavigationView — this is because we need to create a NavigationLink to allow us to eventually navigate programmatically to a new view on a successful login, or throw an error if the login is unsuccessful.

From the code above, we get this very basic login view:

While this is the bread and butter of inputs we need in this view, it isn’t the most attractive experience for the user. Let’s add some SwiftUI modifiers to get this into a better State :P

There are plenty of articles and documentation out there covering SwiftUI modifiers if you aren’t sure on them. After adding a few, our view currently looks like this:

Now to the fun stuff — ultimately, we only want the user to be able to navigate to a new view on a successful login. To be able to programmatically navigate in SwiftUI, first we need to declare a @State property in our struct and initialise it to false.

Then we can create a Navigation Link and add it to our body view which allows us to navigate to the HomeView()when the login has been authenticated, ie: $isAuthenticated = true.
In the mean time, the NavigationLink will sit in the background of the view, which is why we use EmptyView(). This way, we can programmatically navigate to the new view once the user has been authenticated.

But how do we check if the user’s details are authenticated or not?

Depending on which server-side authentication service you choose will depend on how to write your Webservice and login function (See below). Each option will have different client-side code that will need to be written for your Webservice. User authentication will be verified with JSON Web Tokens (JWTs), which are a popular way for securing APIs. JWTs are an open standard that allows for a self-contained way to securely send and receive information between parties as a JSON object. The information can be trusted because it is digitally signed.

Generate JWT Token and Verify in Plain Java — By Deepak Mishra

The above diagram visually explains the steps involved with implementing JWT to secure your app, which essentially are:

  1. The user/client will send a login request with their username and password through to the server
  2. The server receives and verifies the data — if it is authenticated, the server will then create and return a signed JSON Web Token to the client
  3. The client receives and stores the JWT — from then onwards, every request made by the user/client to the server will include the JWT for verification
  4. When the server receives the request, it will check the signed JWT, and send a response — either successfully sending the requested data if the JWT is verified/authenticated, or throwing an unauthenticated error.

Okay, so what options are out there for authentication on the server-side?

There are a few options out there when thinking about user authentication for your SwiftUI project:

  1. Google’s Firebase Authentication SDK, which has a library built especially for SwiftUI called FirebaseUI,
  2. Auth0.swift is another authentication SDK for iOS, with a specific library for SwiftUI, or
  3. Building your own authentication web service using JSON Web Tokens (JWTs) — that includes both client-side, and server-side code.

Firebase and Auth0 are secure and easy to implement, with loads of how-to articles for using them in your project — they essentially handle the creation, signing and sending of JWTs for you. They also allow for account creation, and easy sign-in for social accounts like Google, Facebook, Twitter etc. — but of course, they also come at a cost being a third party product. They are also the less risky option if a developer is relatively new to authentication, due to how secure and reliable the server-side code is.

The third option won’t cost you a dime, but will take a little more effort, code and risk on your part — you will have to write your own server-side code that receives the username and password data, verifies it, and returns a JWT. From then onwards, anytime a request is received, it will have to read the authorization header which will include the verified JWT, and send the requested data back to the client once verified. This option is also useful for using existing data (eg: user details in an established MySQL DB).

For this project, we will add a little more depth and call to the LoginViewModel when the user submits their username and password; we can call to the login() function through this class. We will also need to include the LoginViewModel in our ContentView as a StateObject like so:

Next, we will need to create a new file called Authentication.swift where we can house our Webservice , LoginViewModel and a few different structs for different cases when the user is logging in. We will also be storing the received JWT into user defaults in the case of .success here, with:

Two parameters are used in Webservice.login() to verify the user: username: String and password: String. The code that creates the JSON object and reads the response will be different depending on which authentication option you choose.

We can also include an .alert popup in the body of the view when the login fails — it will be initialised to false, and it will only appear if the login is not successful, throwing an error ie:$loginVM.loginAlert = true.

Where to go from here?

We’ve touched on the basic features and modifiers of a login page, JSON Web Tokens and the options a developer can use for server-side authentications. You can also consider using Apple’s inbuilt biometric authentications (Fingerprint and Face ID) to add further security to your app. Next steps would be deciding the best authentication option going forward depending on your time and budget, as well as your project’s needs and constraints.

Thanks for reading! I’m hoping to write more in the near future — Feel free to leave a comment, follow or show your support with a cuppa’ coffee! :)

--

--