Skip to content

Tutorial: Example User Auth App using Swift & LoopBack iOS SDK

In this tutorial we will create an iOS Swift app that uses the LoopBack iOS SDK to authenticate and manage a user against the LoopBack API. You’ll be able to register/login/logout and change your email address. This will give you a good starting point and an understanding on how to authenticate users in Swift with a LoopBack backend.

Key Features

  • User Registration
  • User Authentication (Login/Logout)
  • Display User Details
  • Edit User Details
  • Supports iOS9 and Swift

Source Code

Available on GitHub: https://github.com/kgoedecke/loopback-swift-user-example

Screenshots

screenshot-2@2xscreenshot-1@2xscreenshot-3@2x screenshot-4@2x

Prerequisites

Getting Started

So you’ve got your Loopback Backend up and running and it is accessible through a public URL. In this tutorial we will use http://api.backend.com:3000/api  as a dummy URL for our Backend. You need to replace this with your actual Backend URL.

Create a new XCode Project (Tabbed Application) and make the LoopBack iOS SDK available in Swift (See: Using LoopBack iOS SDK with Cocoapods in Swift).

start-swift-loopback-auth

Delete the files FirstViewController.swift and SecondViewController.swift and also both View Controllers from the Storyboard. Your Storyboard should only contain one Controller now and that’s the Tab Bar Controller.

Afterwards drag three new View Controllers onto the Storyboard and connect them to the Tab Bar Controller by holding CTRL + Clicking and dragging from the Tab Bar Controller to each of the three View Controllers. Select “view controllers” as Relationship Segue.

storyboard-swift-loopback-auth

UI Elements

Now that we have all our View Controllers, we are going to add the UI elements like buttons and text input fields.

Drag the following elements on the top View Controller:

  • 3x Text Fields (Email, Password, Repeat Password for the Registration)
  • 1x Button (Register Button)

On the middle View Controller:

  • 2x Text Fields (Email, Password for the Login)
  • 1x Button (Login Button)

On the bottom View Controller:

  • 7x Labels (User Account Overview, AccessToken Headline, AccessToken, User ID Headline, User ID, Email Headline, Email
  • 2x Buttons (Change Email Button, Logout Button)

start-loopback-swift-ui-elements

View Controllers

Now that we have all our UI Elements in place, we going to create the View Controllers that are going to control them.

Open “File” -> “New” -> “File…” and select “Cocoa Touch Class” from “iOS” -> “Source”. When you press “Next” enter “RegisterViewController” as the Class name and as “Subclass of” select “UIViewController”.

register-view-controller

Create two more View Controllers by repeating those steps and name them “LoginViewController” and “AccountViewController”.

Connect each View Controller in the Storyboard with our ViewController classes that we just created. In the Identity Inspector under “Custom Class” tell XCode to use the custom classes. Do this for each of the 3 View Controllers: RegisterViewController, LoginViewController and AccountViewController.

swift-loopback-set-classes

IBOutlets and IBActions

In order to control the Labels and Buttons we need IBOutlets and IBActions in the appropriate View Controllers.

Open the RegisterViewController.swift and use the Assistant Editor to display both the Storyboard and the code of the View Controller.

Select the Email Field and hold CTRL and drag the Email Field onto the Storyboard at the first line of the RegisterViewController class. A window will pop up and let you create an Outlet for the TextField.

swift-loopback-outlets

Repeat those steps for the 2 password fields and name the Outlets “PasswordTextField” and “RepeatPasswordTextField”.

Afterwards select the Register Button from the Storyboard and CTRL + Drag it to the RegisterViewController as well. This time select Action as the Connection type so XCode will create an IBAction instead of an IBOutlet.

loopback-register-button

Afterwards your RegisterViewController.swift  should have the following Outlets and Actions:

@IBAction func RegisterButton(sender: UIButton) {
}
@IBOutlet weak var RepeatPasswordTextField: UITextField!
@IBOutlet weak var PasswordTextField: UITextField!
@IBOutlet weak var EmailTextField: UITextField!

Now go ahead and create the appropriate IBOutlets and IBActions in the LoginViewController.swift :

@IBAction func LoginButton(sender: UIButton) {
}
@IBOutlet weak var PasswordTextField: UITextField!
@IBOutlet weak var EmailTextField: UITextField!

For the AccountViewController you only need to create Outlets for the Labels that will actually change its value and of course IBActions for the two buttons on the bottom.

loopback-swift-label-outlets

Afterwards you should have the following IBOutlets and IBActions in your AccountViewController:

@IBAction func LogoutButton(sender: UIButton) {
}
@IBAction func ChangeEmailButton(sender: UIButton) {
}
@IBOutlet weak var EmailLabel: UILabel!
@IBOutlet weak var UserIDLabel: UILabel!
@IBOutlet weak var AccessTokenLabel: UILabel!

LoopBack API Connection

Now that we have our UI Elements in plaace, let’s look into how to connect the app to our LoopBack Backend using the SDK.

Open your AppDelegate.swift and add the following line as a local Variable in the AppDelegate, for example right after the declaration of var window: UIWindow?:

var adapter:LBRESTAdapter = LBRESTAdapter(URL: NSURL(string: "http://api.backend.com:3000/api/"))

The LBRESTAdapter gives you access to your LoopBack REST API and now that it’s in the AppDelegate you can access it pretty much from everywhere in your code.

Before we can access our custom LoopBack “Client” model, we need to let Swift know that there actually is such a model and that it derives from the LBUserModel.

For this we are going to create a new new Class named ClientRepository.

Select “File” -> “New” -> “File…” and select Swift File and name it ClientRepository.swift.

Add the following code:

class ClientRepository: LBUserRepository {
    override init!(className name: String!) {
        super.init(className: "Clients")
    }
    override init() {
        super.init(className: "Clients")
    }
}

class Client: LBUser {
    
}

Next we will create a new Class called BackendUtilities.swift, that will provide access to our Client model which is a subclass of the LoopBack User model.

Select “File” -> “New” -> “File…” -> “Swift File” and name it BackendUtilities.swift.

Insert the following code:

class BackendUtilities  {
    let appDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate!)
    let DEFAULTS_CURRENT_USER_ID_KEY: String = "LBUserRepositoryCurrentUserId"
    var adapter: LBRESTAdapter
    var clientRepo: ClientRepository
    
    static let sharedInstance = BackendUtilities()
    
    init() {
        adapter = appDelegate.adapter as LBRESTAdapter!
        clientRepo = adapter.repositoryWithClass(ClientRepository) as! ClientRepository
    }
}

