Introduction

Kitura is an open source web framework provided by IBM that is created for building microservices written in Swift. Kitura is a great way to leverage Swift knowledge to develop both client and server sides of your project. This presents the opportunity for a frontend Swift developer to become a full-stack developer by eliminating the middle layer, giving him or her control over server-side services.

While this is a compelling reason for Swift developers to adopt Kitura, another powerful use case is for web development. One can use an existing codebase that is built for an iOS application and also use it to serve web pages. In this tutorial, I implement a simple web application in AngularJS to demonstrate how elegant and easy it is to stand up a web application against a Kitura server.

Clone, build and run the sample project

You may clone, build and run this sample or view it in our Github repository. The sample consists of a very simple, server-side Swift application based on Kitura that supports an AngularJS client application that loads data from the Kitura backend and displays the data in a browser-based user interface.

  • Install Xcode 8 from Apple
  • Clone the repository
  • $ git clone https://github.com/IBM-Swift/Kitura-AngularJS-Sample.git

  • Build the application
  • $ cd Kitura-AngularJS-Sample
    $ swift build

  • Run the built executable
  • $ ./build/debug/Kitura-AngularJS-Sample

  • Open a browser pointing at http://localhost:8090

Statically host your files

There are two steps to stand up your application:

1. Add a middleware route on the server to direct the browser to your application

2. Copy your assets into the directory specified in your new route in step 1

For step one, you must serve your frontend source files to the browser. To serve these files, you can use the Kitura StaticFileServer middleware as is done in Kitura-AngularJS-Sample/Sources/main.swift:

import Kitura

let router = Router()

// Allow for serving up static files found in the public directory
router.all("/", middleware: StaticFileServer(path: "./public"))
 

Here, the static file server will direct traffic that does not match a handled route to then use the StaticFileServer middleware to find files indicated by the relative path to the specified directory, which in the case of this sample is ./public.

For step two, copy your web assets into the directory specified above. In this sample, index.html is placed into the ./public directory, so when the app is run from a browser, index.html is found and static content gets served.

Web assets are copied into the public directory
Web assets are copied into the public directory

In addition, routes.js is placed in this directory. This is a custom javascript file where the majority of the AngularJS magic happens– views are managed along with their assigned templates and controllers.

Add server-side routes to the Kitura application

This sample application performs a very simple service of getting and setting timestamps. The service routes are defined in main.swift on the server and can be consumed by any client—iOS or Android applications, web client, etc. In this case, the timestamp services are consumed via REST calls made from an AngularJS service.

Server-side routes defined in main.swift:

import Foundation
import Kitura
import HeliumLogger
import SwiftyJSON

HeliumLogger.use()

let router = Router()

//
// Allow for serving up static files found in the public directory
//
router.all("/", middleware: StaticFileServer(path: "./public"))

//
// Timestamps
//
// Add REST endpoints for getting, setting and clearing timestamps in an array in memory
//
var timestamps = [String]()

router.get("/timestamps") {
    request, response, next in
    response.send(json: JSON(timestamps))
    next()
}

router.post("/timestamps") {
    request, response, next in
    
    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    formatter.timeStyle = .long
    
    var dateStr = formatter.string(from: Date())
    timestamps.append(dateStr)
    
    response.status(.OK)
    next()
}

router.delete("/timestamps") {
    request, response, next in
    
    timestamps = []
    
    response.status(.OK)
    next()
}

//
// Read environment variables and look for port we should listen on
//
let envVars = ProcessInfo.processInfo.environment
let portString: String = envVars["PORT"] ?? envVars["CF_INSTANCE_PORT"] ??  envVars["VCAP_APP_PORT"] ?? "8090"
let port = Int(portString) ?? 8090

Kitura.addHTTPServer(onPort: port, with: router)
Kitura.run()

AngularJS client setup

Since AngularJS is an MVC framework, this project is organized in terms of views. MVC (model-view-controller) is a software pattern that separates an application into 3 logical components. This is an over-simplification, but generally the model represents data logic, the view contains the UI components of a web page, and the controller is the logic that connects the models to the view.

As you can see in my project’s directory structure, each view is encapsulated as its own component under the simpleApp directory. Shared components such as navigation bar or footer can be stored in a shared directory. Each view has a dedicated HTML, controller and optional service file.

sample-project-structure

The organization of the sample application reflects a structure that would successfully scale. This level of organization is not as important for such a simple project as this tutorial, but it is useful to see a best practice of how a more complicated app might be setup.

Using AngularUI’s UI-Router as a routing framework

AngularUI is a companion suite to the AngularJS framework and provides many useful modules and directives to extend AngularJS libraries. I chose to use AngularUI’s UI-Router as a routing framework because UI-Router provides a state machine. Using UI-Router, you change the views in your application based on state, not just the URL. Routes are referred to as states whereas URLs, templates and controllers are properties of the state. This is a smart approach because as applications scale, views multiply, and as URLs become more complicated, the existence of state makes the interaction of parts more manageable.

In the snippet from index.html below, you see where AngularJS, AngularUI, and custom javascript files are loaded. In addition, note the extended HTML attributes—in AngularJS these attributes are called directives—named ng-app and ui-view. The application simpleApp is declared with the ng-app directive and the ui-view directive tells $state where to place HTML templates. When the application starts, it will look at the loaded javascript files for UI-Router, which it will find in routes.js.

