Heads up… this article is old! For an updated version of this article, see Build an iOS App with Secure Authentication in 20 Minutes on the Okta developer blog.

When building mobile apps, authentication is usually the last thing you and your team want to think about. With so many problems to be working on, why spend hours or even days working with your team to implement your authentication system, of all things?

Unfortunately (and embarrassingly, as many of us mobile developers know), teams often ship apps with weak security in the name of speed. In a world where security and privacy matters heavily to the average user, what can we do?

Fortunately, we built Stormpath to solve this problem. Stormpath is a complete solution designed to plug into your backend and mobile apps, and securely manage your users and authentication. Integrating a secure user datastore into your app can now take less than 15 minutes.

Today, we announced support for iOS and Android, and this post will walk you setting up a basic Swift app with user login, with some technical discussion along the way.

Overview

To show what you can do with Stormpath, we’re building Stormpath Notes, a simple note-taking application that allows a person to log in, edit, and save their personal notes on a server. This tutorial is for an iOS app in Swift, but you can also try out our Android tutorial!

Here’s a demo of the app in action:

I’ve already built and hosted the backend at https://stormpathnotes.herokuapp.com, so we will take the perspective of an iOS developer using the Stormpath SDK to build against this backend. If you want to learn how to build this backend, I’ve written a tutorial on building the Stormpath Notes Backend with Node.js.

Stormpath’s backend integrations expose a common API for mobile clients to connect to, which we’ll use the iOS SDK for. This allows your backend developers to protect additional endpoints with Stormpath authentication.

For Stormpath Notes’ backend, I’ve exposed and protected these endpoints:

GET /notes – returns the notes for the authenticated user in the form of a JSON object.

POST /notes – takes a JSON object with the notes and saves it for the authenticated user.

The JSON object takes the form of:

{"notes": "The notes the user saved"} 1 2 { "notes" : "The notes the user saved" }

In case you’re curious, we used the following tools to build the backend for Stormpath Notes:

Express – A Node.js framework that allows us to write the /notes endpoints.

endpoints. Express-Stormpath – Exposes a configurable REST API for our mobile clients within Express.

Stormpath – Allows us to store and authenticate users without having to create our own backend for it.

Heroku – Hosts the code for Stormpath Notes online.

Setting up Our iOS Project

To get started, I’ve built a barebones project with views already created, so we can get to the fun stuff quickly. Start out by downloading from GitHub or cloning it:

git clone https://github.com/stormpath/stormpath-ios-notes-example.git 1 2 git clone https : //github.com/stormpath/stormpath-ios-notes-example.git

Try running the application and clicking around. I’ve put together the login, registration, and notes screens already, with the basic ability to navigate between them. If you’d like to, you can also try browsing through the finished version of the example.

Note: This tutorial uses Xcode 8 / Swift 3. An earlier version will not work.

Install Stormpath

Now that we’ve cloned the app and have it running, the first thing we need to do is add the Stormpath iOS SDK to our iPhone app! We’ll use Cocoapods, a popular dependency manager for iOS projects, to install Stormpath.

To get started with Cocoapods, open up the Terminal and install it:

$ sudo gem install cocoapods 1 2 $ sudo gem install cocoapods

Then, navigate in the command line to the iOS project. We’re going to initialize this project in Cocoapods:

$ pod init 1 2 $ pod init

This creates a Podfile in your current directory, which stores information about libraries that your project depends on. Open the Podfile in your iOS project’s directory, and replace it with the following:

# Uncomment the next line to define a global platform for your project # platform :ios, '9.0' target 'Stormpath Notes' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! # Pods for Stormpath Notes pod 'Stormpath', '~> 2.0' end 1 2 3 4 5 6 7 8 9 10 11 12 # Uncomment the next line to define a global platform for your project # platform :ios, '9.0' target 'Stormpath Notes' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks ! # Pods for Stormpath Notes pod 'Stormpath' , '~> 2.0' end

From the Podfile Cocoapods created, we added one line:

pod 'Stormpath', '~> 2.0' – installs Stormpath 2.0.x. We don’t need to specify the version, but we’re doing this so that any future updates we make are intentional.

After saving the Podfile, go back to the terminal and run pod install . This will now grab Stormpath from GitHub, and install it into your project. Quit out of your Xcode project, and you’ll see that Cocoapods generated a new file, Stormpath Notes.xcworkspace . You’ll open this up instead of Stormpath Notes.xcodeproj , as the workspace contains all of your dependencies in addition to your code.

