Search
  • Riya Manchanda

REST API: How to Connect to the WooCommerce API through Basic Authentication in Swift



So I recently began working on a client project where I had to create an E-Commerce IOS application in Swift 5 (SwiftUI) with the client's Wordpress/WooCommerce Website Store as the API backend. One challenge I faced doing this is making authenticated requests to the backend, especially because I am fairly new to the concept of REST API. So in this tutorial I will write about how I logged in Users from Wordpress and made authenticated HTTP requests to the WooCommerce backend from a SwiftUI app.


Table of Contents:

  • What is the Rest API?

  • Introduction to the Wordpress API

  • Introduction to Authentication

  • Getting Started with Alamofire

  • Authentication Through API Key and Secret

  • The Wordpress Rest Api Authentication Plugin

  • Making an Authenticated Request

  • Adding User Login


Before we begin, let us just do a brief introduction to the Rest API, touching upon the basics and not concerning ourselves much with the details and in-depth information.



What is the REST API?


REST refers to Representational State Transfer, is a protocol or style of engineering software to regulate transfer of data on the World Wide Web.


Image Credits (Copyright infringement not intended).


This protocol was developed by Scientist Roy Fielding, and has been commonly followed worldwide. The REST Architectural style generally includes methods like GET, PUT, POST, DELETE, CUSTOM, and other HTTP methods depending on the purpose of the application. In this tutorial, we will be using this protocol to make requests to our WordPress website. You can learn more about the REST API here.



Introduction to the Wordpress API


So the Wordpress API is pretty much independent for each individual website. This API is an interface for developers to transfer the website's data in the json format, which can be used to power custom web and native applications.


The most popular method of accessing this API is the REST Protocol introduced above. Wordpress provides an exhaustive list of its own endpoints, you can check them out on the developer documentation here. For our purposes we also need to access the API for WooCommerce plugin in our Wordpress websites, since we are dealing with an E-commerce store. In the latest API (WC Version 3), WooCommerce data is accessible through the WordPress Rest API endpoints.


Before we move any forward, we must study these endpoints and find the ones that are useful to us. Our basic url formatting will be:


"https://www.yourwebsitename.com/wp-json". 

And we will add the following to it for accessing WooCommerce data:


"https://www.yourwebsitename.com/wp-json/wc/v3". 

Another thing note is that the Wordpress API is only accessible privately through authentication, and the supported methods include Basic Auth, OAuth, and Token. Let us learn more about Authentication below.



Introduction to Authentication


In simple terms, Authentication is the process of approving a user and providing them access to your server or in this case, your Wordpress API. It refers to the action of verifying a user through a process like login or OTPs, which using particular credentials unique to each user and encrypted to avoid breach. How this is done is, whenever we make a REST or Http Request, we send along those credentials to gain access to the server.


When we talk about software, the Authentication process usually takes place at the launch of all applications before any sensitive requests are made to the API. This practise is important to ensure only privacy is maintained and cyber crimes are minimised, so that only the appropriate user has access to the information relevant only to them. Mainly there are three types of Authentication systems for the REST API:


  • Basic Authentication - This consists of simple username and password, which are sent along the HTTP request as the user's identity credentials.

  • OAuth - OAuth is slightly more complicated being a method of both authentication and authorisation, but it mainly uses access tokes to make API requests.

  • API Keys - An API Key is a uniquely generated value by the server or by the device, which provides access to a particular user to the server.


In this tutorial, we are going to dive deeper into Basic Authentication, utilising the WordPress user's username and password to make requests to the API.



Getting Started with Alamofire


Now let us start looking at our app side of things. For IOS Swift apps, the HTTP and the REST Protocol can be implemented using a dependency called, 'Alamofire'. Alamofire is essentially a HTTP Networking library base purely on Swift, which allows developers to write REST API and make networking requests from an app.


Let us begin by creating a new Swift Project, and adding the Alamofire git Repository to our project as a package dependency by going to File > Swift Packages > Add Package Dependency. Once that is done, we go to our app's target and add the Alamofire framework to our app's Dependencies and Link with Binary Libraries dropdown.