The code follows the singleton pattern to enable you to make static calls and always access the same LBRESTAdapter as well as the same ClientRepository. Later you’ll be able to simply call BackendUtilities.sharedInstance.clientRepo to access your ClientRepository and perform actions.

The DEFAULT_CURRENT_USER_ID_KEY constant holds the key that the LoopBack SDK internally uses to store the User ID in the NSUserDefaults.

RegisterViewController

Now that we have everything in place, let’s can put the pieces together.

Let’s start with the RegisterViewController.

Open the RegisterViewController and add the following code into the RegisterButton IBAction:

if (PasswordTextField.text != RepeatPasswordTextField.text) {
    let alertController = UIAlertController(title: "Password", message:
        "The passwords don't match", preferredStyle: UIAlertControllerStyle.Alert)
    alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default,handler: nil))
    self.presentViewController(alertController, animated: true, completion: nil)
}
else    {
    let user:Client = BackendUtilities.sharedInstance.clientRepo.createUserWithEmail(EmailTextField.text!, password: PasswordTextField.text!) as! Client

    user.saveWithSuccess({ () -> Void in
        NSLog("Successfully registered new user.")
                
        // Display registration confirmation
        let alertController = UIAlertController(title: "Registration", message:
            "New user successfully registered", preferredStyle: UIAlertControllerStyle.Alert)
        alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default,handler: nil))
        self.presentViewController(alertController, animated: true, completion: nil)
    }) { (error: NSError!) -> Void in
        NSLog("Error")
                    
        // Display error alert for registration
        let alertController = UIAlertController(title: "Registration", message:
            "Error creating new user", preferredStyle: UIAlertControllerStyle.Alert)
                    
        alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default,handler: nil))
        self.presentViewController(alertController, animated: true, completion: nil)
    }
}

Line 1-6: Simply checks if the passwords in the input fields match and in case they don’t, fires an UIAlertController.

Line 8: Get’s the ClientRepository from the BackendUtilities that we created earlier and calls the createUserWithEmail method of it.

Line 10-18: Calls saveWithSuccess and handles what happens in case of the successful creation of the user. We simply going to display a UIAlertController here to inform the user that everything worked and he’s all set.

Line 19-27: Handles the behavior in case an error occurred and displays and error message in an UIAlertController accordingly.

Allowing HTTP Connections

The only thing missing for our app to have it’s first feature ready is that by default XCode doesn’t allow insecure HTTP connections.

Open your Info.plist file, right click and select “Add Row”. Type in “App Transport Security Settings” as key and afterwards create a sub-entry with the key “Allow Arbitrary Loads” that has the value “YES”.

Your plist file should look like this:

loopback-ios-allow-connectionsCongratulations: If you run your app now, you should be able to register a new user with your existing LoopBack API.