Now that we have Stormpath installed, open AppDelegate.swift and add the following lines to the file:

Under the import UIKit statement:

import Stormpath 1 2 import Stormpath

In application(application:didFinishLaunchingWithOptions:)

Stormpath.sharedSession.configuration.APIURL = URL(string: "https://stormpathnotes.herokuapp.com")! 1 2 Stormpath . sharedSession . configuration . APIURL = URL ( string : "https://stormpathnotes.herokuapp.com" ) !

This will configure your application to use our API server for Stormpath.

Note: Xcode may not recognize the import Stormpath command after installing it via Cocoapods. You can get rid of the error by pressing ⌘-B to rebuild your project.

Add User Registration

Now that we have the Stormpath SDK embedded in your Xcode project, let’s add it to your RegisterViewController so that users can sign up for an account!

As earlier, we need to add import Stormpath to the top of the file so you can use the Stormpath SDK.

Looking through the file, you’ll notice a function called register: , which will be run whenever someone presses the register button in the app. Let’s start writing some code so that we can register a user.

In register(_:) , we need to create a RegistrationModel , which represents the registration form for the user. Depending on the configuration in your backend, you may require different fields for registration. In this case, we are using the default of email , password , givenName , and surname :

let newUser = RegistrationModel(email: emailTextField.text!, password: passwordTextField.text!) newUser.givenName = firstNameTextField.text! newUser.surname = lastNameTextField.text! 1 2 3 4 let newUser = RegistrationModel ( email : emailTextField . text ! , password : passwordTextField . text ! ) newUser . givenName = firstNameTextField . text ! newUser . surname = lastNameTextField . text !

After we create the RegistrationModel , we can send it to the API server by calling Stormpath’s register method. Stormpath exposes itself as a singleton named sharedSession , so you can easily access it in any view controller without problems. When the registration completes, it calls back on the main thread (so you can do UI work) and sends an account and error parameter, both which are optionals.

In this case, we want to show an error alert if there’s an error, otherwise close the registration screen. Add below your previous code:

// Register the new user Stormpath.sharedSession.register(newUser) { (account, error) -> Void in if let error = error { self.showAlert(withTitle: "Error", message: error.localizedDescription) } else { self.exit() } } 1 2 3 4 5 6 7 8 9 // Register the new user Stormpath . sharedSession . register ( newUser ) { ( account , error ) - > Void in if let error = error { self . showAlert ( withTitle : "Error" , message : error . localizedDescription ) } else { self . exit ( ) } }

Note: showAlert(withTitle:message:) is a helper extension to UIViewController I’ve added to make displaying an UIAlert easier

Try running your app and testing the registration page. You should be able to register a new user!

Add Login Functionality

Now that people can register for your app easily, let’s configure your login screen in LoginViewController !

As always, don’t forget to import Stormpath at the top of the file.

In this file, we have four stubs: login(_:) , loginWithFacebook(_:) , loginWithGoogle(_:) , and resetPassword(_:) . We’re going to add login and reset password functionality for now, and implement Login with Facebook/Google at the end of the tutorial.

To add login functionality, in login(_:) , replace the existing view code with:

Stormpath.sharedSession.login(emailTextField.text!, password: passwordTextField.text!, completionHandler: openNotes) 1 2 Stormpath . sharedSession . login ( emailTextField . text ! , password : passwordTextField . text ! , completionHandler : openNotes )

This will call Stormpath with the email and password provided, and attempt to log the user in. When done, Stormpath calls openNotes(_:error:) .

Speaking of which, in this case the callback is a StormpathSuccessCallback , which means it’s looking for a method of type (Bool, NSError?) -> Void . The Bool represents if the login was successful, while NSError? will be set if there’s an error.

Let’s modify openNotes so it alerts the user on an error, and otherwise opens the login page:

func openNotes(success: Bool, error: NSError?) { if let error = error { showAlert(withTitle: "Error", message: error.localizedDescription) } else { performSegueWithIdentifier("login", sender: self) } } 1 2 3 4 5 6 7 8 9 func openNotes ( success : Bool , error : NSError ? ) { if let error = error { showAlert ( withTitle : "Error" , message : error . localizedDescription ) } else { performSegueWithIdentifier ( "login" , sender : self ) } }