Now the first step will be to check if our Package has been added correctly, by importing it in our Swift file. For now I will just use our ContentView.swift file, but ideally you should make a separate file for your REST API.


import Alamofire

If you're getting an error at this point, you might want to check your Alamofire installation again. Otherwise, we are now good to go and start making requests. Let us make a new observable Class for our request functions, so that we can call them in any View we want:



class Queries : ObservableObject {

    // Code goes here!

}


Great, now let us write our very first REST API function using Alamofire. For our purposes, let us all the WooCommerce products of our WordPress website. Note that this syntax works for the latest version of Xcode (V12) and Swift (V5).



func firstRequest {
    AF.request ("https://www.yourwebsitename.com/wp-json/wc/v3/products")
}
    

And there you are, that is our request. By default, the request will be a GET request, meaning we will be fetching data. However this function yet does not deal with our data in anyway. Before we move on, we must familiarise ourselves with the 'JSON' format of data, since that is the format we will receive. Now let us add a handler:



func firstRequest {
    AF.request ("https://www.yourwebsitename.com/wp-json/wc/v3/products")
    .responseJSON {
        response in if json = response.value {
            if (json as? [String : AnyObject]) != nil {
                if let dictionary = json as? Dictionary<String, AnyObject?> {
                if let array = dictionary as? Array<Dictionary<String, AnyObject?>> {
                    // More code here!
                }
            }
        }
    }
}
    

Phew, confusing? What we have done here is, taken the JSON response value obtained as a result of our GET request, and unpacked its value. If you have worked with JSON before, you will see how I have unpacked the structure to receive first the json in a String: Object format, then converted it to a Dictionary, and then as an Array of individual product Dictionaries. Now we can iterate over our Array to create our Products. But in order to store this product information in our app front-end, we will make an identifiable struct and create its instances:



struct Product : Identifiable {
    public var id: Int
    public var name: String
}


And now we make instances of this structure for each of our products.



@Published var products = [Product]()

func firstRequest {
    AF.request ("https://www.yourwebsitename.com/wp-json/wc/v3/products")
    .responseJSON {
        response in if json = response.value {
            if (json as? [String : AnyObject]) != nil {
                if let dictionary = json as? Dictionary<String, AnyObject?> {
                if let array = dictionary as? Array<Dictionary<String, AnyObject?>> {
                    for i in 0 ..< array.count {
                        let product = array[i]
                        if let id = product["id"], let title = product["name"] as? String {
                            self.products.append(Product(id: id, name: title))
                        }
                    }
                }
            }
        }
    }
}
    

Here, we have created a published variable for our products list, so that this data is now accessible and available for our other views and structures. So, that completes our request! You can already try to run it, but it won't yet work since we have not authenticated yet.



Authentication through API Key and Secret


To begin with, let us start by using simple Consumer API Key and Consumer Secret to access our WooCommerce API, just to get a taste of what we're dealing with. These are nothing but an access token we will be generating for ourselves and hardcoding for developer purposes.


NOTE: API Keys and Secrets are extremely sensitive information and must be handled with caution, sharing or storing online without encryption is majorly discouraged.


Firstly, we should navigate to our WooCommerce Plugin and go to Settings > Advanced. There in the menu bar, navigate to REST API. The following window should pop up:



Here, press on Add key. There you should a description for the purpose of the key, you can just write "Testing". Then select the user who has access to this key, which would ideally be the admin. Lastly, you can change the permissions to Read & Write or whatever you are comfortable with. It should look like this:



Then you will be prompted with a screen with your API Key and Password, make note of it and save it since you will not be able to see your password again. Once you are done with that, navigate to Legacy API option on the menu bar, and enable Legacy REST API:



With that, we can now start making our requests by adding this information to our Alamofire request which we structured before this. We can do it like this:


AF.request("https://www.digdeco.com/wp-json/wc/v3/products?consumer_key={yourkey}&consumer_secret={yoursecret}")


Now if you run this same code again, this will give you an output in the json format. And voila, just like that you can make requests to your Wordpress API!



The Wordpress REST API Authentication Plugin


One thing to observe here for first-time programmers is that the main purpose of authentication is to verify users who are logging in to our app/website, and hard-coding consumer keys and secrets like we just did above is NOT the ideal way of doing that. For this we use basic authentication, where we send our user's username and password along with our url request to the API.


However, to enable this form of authentication in our Wordpress API, for it to be able to interpret our user's credentials, I will be using a Wordpress Plugin called WordPress Rest API Authentication Plug-in. It basically secures access to your API using different options:



The above is what you will see once you install the plugin, activate it, and navigate to it. Some of their options are paid, but we are going to use their free 'Basic Authentication' method, and then select username-password. This will authenticate our users through their Wordpress username-password. Then click on 'Save Configuration'.


The last step for this part would be to check if our desired WooCommerce API endpoints are included in the Protected Rest APIs. So Navigate to Protected REST APIs. And make sure that all our desired endpoints are checked, especially WooCommerce V3:



Once that is done, we are now ready to use our Plugin and make secure requests using basic authentication for our users. Let's see how that can be done.



Making an Authenticated Request


Now we will get rid of our consumer key and secret, and add basic authentication to the first request function that we made. For this, let us review our function:



@Published var products = [Product]()

func firstRequest {
    AF.request ("https://www.yourwebsitename.com/wp-json/wc/v3/products")
    .responseJSON {
        response in if json = response.value {
            if (json as? [String : AnyObject]) != nil {
                if let dictionary = json as? Dictionary<String, AnyObject?> {
                if let array = dictionary as? Array<Dictionary<String, AnyObject?>> {
                    for i in 0 ..< array.count {
                        let product = array[i]
                        if let id = product["id"], let title = product["name"] as? String {
                            self.products.append(Product(id: id, name: title))
                        }
                    }
                }
            }
        }
    }
}
  

For sending the username and password along with out request, we use something called 'HTTP Headers', and we add these headers to our Alamofire request. These headers allow us to pass extra information along with our requests. Another thing to note is that the username and password that we will send will be base-64 encoded to make it secure.


The format is: "Authorization": "Basic username : password"


So this means that we need to convert our username and password into base64, and join them with a colon in between like this:



let credentialData = "\(username):\(password)".data(using: String.Encoding.utf8)!
let base64Credentials = credentialData.base64EncodedString(options: [])


Here we are first converting our string-type username and password into UTF8 data format, and then we are using basic Swift syntax to make it into Base-64. Now we can make this into a HTTP header with the following syntax:



let headers: HTTPHeaders = ["Authorization": "Basic \(base64Credentials)"]


Great, now we have our HTTP Header named 'authorization', so now we can go ahead and add it to our Alamofire request as follows:



AF.request ("https://www.yourwebsitename.com/wp-json/wc/v3/products", headers: headers)


And voila! Just like that, you have successfully made your basic authentication requests to WordPress. The entire class should look like this:



class Queries : Observable {
    @Published var products = [Product]()

    func firstRequest (username: String, password: String) {

        let credentialData = "\(username):\(password)".data(using: String.Encoding.utf8)!
        let base64Credentials = credentialData.base64EncodedString(options: [])

        let headers: HTTPHeaders = ["Authorization": "Basic \(base64Credentials)"]

        AF.request ("https://www.yourwebsitename.com/wp-    json/wc/v3/products", headers: headers)
        .responseJSON {
            response in if json = response.value {
                if (json as? [String : AnyObject]) != nil {
                    if let dictionary = json as? Dictionary<String, AnyObject?> {
                    if let array = dictionary as? Array<Dictionary<String, AnyObject?>> {
                        for i in 0 ..< array.count {
                            let product = array[i]
                            if let id = product["id"], let title = product["name"] as? String {
                                self.products.append(Product(id: id, name: title))
                            }
                        }
                    }
            }
            }
        }
    }
}


Great job! Now the only one thing left for us to do is prompt the user to input their username and password in a simple Login View and pass the information when calling our firstRequest() function. Let us see how we can do that.



Making User Login


Jumping straight to the point, let us first create a View for our login with two textfields, state variables to store our username and password inputs, and a button to submit and trigger our login function:



struct LoginView: View {
 
    @State var username: String = ""
    @State var password: String = ""
 
    var body: some View {
        VStack {
            Text("Let's Sign You In.")
            Text("To Continue, first Verify it's You.")
            
            TextField("Email", text: $username)
            SecureField("Password", text: $password)
 
            Button(action: {
                // action here
            }) {
                Text("Submit")
            }
         }
    }
}


We have made a very basic and not-so-pretty View which can be improved later once the functionality is done. This should look something like this:



I will also go ahead and make a Customer Identifiable struct:



struct Customer : Identifiable {
	public var id: Int
	public var username: String
	public var email: String
}


Now, we need to tweak our above Observable Object function a little bit to fetch the user's details after authentication. We will also need to add a variable which records whether or not the user is logged in, so that we can decide which View to display. Instead of changing our above function, I will create a new UserAuth Class for the sake of clarity:



There you go, with that what we are doing is, making a request to fetch the user's details with their provided credentials. If the credentials are correct, we get a success code from the browser and we make isLoggedIn variable equal to true, moving on to create an instance of our Customer class. Otherwise, our isLoggedIn variable remains false. Now let us go ahead and call this function in our Login View:



struct LoginView: View {
 
    @EnvironmentObject var UserAuth: UserAuth
    @State var username: String = ""
    @State var password: String = ""
 
    var body: some View {
        VStack {
            Text("Let's Sign You In.")
            Text("To Continue, first Verify it's You.")
            
            TextField("Email", text: $username)
            SecureField("Password", text: $password)
 
            Button(action: {
                UserAuth.login(username: username, password: password)
            }) {
                Text("Submit")
            }
         }
    }
}


For this what we have done is, create an EnvironmentObject in our LoginView and assigned it to the value of UserAuth, so that data can easily be passed between these two views. We will also save our user's credentials in our UserAuth, to be able to call functions and requests from different Views.


Great going, now the last step would be to add this LoginView in our ContentView and determine whether or not to show it, using our isLoggedIn variable. For this, we add our UserAuth Class to our ContentView as an Environment on our AppName.swift file:



struct Dig_DecoApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(UserAuth())
        }
    }
}


Then we can add the following code onto our Content View:



struct ContentView: View {

    @EnvironmentObject var UserAuth: UserAuth

    var body: some Scene {
        if !(UserAuth.isLoggedIn) {
            LoginView()
        } else {
            // Your Main View(s)
        }
    }
}
    

And voila! We're done, just like that! For the sake of this tutorial, let me just also show you how you can reuse the user's login credentials to call our firstRequest function as well:



struct ContentView: View {

    @EnvironmentObject var UserAuth: UserAuth
    @ObservedObject var observed = Observer()
    
    var body: some Scene {
        if !(UserAuth.isLoggedIn) {
            LoginView()
        } else {
            Button (action: {
                observed.firstRequest(username: UserAuth.username, password: UserAuth.password)
            }) {
                Text("Tap Me!")
            }
        }
    }
}
 

And to test whether the data is what you expected, you can create a simple view which displays your products stored in observed.products. With this we are done with our REST API Authentication tutorial for WordPress! You can use the same methods for any of the WordPress or WooCommerce API by reading up the documentation.



Conclusion

Excellent job, dear readers! You are now equipped to power your own custom SwiftUI IOS Mobile Application with the API from your very own WordPress website and WooCommerce Store. Remember, the API Authentication is an extremely sensitive activity and should be approached with caution. I hope you found my tutorial helpful, and if you did, don't forget to show your appreciation through the likes and comments section! Before I sign off, I would like to remind you all to keep rockin' and keep discovering!


Until Next Time ~

38 views0 comments