LoginViewController

Open your LoginViewController and add the following code to your LoginButton IBAction:

@IBAction func LoginButton(sender: UIButton) {
    BackendUtilities.sharedInstance.clientRepo.userByLoginWithEmail(EmailTextField.text, password: PasswordTextField.text, success: { (LBUser client) -> Void in
            NSLog("Successfully logged in.");
        
            // Display login confirmation
            let alertController = UIAlertController(title: "Login", message:
                "Successfully logged in", preferredStyle: UIAlertControllerStyle.Alert)
            alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default,handler: nil))
            self.presentViewController(alertController, animated: true, completion: nil)
        }) { (error: NSError!) -> Void in
            NSLog("Error logging in.")
            
            // Display error alert
            let alertController = UIAlertController(title: "Login", message:
                "Login failed", preferredStyle: UIAlertControllerStyle.Alert)
            alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default,handler: nil))
            self.presentViewController(alertController, animated: true, completion: nil)
    }
}

Line 2: Simply calls userByLoginWithEmail on our clientRepo to login the user by the email and password provided.

Line 5-9: Displays the login confirmation in the case the login succeeds.

Line 13-17: Displays an UIAlertController error message in the case the login fails.

AccountViewController

First we are going to fill the labels in the AccountViewController with data.

Add the following code to your AccountViewController.swift:

var currentUser: Client
    
required init(coder aDecoder: NSCoder) {
    currentUser = Client()
    super.init(coder: aDecoder)!
}

Line 1: Creates a local variable named currentUser that holds the currently logged in User.

Line 3-6: Adds a designated initializer and actually initializes the currentUser object.

Next add a function that assigns the currentUsers details to the IBOutlets we created earlier. We will call this function later when changes are being made.

func loadUserInformation()  {
    AccessTokenLabel.text = BackendUtilities.sharedInstance.adapter.accessToken
    UserIDLabel.text = currentUser._id as? String
    EmailLabel.text = currentUser.email  
}

Now override the viewDidAppear function so when the view appears we can load the users details from the SDK into the currentUser object.

override func viewDidAppear(animated: Bool) {
    BackendUtilities.sharedInstance.clientRepo.findCurrentUserWithSuccess({ (client) -> Void in
        NSLog("Found user")
        if let _ = client    {
            self.currentUser = client as! Client
            self.loadUserInformation()
        }
    }) { (error: NSError!) -> Void in
        NSLog("Error fetching current user")
    }
}

findCurrentUserWithSuccess internally looks up if the User ID is set in the NSUserDefaults (key: LBUserRepositoryCurrentUserId) and if it’s set returns success. In this case we are going to set the currentUser to the the returned client and also call loadUserInformation to load the data into the UI Labels.

All that’s left is adding functionality to the Logout button and to the Change Email button.

@IBAction func LogoutButton(sender: UIButton) {
    BackendUtilities.sharedInstance.clientRepo.logoutWithSuccess({ () -> Void in
        // Reset local Client class object
        NSLog("Successfully logged out")
            
        // Display logout confirmation
        let alertController = UIAlertController(title: "Logout", message:
            "Successfully logged out", preferredStyle: UIAlertControllerStyle.Alert)
        alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default,handler: nil))
            
        self.presentViewController(alertController, animated: true, completion: nil)
            
        self.currentUser = Client()
        self.loadUserInformation()
    }) { (error: NSError!) -> Void in
        NSLog("Error logging out")
    }
}

Line 2: Calls the logoutWithSuccess function on the ClientRepository

Line 13: Sets the currentUser object to a new empty Client object

Line 14: Calls loadUserInformation to update the Labels through its IBOutlets

@IBAction func ChangeEmailButton(sender: UIButton) {
    let alertController = UIAlertController(title: "Email?", message: "Please enter your email", preferredStyle: .Alert)
    let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (_) in }
        alertController.addTextFieldWithConfigurationHandler { (textField) in
            textField.placeholder = title
        }
    let saveAction = UIAlertAction(title: "Confirm", style: .Default) { (_) in
        if let field = alertController.textFields![0] as? UITextField {
            // store your data
            self.currentUser.email = field.text
            self.currentUser.saveWithSuccess({ () -> Void in
                NSLog("sucessfully saved")
                }, failure: { (error: NSError!) -> Void in
                    NSLog("error saving")
                })
            self.loadUserInformation()
        }
    }
    alertController.addAction(cancelAction)
    alertController.addAction(saveAction)
    self.presentViewController(alertController, animated: true, completion: nil)
}

Line 10: Set the email of the currentUser object to the one that was entered in the UIAlertController

