iOS Client

In this guide we are going to create a simple iOS application that uses a KituraKit client to interact with a Kitura server. The app we are going to make will be simple and holds a list of fruit that you can scroll through. Upon selecting a desired fruit, we will send a GET request to a server that will return the price of the selected fruit.

This guide requires Xcode 11.


Setting up your server

If you don't have a server, follow our Create a Server guide.

Kitura and KituraKit send and receive instances of Swift types directly. These types (aka models) can be shared between the client and server.

We're going to do our development inside Xcode, so let's open your server project in Xcode.

Firstly, we need to create a Fruit model. Create a folder, if you haven't already, called Models, inside your Application folder, by doing File > New > Group. Within your Models folder, create a new file called Fruit.swift, by doing File > New > File and selecting Swift as the type of file.

Open your Fruit.swift file and create your Fruit model. The only requirement for a model is that it conforms to the Codable protocol:

public struct Fruit: Codable {
    public let name: String
    public let price: Double
    public init(name: String, price: Double) {
        self.name = name
        self.price = price
    }
}

We need to add a file for our fruit routes. Open your Application.swift file and inside the postInit() function add:

initializeFruitRoutes(app: self)

Create a new file, called FruitRoutes.swift in your Routes folder and inside this file, add the following code:

import Foundation
import KituraContracts

func initializeFruitRoutes(app: App) {

}

This code imports our required modules and sets up the framework for a routes page.

Firstly, we're going to create a String to Double dictionary that holds all the names for fruit in our list and their corresponding prices. Inside the scope of our initializeFruitRoutes function add this code:

var fruitDict: [String:Double] = [:]
fruitDict = ["Apple": 0.35,
               "Banana": 0.27,
               "Orange": 0.29,
               "Pear": 0.18,
               "Watermelon": 1.79,
               "Mango": 0.68,
               "Pineapple": 1.49,
               "Lemon": 0.45,
               "Kiwi": 0.78,
               "Grapefruit": 0.64]

After we have done this, we will register a GET route on the router to the "/fruit" path. We will want this to take the the name of the fruit, as a String, get the corresponding value and return the Fruit object containing both the fruit name and its price. Add this code after the dictionary:

app.router.get("/fruit") { (fruitName: String, completion: (Fruit?, RequestError?) -> Void) in
    let fruitPrice = fruitDict[fruitName]
    let fruitObject = Fruit(name: fruitName, price: fruitPrice!)
    completion(fruitObject, nil)
  }

Your finished FruitRoutes.swift file should look like so:

import Foundation
import KituraContracts

func initializeFruitRoutes(app: App) {

    var fruitDict: [String:Double] = [:]
    fruitDict = ["Apple": 0.35,
                   "Banana": 0.27,
                   "Orange": 0.29,
                   "Pear": 0.18,
                   "Watermelon": 1.79,
                   "Mango": 0.68,
                   "Pineapple": 1.49,
                   "Lemon": 0.45,
                   "Kiwi": 0.78,
                   "Grapefruit": 0.64]

    app.router.get("/fruit") { (fruitName: String, completion: (Fruit?, RequestError?) -> Void) in
        let fruitPrice = fruitDict[fruitName]
        let fruitObject = Fruit(name: fruitName, price: fruitPrice!)
        completion(fruitObject, nil)
    }
}

Setting up your iOS app

Open Xcode, and select File > New > Project and select new iOS Single View App. Name the product name FruitApp, enter your organization identifier as your name and select Storyboard as the option for your user interface. Select the directory where you would like your app to be situated, then click create.

Adding KituraKit using CocoaPods

Before we begin creating the app, open a terminal window and navigate to where your .xcodeproj file is and enter the following code:

pod init

This will create the podfile for our app, open the podfile in your default text editor and add the following code beneath # Pods for FruitApp:

pod 'KituraKit'

Save the file, close Xcode and run:

pod install

You will notice that an .xcworkspace file has been generated, from now on we will be using this file when creating the app.

Creating the user interface

Open FruitApp.xcworkspace in Xcode and navigate to the Main.storyboard file, click on this file and you should see a blank iPhone screen. At the top right of the Xcode window you should see a plus symbol on a button, this is the library for the user interface. Click on this button and add a label, picker view and button individually so that it looks like this:

iOS UI iamge