Markup where the application is launched– index.html:

<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
	<!-- load angular, ui-router, and our js file) -->
	<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js" type="text/javascript"></script>
	<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular-resource.js"></script>
	<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular-route.min.js"></script>
	<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js"></script>
    <link rel="stylesheet"
        href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css"
        integrity="sha384-AysaV+vQoT3kOAXZkl02PThvDr8HYKPZhNT5h/CXfBThSRXQ6jW5DO2ekP5ViFdi" crossorigin="anonymous">

	<script src="routes.js"></script>
	<script src="simpleApp/components/homepage/homepageController.js"></script>
    <script src="simpleApp/components/homepage/homepageService.js"></script>
    <link rel="stylesheet" href="assets/css/styles.css" type="text/css" />
</head>

<body ng-app="simpleApp">
	<div id="main">
		<div ui-view ></div>
    </div>
	</body>
</html>

Custom javascript where AngularJS routes are defined– routes.js:

angular.module("simpleApp", ['ui.router', 'ngResource']);

angular.module('simpleApp')
.config(['$stateProvider', '$urlRouterProvider',
         function($stateProvider, $urlRouterProvider) {
         
         $stateProvider
         .state('homepage', {
                url: '/homepage',
                templateUrl: 'simpleApp/components/homepage/homepage.html',
                resolve: {
                timestamps: ['TimestampService', function (TimestampService) {
                             return TimestampService.getInitial();
                             }]
                },
                controller: 'homepageController'
                })
         
         $urlRouterProvider.otherwise('/homepage');
         
         }])
 

Looking closer at the homepage state, you see it contains the properties: url, templateUrl, resolve and controller. TemplateUrl points to the statically-hosted markup and the controller property points to the statically-hosted controller. Resolve provides your controller with data that is custom to the state. It’s a map of dependencies which should be injected into the controller. If any of the dependencies are promises, they will be resolved and converted to a value before the controller is instantiated.

In the case of the homepage state, we want to resolve all timestamps before the page and its controller is loaded. Adhering to a best practice in AngularJS, all of the REST calls are contained in an AngularJS service. In this case, TimestampService makes a REST call to retrieve timestamps from the Kitura server.

AngularJS service– homepageService.js:

angular.module('simpleApp')
.service('TimestampService', ['$http', function ($http) {
                              
                              var url = '/timestamps';
                              
                              this.getInitial = function() {
                              return $http.get(url)
                              }
                              
                              this.get = function(callback) {
                              $http.get(url).then(function(response) {
                                                  callback(response.data)
                                                  });
                              
                              return
                              }
                              
                              this.set = function(callback) {
                              $http.post(url).then(function(response){
                                                   callback(response.data)
                                                   });
                              }
                              
                              this.reset = function(callback) {
                              $http.delete(url).then(function(response){
                                                     callback(response.data)
                                                     })
                              }
                              }]);
 

Returning to routes.js, the timestamps object is populated with the results of the asynchronous call that is made from TimestampService, then it is injected as a dependency into homepageController. An advantage of using ui-router’s resolve object is that it supports a best practice of keeping controllers “skinny” by isolating REST calls to services. This maintains a separation of concerns where the primary objective of the controller is to communicate between services and the view and services are reusable.

AngularJS Controller– homepageController.js:

angular.module('simpleApp')
.controller('homepageController',
            ['$scope', 'timestamps', 'TimestampService', function($scope, timestamps, TimestampService) {
             
             'use strict';
             
             $scope.items = timestamps.data
             
             //
             //	Call the Timestamp service to fetch all the timestamps
             //
             $scope.reloadTimestamps = function() {
             TimestampService.get(function(response) {
                                  $scope.items = response
                                  console.log("Items is ", $scope.items)
                                  });
             }
             
             //
             //	Send a new timestamp request to the server
             //
             $scope.postTimestamp = function() {
             //
             //	Set another timestamp and then reload when completed
             //
             TimestampService.set($scope.reloadTimestamps);
             return true;
             }
             
             $scope.clearTimestamps = function() {
             TimestampService.reset($scope.reloadTimestamps)
             return true;
             }
             }]);

When the application is run in a browser, you’ll see 3 buttons that trigger the REST calls to get, set and delete timestamps and also a list of existing timestamps.

screen-shot-2016-12-05-at-7-18-49-pm

Conclusion

In conclusion, Kitura is a great choice for a server-side framework when building both mobile and web applications. This is particularly true when developing an iOS application that shares a backend implementation language. I chose to implement this sample with AngularJS, but the project setup is the same regardless if you use Angular 2, React or JQuery so feel free to choose your favorite client framework and get started. If you’d like to check out a more sophisticated application, go to our Github repository to see BluePic, which includes a Kitura server and an AngularJS web app.

For more information about building web applications in Swift:

Swift@IBM DevCenter

Kitura.io

Join our Swift@IBM Slack team to ask questions or get engaged with the Kitura project.

2 Comments on "Getting Started with Kitura and AngularJS"

Join The Discussion

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