Line 11: Call saveWithSuccess on the currentUser object to execute the saving of the email address

After adding those two Actions you should be able to logout as well as change the email address of the currently logged in user.

Conclusion

That’s it! 😉 You now have an app that provides basic user management features.

This tutorial should give you a good starting point to develop your own app based on a LoopBack Backend. You should be able to easily extend it and take it further from here.

You can find the complete source code of this tutorial on GitHub.

Published inProgramming

22 Comments

  1. Logiq

    Great tutorial! Can you provide some code to be able to use remote methods and query relations?
    Thanks

    • kgoedecke

      @Logiq: Thanks 😉 I’m currently working on a simple CRUD tutorial in Swift that I’ll release next week. I might do something with relations afterwards, stay tuned! Just follow me on twitter @kgoedecke

    • that he had promised Jane and the girls that they wouldn’t have to face that while they were still alive (and I think El1reoa&#82n7;s still in her twenties).

  2. Steve

    This is a great tutorial, thank you very much for sharing your knowledge – exactly what I was looking for. I had one question regarding the userByLoginWithEmail – I am using the “realm” in addition to the username and password fields for logging in. It works with the angular app and for my REST/Alamofire authentication, but was wondering how to use it with the SDK. Thank you!

    • Steve

      Solved it – thought I would share. You can pass the realm with the username/email field as long as you specify the realmDelimiter in the LoopBack model config – example “:”

      Thanks again for the tutorial!

      • kgoedecke

        Steve, thanks for your feedback. I’m also currently working with the LoopBack SDK and Realm, how is your experience so far? I’m thinking about publishing an other tutorial, but I’m still facing a lot of problems.

  3. Steve

    Hey Kevin,
    Sorry for the delayed reply. Glad to hear you are thinking about another tutorial – I have enjoyed the two already published. I too was facing some problems – and the security around the models was an issue regarding roles. On the server side we have opted to create remote operation hooks in order to better control access. I am now shifting efforts toward Alamofire and PromiseKit to write and APIController and get the data that way (but still using Loopback SDK for authentication to return the token ).

    • kgoedecke

      Yeah I also looked in to Alamofire a few weeks ago. Stay tuned for more stuff 😉 So far I haven’t found something that can’t be done with the SDK though I’m sure I’ll run into problem when creating the relations tutorial!

  4. Eric

    Hi Kevin,
    Thanks for the great tutorials! They have helped me greatly in getting our project up and running. Today I discovered that LoopBack iOS SDK stores the user’s access token in NSUserDefaults as well. This is a showstopper for us since it renders our LoopBack web service insecure. I guess I will be rewriting the LBRestAdapter. 🙁

      • Eric

        Hey Kevin, Parse uses keychain, FaceBook uses NSUserDefaults. The app I’m working on needs a higher level of security as in banking or healthcare apps. I agree that for some apps storing passwords or session tokens in a plist *may* be ok, but I think it’s generally a bad idea.

  5. Eric

    Tools like iExplorer can browse the iPhone file system and look at the program’s documents directory, etc.. Anything in plain text can be viewed. Keychain is relatively secure, except on phones that have been jailbroken.

  6. Seán

    Hey Kevin,

    I have the pod and the framework installed but I can’t see to import “LoopBack”.

    In AppDelegate, LBRESTAdapter is unidentified.

    Any thoughts?

    Thanks for the great looking tutorial!

    • Seán

      My LoopBack folder is there with all the files but is located at /Users/Sean/Documents/xcodeApps/loginstrongloopDemo/Pods/LoopBack/LoopBack

      Any idea why Swift cant read it ?

  7. AA

    Hi Kevin
    i am sorry for German Team in EU , but you still have a great team.

    i need your help in my first App, i need the perfect code for sign up-sign in-login&logout

    Forget Password , and which server do you recommend retrieve user info.

    Regards
    AA

  8. Thanks for great tutorial. It helps me a lot.

    Just one question, how to I put a custom header field into requests rather than just accessToken?

    Thanks.

  9. kgoedecke

    What exactly are you trying to achieve? I don’t think its intended nor possible by the LoopBack SDK to add custom header fields to the request.

    Cheers

    Kevin

  10. My loopback api app requires to put another token (3rd party) into the header and have a middle-ware to process it.

    So I want to put a new field into the header rather than access_token. Seems they don’t provide a way to do it.

    Thanks.

  11. I see you don’t monetize kevingoedecke.com, don’t waste your
    traffic, you can earn extra bucks every month with new monetization method.
    This is the best adsense alternative for any type of website (they approve all sites), for more
    details simply search in gooogle: murgrabia’s tools

Leave a Reply

Your email address will not be published. Required fields are marked *