Once you have set up your interface, in the top bar, click Editor > Assistant so that your Xcode has the Main.storyboard and ViewController.swift file showing.

Now, hold control, click on the UIPIckerView and drag it into the ViewController class naming it itemName. Then do the same for the Button, naming it priceButton.

Make sure to select outlet as the option for type of connection.

Finally, click on the Button and drag it into the ViewController class after the viewDidLoad function, this time select Action for the type of connection, select the sender to be UIButton and name it getPriceAction:

Adding functionality

Firstly, open the info.plist file in your default text editor and add this anywhere in the first dictionary declaration:

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

Next, we need to create another Fruit model. Create a Fruit.swift file in your FruitApp folder, open your file and create your Fruit model:

public struct Fruit: Codable {
    public let name: String
    public let price: Double
    public init(name: String, price: Double) {
        self.name = name
        self.price = price
    }
}

Then go back to our ViewController.swift file and add these to our import statements:

import UIKit
import KituraKit
import KituraContracts
import Dispatch

Now, add UIPickerViewDelegate and UIPickerViewDataSource as conformities for the ViewController so that it looks like this:

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

After this, create two variables, we will use these later on:

var valueSelected: String = "Apple"
var pickerData: [String] = [String]()

Inside the scope of the viewDidLoad function, we need to add the data for the UIPickerView to display:

self.itemName.delegate = self
self.itemName.dataSource = self

pickerData = ["Apple", "Banana", "Orange", "Pear", "Watermelon", "Mango", "Pineapple", "Lemon", "Kiwi", "Grapefruit"]

In order to get the selected value from the UIPickerView, we have to include several functions. After the viewDidLoad() function, add:

func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return pickerData.count
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    return pickerData[row]
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    valueSelected = pickerData[row] as String
}

Now, when using the variable valueSelected, it will return the value that is currently selected on the UIPickerView.

valueSelected has an initial default value set to Apple, the first fruit in our list.

Inside the scope of the getPriceAction function we will now set up the client side of our application. Firstly we will create a client object:

if let client = KituraKit(baseURL: "http://localhost:8080") {

}

Inside this if statement, we will declare our GET route, which has an identifier of valueSelected and expects a Fruit object returned:

client.get("/fruit", identifier: valueSelected) { (returnedFruit: Fruit?, error: RequestError?) -> Void in
}

With the returned fruit, we will create an alert that will tell the user how much one of their selected fruit costs:

let alert = UIAlertController(title: "Fruit price:", message: "\(returnedFruit!.name)s cost £\(returnedFruit!.price) each", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: .default))

Finally, we need to present this alert in the view:

DispatchQueue.main.async {
    self.present(alert, animated: true, completion: nil)
}

Any code that changes the view has to be executed on the main thread, this is why we have to use DispatchQueue.main.async.

Congratulations! If you have followed everything correctly, your ViewController.swift file should look like this:

import UIKit
import KituraKit
import KituraContracts
import Dispatch

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    @IBOutlet weak var itemName: UIPickerView!
    @IBOutlet weak var priceButton: UIButton!

    var valueSelected: String = "Apple"

    var pickerData: [String] = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        self.itemName.delegate = self
        self.itemName.dataSource = self

        pickerData = ["Apple", "Banana", "Orange", "Pear", "Watermelon", "Mango", "Pineapple", "Lemon", "Kiwi", "Grapefruit"]
    }

    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return pickerData.count
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return pickerData[row]
    }

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        valueSelected = pickerData[row] as String
    }

    @IBAction func getPriceAction(_ sender: UIButton) {
        if let client = KituraKit(baseURL: "http://localhost:8080") {
            // Use client to make requests here
            client.get("/fruit", identifier: valueSelected) { (returnedFruit: Fruit?, error: RequestError?) -> Void in
                let alert = UIAlertController(title: "Fruit price:", message: "\(returnedFruit!.name)s cost £\(returnedFruit!.price) each", preferredStyle: UIAlertController.Style.alert)
                alert.addAction(UIAlertAction(title: "Dismiss", style: .default))
                DispatchQueue.main.async {
                    self.present(alert, animated: true, completion: nil)
                }
            }
        }
    }
}

Test your app

Now everything is set up, run your server project and your iOS project to test your app!

You have now successfully created a front end in iOS using a KituraKit client and a corresponding back end on a server using Kitura!