Try this out; you can now log in with the account you just registered!

Note: the iOS Simulator currently has a bug which may prevent you from logging in on your phone. See this Stackoverflow thread for more details. In the meantime, run this example on your physical phone.

In addition, we need to add functionality to resetPassword: to send you a password reset email. Stormpath’s reset password method calls back with a StormpathSuccessCallback just like earlier, so we’ll have it alert the user with the result:

Stormpath.sharedSession.resetPassword(emailTextField.text!) { (success, error) -> Void in if let error = error { self.showAlert(withTitle: "Error", message: error.localizedDescription) } else { self.showAlert(withTitle: "Success", message: "Password reset email sent!") } } 1 2 3 4 5 6 7 8 Stormpath . sharedSession . resetPassword ( emailTextField . text ! ) { ( success , error ) - > Void in if let error = error { self . showAlert ( withTitle : "Error" , message : error . localizedDescription ) } else { self . showAlert ( withTitle : "Success" , message : "Password reset email sent!" ) } }

Retrieve Your Notes from the API

Now that we can register and login to Stormpath Notes, let’s build the core of the app: the note taking view!

When we first open the notes view, we see two things: your notes, and a “Hello [Name]!” message. Since we register our users with a name property, Stormpath exposes a /me endpoint in the backend which allows you to access a user’s profile data. Let’s put code to retrieve the user’s account, and add the user’s name to the helloLabel !

In viewWillAppear(_:)

Stormpath.sharedSession.me { (account, error) -> Void in if let account = account { self.helloLabel.text = "Hello \(account.fullName)!" } } 1 2 3 4 5 6 Stormpath . sharedSession . me { ( account , error ) - > Void in if let account = account { self . helloLabel . text = "Hello \ ( account . fullName ) !" } }

Right below this segment of code, we’ll retrieve our notes by sending an authenticated GET request to /notes .

Stormpath uses token based authentication, which means that after authenticating, your application receives an access token and refresh token. When making an API request, you send the access token along with the request in the form of an Authorization: Bearer [AccessToken] header. The access token expires after some time, and when it expires, you can request a new one with the refresh token.

While this sounds complex, this ultimately makes your backend more secure and scalable.

To retrieve our notes, add this code in viewWillAppear(_:) :

let notesEndpoint = URL(string: "https://stormpathnotes.herokuapp.com/notes")! var request = URLRequest(url: notesEndpoint) request.setValue("Bearer \(Stormpath.sharedSession.accessToken ?? "")", forHTTPHeaderField: "Authorization") let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in guard let data = data, let json = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any], let notes = json["notes"] as? String else { return } DispatchQueue.main.async(execute: { self.notesTextView.text = notes }) }) task.resume() 1 2 3 4 5 6 7 8 9 10 11 12 13 let notesEndpoint = URL ( string : "https://stormpathnotes.herokuapp.com/notes" ) ! var request = URLRequest ( url : notesEndpoint ) request . setValue ( "Bearer \ ( Stormpath . sharedSession . accessToken ? ? "" ) " , forHTTPHeaderField : "Authorization" ) let task = URLSession . shared . dataTask ( with : request , completionHandler : { ( data , response , error ) - > Void in guard let data = data , let json = ( try ? JSONSerialization . jsonObject ( with : data , options : [ ] ) ) as ? [ String : Any ] , let notes = json [ "notes" ] as ? String else { return } DispatchQueue . main . async ( execute : { self . notesTextView . text = notes } ) } ) task . resume ( )

In addition to constructing the HTTP request with the Authorization header, we convert the JSON returned by the server into text, and put it in the notes view.

Note: we wrapped our view code in DispatchQueue.main.async(execute:) , as we can only manipulate UIKit views on the main thread, and NSURLSession calls back on a secondary thread.

Save Your Notes to the API

Let’s also make our app save when we stop editing the text field. This API endpoint requires that we send a POST request to /notes , with JSON of the notes. Since we’re sending JSON to the server, we also need to additionally specify that we’re sending Content-Type: application/json .

In textViewDidEndEditing(_:) , add:

