Categories
Past Tutorials Swift WordPress

Swift: Login to WordPress from your iOS App

This tutorial will cover a basic approach to logging into your WordPress site from your iOS using Swift. First, I’m going to start in Xcode by setting up our most basic of login app.

Check out the OAuth2 version of this tutorial

Create your project

What we’re using in this tutorial:

  • Xcode 7.2
  • Swift 2.1
  • Alamofire 3.1.4
  • SwiftyJSON 2.3.2

Fire up Xcode and create a new project. I named this project “Basic WordPress Login”, but you can name it whatever you want. Make sure to select Swift as your language.

Setting up the scenes

Next, we’ll set up the scenes. Drag a label and button onto the initial ViewController scene and set their respective constraints. I named my button “Proceed to Login”, and checked it hidden in the Attributes Inspector. I will explain this a little later once we start setting up the logic behind the app.

Drag another ViewController object onto the storyboard. Next, drag 2 textfields and another button onto the second View. Label the placeholders of each textfield username and password respectively. Name the Login and add the necessary constraints to each object.

View Controllers

Let’s attach the required view controllers to both scenes. On the Main.storyboard, select the initial view controller. On the right utilities pane (make sure it is visible), select the Identity Inspector and make sure it’s attached to the ViewController class.

Go to File > New File and select Cocoa Touch Class. Next, name the view controller LoginViewController, make sure it’s a subclass of UIViewController and save.

Now attach the LoginViewController class to the second scene on the Main.Storyboard. Again, select the scene and the Identity Inspector on the right utilities pane to accomplish this.

Let’s create one more file. I call this the “Needs” file. It’s analogous to a config file, but this file will only contain a few lines. Go to File > New File and select Swift File. Name it and save it into your project. Navigate to the file and add the following line under import Foundation:

public let myWordPressSite = ""

CocoaPods, Alamofire and SwiftyJSON

As mentioned in a previous tutorial, native handling of json is a pain. Again, we will be using SwiftyJSON to handle the response json objects. Alamofire will be used to make the remote request calls to your WordPress site. CocoaPods is what will be used to install Alamofire and SwiftyJSON into our projects. CocoaPods is a dependancy management tool that handles all your project’s external libraries. If you don’t have CocoaPods installed yet, check out the following CocoaPods beginning tutorial by Ray Wenderlich.

After installing CocoaPods, you are going to create a Podfile and save it inside your Xcode project. The Podfile contains a list of libraries used in the project. The Podfile is used by the CLI to install and update these libraries. Open your Podfile and add the following:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

pod 'Alamofire', '~> 3.0'
target 'Basic WordPress Login' do
 pod 'SwiftyJSON', :git => 'https://github.com/SwiftyJSON/SwiftyJSON.git'
end

At the time this tutorial was written, the latest versions of Alamofire and SwiftyJSON are reflected above. Please replace the above version numbers with the latest versions if above version numbers are out of date.

Next, Quit Xcode and open the terminal and cd into your project’s directory. Run the following command:

pod install

After the installation is complete, you’ll notice an additional file named Basic WordPress Login.xcworkspace. From this point forward, you will be launching the project from this file.

So what’s going on here?

The way this setup will work is the initial view controller will send a request to your WordPress site to check if you are logged in, if not, the “Proceed to Login” button will appear prompting you to proceed to the login page. A more sophisticated approach would be to check the login status upon app launch and to display the login screen as the first view if you’re not logged in. Again, this is just a basic tutorial.

Username and password credentials are entered in the Login View Controller. If the login is successful, we will be rerouted back to the parent (initial view controller) with a welcome message awaiting us upon arrival. If the login failed, we will get alert messages telling us what it wrong. Simple enough, right?

Setting up the server side request handler

A little bit of code has to be added to the functions.php before your WordPress site can acknowledge and handle the request from your iOS app. Add the following to your functions.php:

interface iOSWPLoginInterface
{
    public function check_ios_login();
    public function get_ios_login($creds);
    public function is_the_user_logged_in();
}

class iOSWPLogin implements iOSWPLoginInterface
{
    public function check_ios_login()
    {
        $check_login = !empty($_POST['check_login']) ?  (int)$_POST['check_login'] : false;
        
        switch($check_login)
        {
            case 1:
                $this->is_the_user_logged_in();
                //wp_logout();
            break;
            
            case 2:
                $this->get_ios_login($_POST);
            break;
            
            case 3:
                
                $response = array();
                
                if ( is_user_logged_in() ) {
                    wp_logout();
                    $response['logged_out'] = 1;
                } else {
                    $response['logged_out'] = 0;
                }
                
                echo json_encode($response);
                
            break;
        }
    }
    
    public function is_the_user_logged_in()
    {
        if ( is_user_logged_in() ) { 
        
            $user_id = get_current_user_id();
            $user_data = get_userdata( $user_id );
            echo json_encode($user_data);
            
        } else {
            echo json_encode( array('logged_in'=> 0) );
        }
        
        exit();
    }
    
    public function get_ios_login($creds)
    {   
        $message = array();
        
        if( !empty($creds['ios_userlogin']) && !empty($creds['ios_userpassword']) )
        {
            /*
             * Add wp_signon $_POST parameter after $_POST request has been made
             * to avoid conflicts with login form and plugins
             */
             
            $creds['user_login'] = $creds['ios_userlogin'];
            $creds['user_password'] = $creds['ios_userpassword'];
            $creds['remember'] = true;
            
            //wp_signon sanitizes login input
            $user = wp_signon( $creds, false );
            
            if ( is_wp_error($user) ){
                
                //Return error messages
                if(isset($user->errors['invalid_username'])){
                    $message['error'] = 1;
                } elseif(isset($user->errors['incorrect_password'])){
                    $message['error'] = 2;
                }
                
                echo json_encode($message);
                exit(); 
                
            }else{
                echo json_encode($user);
                exit(); 
            }   
        }   
    }
}

add_action('after_setup_theme', array($ios_login = new iOSWPLogin,'check_ios_login'));

You don’t have to add this to the functions.php, you can very well create a plugin from this snippet if you choose. Be aware that I am running PHP 5.6, so this object oriented approach might not be compatible with older versions of PHP. We’re only calling one method from the class check_ios_login. The rest of the logic is handled within the class. It should be noted that you only have to instantiate this class once.

The check_ios_login method checks for the check_login parameter in the $_POST request. The method is_the_user_logged_in is called and responds with json object that contains either user data if logged in, or a logged_in = 0. If the app receives a logged_in = 0 response, we are prompted to “Proceed to Login”. If the app receives user data, we will receive a “Welcome Back” message.

When user and password information is submitted from the login screen of our app, our WordPress site will receive the $_POST request and call the get_ios_login method to log us in.

Note that it’s imperative that you exit() after you call the json_encode function. Remember, the responses are being sent before any page output is rendered. If you don’t exit, the entire HTML output will be included in the response and the App will throw and error.

Back to Xcode

In the Needs.swift file, add your website url to the empty myWordPressSite constant. Select the Login Scene from the Main.storyboard, open the Identity Inspector and add LoginPopOverVC in the Storyboard ID field.

In the ViewController.swift file, add the following:

//
//  ViewController.swift
//  Basic WordPress Login
//
//  Created by wlc on 8/11/15.
//  Copyright (c) 2015 wLc Designs. All rights reserved.
//

import UIKit
import Alamofire
import SwiftyJSON

class ViewController: UIViewController, LoginViewControllerDelegate, UIPopoverPresentationControllerDelegate {

    @IBOutlet weak var loginMessage: UILabel!
    @IBOutlet weak var proceedButton: UIButton!
    @IBOutlet weak var logoutButton: UIButton!
    