let postBody = ["notes": notesTextView.text] let notesEndpoint = URL(string: "https://stormpathnotes.herokuapp.com/notes")! var request = URLRequest(url: notesEndpoint) request.httpMethod = "POST" request.httpBody = try? JSONSerialization.data(withJSONObject: postBody, options: []) request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("Bearer \(Stormpath.sharedSession.accessToken ?? "")", forHTTPHeaderField: "Authorization") let task = URLSession.shared.dataTask(with: request) task.resume() 1 2 3 4 5 6 7 8 9 10 11 12 let postBody = [ "notes" : notesTextView . text ] let notesEndpoint = URL ( string : "https://stormpathnotes.herokuapp.com/notes" ) ! var request = URLRequest ( url : notesEndpoint ) request . httpMethod = "POST" request . httpBody = try ? JSONSerialization . data ( withJSONObject : postBody , options : [ ] ) request . setValue ( "application/json" , forHTTPHeaderField : "Content-Type" ) request . setValue ( "Bearer \ ( Stormpath . sharedSession . accessToken ? ? "" ) " , forHTTPHeaderField : "Authorization" ) let task = URLSession . shared . dataTask ( with : request ) task . resume ( )

Finally, we’ll make sure to have Stormpath log us out when we click the logout button.

In logout(_:) , add:

Stormpath.sharedSession.logout() 1 2 Stormpath . sharedSession . logout ( )

There we go! If you now run and try out your app again, you’ll find that you can now register users, log in, and save your notes!

Add Facebook and Google Login

With Stormpath, we’ve made it really easy to add social login with Facebook and Google. The Facebook and Google SDKs are very feature-rich, but overkill when you just want to log in. In our iOS SDK, we’ve simplified Facebook and Google Login so integration is just one line of code!

When integrating social login for your own app, you’ll have to register an app on Facebook or Google’s developer website, and set that up. However, since this is for our test app, we already have App IDs you can utilize for sign in.

To integrate social login, Facebook and Google assign us custom URL schemes, so their login page can redirect back to our app. Just like how links with http:// get opened with Safari, we can register a custom URL scheme to do so for Stormpath Notes.

To do so, open the Stormpath Notes project file, click on the Info tab, and scroll down. Under URL Types, click the + button to add a new URL Type.

In the URL Schemes box, add: fb1697895913782798 and com.googleusercontent.apps.120814890096-1dt9ac0f83eng66troavm0a6dt9dgsp4 .

Now, in the AppDelegate , add the following method:

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool { return Stormpath.sharedSession.application(app, open: url, options: options) } 1 2 3 4 func application ( _ app : UIApplication , open url : URL , options : [ UIApplicationOpenURLOptionsKey : Any ] ) - > Bool { return Stormpath . sharedSession . application ( app , open : url , options : options ) }

This function will notify Stormpath when Facebook or Google redirects back into Stormpath Notes, so that Stormpath can complete the social login process.

Finally, in the LoginViewController , insert into loginWithFacebook(_:) :

Stormpath.sharedSession.login(socialProvider: .facebook, completionHandler: openNotes) 1 2 Stormpath . sharedSession . login ( socialProvider : . facebook , completionHandler : openNotes )

And loginWithGoogle: :

Stormpath.sharedSession.login(socialProvider: .google, completionHandler: openNotes) 1 2 Stormpath . sharedSession . login ( socialProvider : . google , completionHandler : openNotes )

Note: Stormpath considers email/password, Facebook, and Google accounts to be completely separate, so notes saved under one account will remain solely in that account.

Try running the app, and you’ll find that you’re now able to login with Facebook and Google!

Congrats! You’re amazing — you finished building your note-taking app! In the process of building it, you’ve learned how to implement registration and login via Stormpath, and use Stormpath’s access token to access a protected API endpoint!

Try the Android SDK – If you (or a friend) is into Android development, try following through the Android tutorial for Stormpath Notes. Since the app would make requests against the same API, you’ll notice that you can save your notes on one device, and open them up on the other!

Build a Backend with Stormpath – Try building this API from scratch! Stormpath Notes’ example backend is just 45 lines of code! Learn how to do it in our Stormpath Notes REST API tutorial, or view code on GitHub. Alternatively, try getting started with express-stormpath or check out our comprehensive documentation!

Stormpath is free to use, and can help your team write a secure, scalable application without worrying about the nitty gritty details of authentication, authorization, and user security. Sign up for an account today!

Talk with Us – We’re proud of the exceptional level of support we provide our community, and would love to hear from you about your project! Please don’t hesitate to contact us at [email protected], file an issue against one of our GitHub projects, or leave a comment below!