    lazy var json : JSON = JSON.null
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        checkLogin()
    }
    
    func checkLogin()
    {
        Alamofire.request(.POST, myWordPressSite, parameters: ["check_login": 1])
            .responseJSON { response in
                
                //Check if response is valid and not empty
                guard let data = response.result.value else{
                    self.loginMessage.text = "Request failed"
                    return
                }
                
                //Convert data response to json object
                self.json = JSON(data)
                
                //Check if logged in
                guard self.json["logged_in"] != 0 else{
                    
                    self.loginMessage.text = "You are not logged in."
                    self.proceedButton.hidden = false
                    
                    return
                }
                
                //If logged in, return welcome message
                guard let name = self.json["data"]["display_name"].string else{
                    return
                }
                
                self.logoutButton.hidden = false
                self.welcomeMessage(name)
                
        }
    }
    
    /* Required method LoginViewControllerDelegate
     * This method returns a welcome message from the LoginViewController
     */
    func sendUser(name : String){
        //self.loginMessage.text = "Welcome back \(name)!"
        self.welcomeMessage(name)
        self.proceedButton.hidden = true
    }
    
    @IBAction func goLogin(sender: AnyObject) {
    
        //Set up the PopOver segue for the LoginViewController
        let loginViewController = storyboard?.instantiateViewControllerWithIdentifier("LoginPopOverVC") as! LoginViewController
        
        loginViewController.delegate = self;
        loginViewController.modalPresentationStyle = .Popover
        
        if let popoverController = loginViewController.popoverPresentationController {
            popoverController.sourceView = sender as? UIView
            popoverController.sourceRect = sender.bounds
            popoverController.permittedArrowDirections = .Any
            popoverController.delegate = self
        }
        
        presentViewController(loginViewController, animated: true, completion: nil)
    }
    
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
        return .FullScreen
    }

    func welcomeMessage(name:String){
        self.loginMessage.text = "Welcome back \(name)!"
        
        //Show Logout Button upon succesful login
        self.logoutButton.hidden = false
    }
    
    
    @IBAction func logOut(sender: AnyObject) {
        
        Alamofire.request(.POST, myWordPressSite, parameters: ["check_login": 3])
            .responseJSON { response in
            
            self.loginMessage.text = "You are not logged in."
            self.proceedButton.hidden = false
            self.logoutButton.hidden = true
 
        }
        
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

And in the LoginViewController.swift, add:

//
//  LoginViewController.swift
//  Basic WordPress Login
//
//  Created by wlc on 8/11/15.
//  Copyright (c) 2015 wLc Designs. All rights reserved.
//

import UIKit
import Alamofire
import SwiftyJSON

//Create a delegate for the LoginViewController
protocol LoginViewControllerDelegate{
    func sendUser(name : String)
}

class LoginViewController: UIViewController {
    
    //inititalize the delegate
    var delegate:LoginViewControllerDelegate!

    lazy var json : JSON = JSON.null
    
    @IBOutlet weak var username: UITextField!
    @IBOutlet weak var password: UITextField!
    @IBOutlet weak var loginButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    
    @IBAction func loginToWordPress(sender: AnyObject) {
        
        if !username.text!.isEmpty && !password.text!.isEmpty{
            remoteLogin(username.text!, password: password.text!)
        }
    }
    
    func remoteLogin(user: String, password: String)
    {
        Alamofire.request(.POST, myWordPressSite, parameters: [
            "check_login": 2,
            "ios_userlogin":user,
            "ios_userpassword":password
            ]).responseJSON { response in
                
                //Check if response is valid and not empty
                guard let data = response.result.value else{
                    self.loginAlert("Login Failed")
                    return
                }
                
                //Convert data response to json object
                self.json = JSON(data)
                
                //Check for serverside login errors
                guard self.json["error"] == nil else{
                    
                    switch(self.json["error"])
                    {
                    case 1:
                        self.loginAlert("Invalid Username")
                        
                    case 2:
                        self.loginAlert("Invalid Password")
                        
                    default:
                        self.loginAlert("Login Failure")
                    }
                    
                    return
                }
                
                //Return to homescreen with welcome message
                guard let name = self.json["data"]["display_name"].string else{
                    return
                }
                
                self.delegate?.sendUser(name)
                
                self.dismissViewControllerAnimated(true, completion: {
                    action in
                })
        }
    }
    
    func loginAlert(alertMessage: String)
    {
        let loginAlert = UIAlertController(title: "Alert", message: alertMessage, preferredStyle: UIAlertControllerStyle.Alert)
        
        let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)
        
        loginAlert.addAction(okAction)
        
        self.presentViewController(loginAlert, animated: true, completion: nil)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

Now if you run the app, you should get the following:

Important takeaways from this tutorial

Delegates

Delegation is the preferred method in Xcode to pass values and data from one view controller to the next. In the LoginViewController.swift file, right above the class you will see the LoginViewControllerDelegate protocol and it’s sendUser method required by view controllers receiving data from the LoginViewController class.

If you have been paying close attention, you will notice that I also applied that same pattern to the iOSWPLogin by way of iOSWPLoginInterface interface. This technique of writing cleaner, more scalable plugins for WordPress was proposed by Daniel Pataki.

Security

Now that you know the basics of logging into WordPress from an iOS app, DO NOT USE THE METHOD IN PRODUCTION. If you want to quell the App Transport Security error message that appears after logging out, you can add you localhost as an NSExceptionDomains in the Info.plist.

First and foremost you want your requests secured by SSL. Second, even though it wasn’t done in this tutorial, you NEVER want to store login information inside the app. I bring this up because you will soon learn that storing cookies in the headers to the app isn’t the most reliable method to staying logged in.

A much better approach would be to use OAuth with a refresh token. In the future, I will cover how to login to WordPress via OAuth and how to set up an OAuth server on your WordPress installation.

Git this Xcode project .