Do you need Zend Framework training or consultancy? Get in touch!

Cross-platform Makefile for Swift

I'm mostly building Swift applications for deployment to Linux, but sometimes it's easier to build and test directly on OS X rather than spinning up a VM. To facilitate this, I use a Makefile that means that I don't have to remember the compiler switches.

It looks like this:

# This Makefile assumes that you have swiftenv installed
# To get going, start with `make init`

SWIFT_VERSION = DEVELOPMENT-SNAPSHOT-2016-05-03-a

# OS specific differences
UNAME = ${shell uname}
ifeq ($(UNAME), Darwin)
SWIFTC_FLAGS =
LINKER_FLAGS = -Xlinker -L/usr/local/lib
endif
ifeq ($(UNAME), Linux)
SWIFTC_FLAGS = -Xcc -fblocks
LINKER_FLAGS = -Xlinker -rpath -Xlinker .build/debug
PATH_TO_SWIFT = /home/vagrant/swiftenv/versions/$(SWIFT_VERSION)
endif


build:
	swift build $(SWIFTC_FLAGS) $(LINKER_FLAGS)

test: build
	swift test

clean:
	swift build --clean

distclean:
	rm -rf Packages
	swift build --clean

init:
	- swiftenv install $(SWIFT_VERSION)
	swiftenv local $(SWIFT_VERSION)
ifeq ($(UNAME), Linux)
	cd /vagrant && \
	  git clone --recursive -b experimental/foundation https://github.com/apple/swift-corelibs-libdispatch.git && \
	  cd swift-corelibs-libdispatch && \
	  sh ./autogen.sh && \
	  ./configure --with-swift-toolchain=/home/vagrant/swiftenv/versions/$(SWIFT_VERSION)/usr \
	    --prefix=/home/vagrant/swiftenv/versions/$(SWIFT_VERSION)/usr && \
	  make && make install
endif


.PHONY: build test distclean init

Let's go through it.

SWIFT_VERSION = DEVELOPMENT-SNAPSHOT-2016-05-03-a

This simply sets which version of Swift this project uses. It's up the top as this is the only line that I change on a regular basis.

# OS specific differences
UNAME = ${shell uname}
ifeq ($(UNAME), Darwin)
SWIFTC_FLAGS =
LINKER_FLAGS = -Xlinker -L/usr/local/lib
endif
ifeq ($(UNAME), Linux)
SWIFTC_FLAGS = -Xcc -fblocks
LINKER_FLAGS = -Xlinker -rpath -Xlinker .build/debug
endif

This section allows for different compiler and linker switches for each operating system.

We're compiling libdispatch on Liux, so we need to enable the blocks language feature in clang. This is done by using the -Xcc switch to tell the compiler to pass the next switch (-fblocks) to clang.

For linking on OS X, I nee to pick up the Homebrew Mysql library in /usr/local/lib. This isn't needed on Linux as apt puts the relevant library in the right place. However, on Linux we need to link against a library in our own .build/debug directory, so we pass the switches for that. In the same way as -Xcc, -Xlinker passes the next parameter to the linker (ld). We need to pass -rpath .build/debug, but as we can only pass one argument at a time with -Xlinker, we do it twice.

build:
	swift build $(SWIFTC_FLAGS) $(LINKER_FLAGS)

test: build
	swift test

clean:
	swift build --clean

distclean:
	rm -rf Packages
	swift build --clean

These are the standard make targets for building, testing and cleaning up intermediate files. By using the standard names, working on different projects is very easy and predictable as the same make commands work everywhere.

init:
	- swiftenv install $(SWIFT_VERSION)
	swiftenv local $(SWIFT_VERSION)
ifeq ($(UNAME), Linux)
	cd /vagrant && \
	  git clone --recursive -b experimental/foundation https://github.com/apple/swift-corelibs-libdispatch.git && \
	  cd swift-corelibs-libdispatch && \
	  sh ./autogen.sh && \
	  ./configure --with-swift-toolchain=/home/vagrant/swiftenv/versions/$(SWIFT_VERSION)/usr \
	    --prefix=/home/vagrant/swiftenv/versions/$(SWIFT_VERSION)/usr && \
	  make && make install
endif

The init target is specific to my Swift projects. It sets the correct local Swift version for this project using swiftenv. The - before the swiftenv install command ensures that make continues even if this command fails (which it will if the version is already installed).

We then do something that's specific to Linux and install lib-dispatch which we need for GCD. It's included already in OS X, which is why this is guarded by the ifeq ($(UNAME), Linux).

That's it. This is a simple Makefile which leaves me to think entirely about my code, rather than my build system.

View header and body with curl

I recently discovered the -i switch to curl! I have no idea why I didn't know about this before…

Curl is one of those tools that every developer should know. It's universal and tends to be available everywhere.

When developing APIs, I prefer to use curl to view the output of a request like this:

$ curl -v -H "Accept: application/json" https://api.joind.in/
*   Trying 178.208.42.30...
* Connected to api.joind.in (178.208.42.30) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: api.joind.in
* Server certificate: Gandi Standard SSL CA 2
* Server certificate: USERTrust RSA Certification Authority
* Server certificate: AddTrust External CA Root
> GET / HTTP/1.1
> Host: api.joind.in
> User-Agent: curl/7.43.0
> Accept: application/json
> 
< HTTP/1.1 200 OK
< Date: Sun, 15 May 2016 11:05:27 GMT
< Server: Apache
< X-Powered-By: PHP/5.6.4
< Access-Control-Allow-Origin: *
< Content-Length: 363
< Content-Type: application/json; charset=utf8
< 
* Connection #0 to host api.joind.in left intact
{"events":"https:\/\/api.joind.in\/v2.1\/events","hot-events":"https:\/\/api.joind.in\/v2.1\/events?filter=hot","upcoming-events":"https:\/\/api.joind.in\/v2.1\/events?filter=upcoming","past-events":"https:\/\/api.joind.in\/v2.1\/events?filter=past","open-cfps":"https:\/\/api.joind.in\/v2.1\/events?filter=cfp","docs":"http:\/\/joindin.github.io\/joindin-api\/"}

-v is for verbose and so you get told all the information you could possibly want. However, usually, I only want to know the response's headers and body.

Enter the -i switch!

$ curl -i -H "Accept: application/json" https://api.joind.in/
HTTP/1.1 200 OK
Date: Sun, 15 May 2016 11:10:24 GMT
Server: Apache
X-Powered-By: PHP/5.6.4
Access-Control-Allow-Origin: *
Content-Length: 363
Content-Type: application/json; charset=utf8

{"events":"https:\/\/api.joind.in\/v2.1\/events","hot-events":"https:\/\/api.joind.in\/v2.1\/events?filter=hot","upcoming-events":"https:\/\/api.joind.in\/v2.1\/events?filter=upcoming","past-events":"https:\/\/api.joind.in\/v2.1\/events?filter=past","open-cfps":"https:\/\/api.joind.in\/v2.1\/events?filter=cfp","docs":"http:\/\/joindin.github.io\/joindin-api\/"}

Much better!

-i is for include and from the man page:

Include the HTTP-header in the output. The HTTP-header includes things like server-name, date ofthe document, HTTP-version and more…

This is exactly what I want without the information that I don't!

swiftenv: Swift version manager

Swift 3 development is so fast at the moment, that a new development snapshot is coming out every couple of weeks. To manage this, Kyle Fuller has rather helpfully written swiftenv which works on both OS X and Linux.

Once installed, usage is really simple. To install a new snapshot:

swiftenv install {version}

Where {version} is something like: DEVELOPMENT-SNAPSHOT-2016-05-09-a, though you can also use the full URL from the swift.org download page.

The really useful feature of swiftenv is that you can set the swift version on per-project basis. As change is so fast, projects are usually a version or so behind. e.g. at the time of writing, Kitura's current release (0.12.0) works with DEVELOPMENT-SNAPSHOT-2016-04-25-a.

We register a project specific swift version using:

swiftenv local {version}


i.e. for Kitura 0.12: swiftenv local DEVELOPMENT-SNAPSHOT-2016-04-25-a

Nice and easy!

Filtering the PSR-7 body in middleware

Sometimes, there's a requirement to alter the data in the Response's body after it has been created by your controller action. For example, we may want to ensure that our brand name is consistently capitalised.

One way to do this is to create middleware that looks like this:

$brandFilter = function ($request, $response, $next) {
    // call next middleware
    $response = $next($request, $response);

    $content = (string)$response->getBody();
    $newContent = str_ireplace('nineteen feet', 'Nineteen Feet', $content);

    $response->getBody()->rewind();
    $response->getBody()->write($newContent);

    return $response;
};

This works perfectly, so if my response body contains "I consult through my company, nineteen feet.", then the output of the middleware$brandFilter middleware is "I consult through thought my company, Nineteen Feet.", which is exactly what we want.

That's great, but what happens if the new string is shorter than the old one? For instance, suppose I want to replace all uses of "nineteen feet" with "19FT".

Using the same middleware, but changing the str_ireplace call with:

$newContent = str_ireplace('nineteen feet', '19FT', $content);

creates this output:

I consult through my company, 19FT.een feet.

That's not quite what we wanted! This result is because the new string is shorter, so the characters from the old string are still in the PHP stream and as we haven't change the length, they are still there. The PSR-7 StreamInterface doesn't allow us access to the underlying stream, so we can't call ftruncate on it.

Hence, the easiest solution is to replace the Response's PSR-7 StreamInterface object with a new one containing what we need:

$brandFilter = function ($request, $response, $next) {
    // call next middleware
    $response = $next($request, $response);

    $content = (string)$response->getBody();
    $newContent = str_ireplace('nineteen feet', '19FT', $content);

    $newBody = new \Slim\Http\Body(fopen('php://temp', 'r+'));
    $newBody->write($newContent);

    return $response->withBody($newBody);
};

The output is now what we expect:

I consult through my company, 19FT.

Slim 3.4.0 now provides PSR-7!

I've been neglecting Slim's PR queue recently, so this weekend I dedicated a lot of time to merging all the good work that our contributors have done. As a result, I'm delighted to release version 3.4.0!

This release has a larger set of changes in it than I would have ideally liked which is a direct consequence of having gone two months between releases rather than one.

One particularly interesting addition that we have a made this release is adding a provide section to our composer.json file:

    "provide": {
        "psr/http-message-implementation": "1.0"
    },

This means that we have informed Composer that Slim provides a valid implementation of the interfaces in psr/http-message-implementation virtual package that defines the PSR-7 interfaces.

This means that when you install a Composer package that requires psr/http-message-implementation in your Slim project, then Composer will now recognise that Slim satisfies this requirement and won't insist you install another PSR-7 implementation just for package resolution!

There's lots of other goodies in 3.4.0, so check out the release notes and upgrade!

Compiling Swift on Linux

Swift is open source, which means that we can build it ourselves. This isn't too hard to do, but takes some time.

Set up the dependencies and grab the code

Firstly, you need a lot of memory and as it takes ages, give your VM plenty of CPUs! My Macbook Pro has a quad core process, so I tell Virtualbox that it can use all 4 using this configuration in my Vagrantfile:

  config.vm.provider "virtualbox" do |vb|
    vb.memory = "8192"
    vb.cpus = 4
  end

You then need all the dependencies inside the VM:

$ sudo apt-get install git cmake ninja-build clang python uuid-dev libicu-dev \
icu-devtools libbsd-dev libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev \
libncurses5-dev pkg-config

If you want to build the docs, then you also need Sphinx:

$ sudo easy_install -U Sphinx==1.3.4

Now grab the code from GitHub:

$ mkdir swift-dev && cd swift-dev
$ git clone git@github.com:apple/swift.git

In addition to the core swift repository, you also need a number of other repositories and fortunately, there's a script to do this for us:

$ swift/utils/update-checkout --clone-with-ssh

Alternatively, if you don't want to do all this manually, you can just grab this Vagrantfile from IBM.

At a later date, if you want to collect the latest changes to the source files just run update-checkout with no arguments:

swift/utils/update-checkout --all

Introducing build_script

To compile Swift we use a script to do it all for us. This one is called build-script which takes a number of different parameters. Use -h to see what it provides.

In order to successfully build in 8GB of RAM, we need to build without the debug symbols, so we use the -R switch for that. To also run the tests, we need -t and if you want to build Foundation and XCTest, you need to add the switches --xctest and --foundation.

The simplest build command, however, is:

$ swift/utils/build-script -R

This is a good time to get a cup of tea. On my computer, it takes 55 minutes to build… Once built, the binaries are in build/Ninja-ReleaseAssert/swift-linux-x86_64/bin.

Compiling for usage

When you build with -R, you're building for contributing to the compiler and associated libraries. If you want to build so that you can then use the tool chain for writing Swift applications, then the easiest way is to use preset which creates an installable package:

$ swift/utils/build-script --preset=buildbot_linux_1510 installable_package=~/swift.tar.gz install_destdir=~/swift-install

Again, this takes a while to do!!

Once done, you have a fully working swift package at ~/swift-install.

Test using:

$ swift-install/usr/bin/swift -v

DI Factories for Slim controllers

When using classes for route actions in Slim 3, I recommend using a single class for each route. However you can use a single class for multiple routes.

To register a class method to a route you pass a string as the route callable where the class name is separate from method by a colon like this:

$app->get('/list', 'MyController:listAction');

Slim will retrieve MyController from the DI container and then call the listAction method using the usual signature:

function (Request $request, Response $response, $args = []) : Response;

If you don't specify a method, then Slim will see if it treat the class as a callable, so you can implement __invoke() and then register the route like this:

$app->get('/list', 'MyController');

and Slim will call MyController::__invoke() for you.

Writing a DI factory for your class

Usually, your controller action will need some dependencies in order to work, such as access to a service layer class, or ORM's entity manager.

To handle this, you should inject the dependency in your controller's constructor by writing a factory for the DI container. This sounds scary and complicated, but a factory is just another way of saying "a function that instantiates an object". This is the simplest DI container factory we can write for MyController:

// Retrieve container instance
$container = $app->getContainer();

// Register MyController
$container['MyController'] = function ($c) {
    return new MyController();
};

The closure is the factory and as you can see, it simply returns an new instance of MyController. It is registered with Slim's default DI container by assigning the closure to an array key (['MyController']) and it is vital that the string you use here is the same as the string you use before the colon in the route configuration ('MyController:list'.

Injecting the dependencies

To inject the dependencies, we register them with the DI container too as factories and then retrieve them in our controller factory.

Firstly, register a dependency:

$container['DatabaseService'] = function ($c) {
    return new DatabaseService();
};

Now we can use this in our controller factory. To do this note that the factory closure has a parameter, $c, which is the DI container itself. This means we can retrieve anything that's registered with the DI container by using the get() method.

Hence we update our controller factory like this:

$container['MyController'] = function ($c) {
    $dbService = $c->get('DatabaseService');
    return new MyController($dbService);
};

The MyController constructor now receives our dependency and can store it to a class property ready for use in the route action method like this:

final class MyController
{
    private $dbService;
    public function __construct($dbService)
    {
        $this->dbService = $dbService;
    }

    public function listAction($request, $response, $args)
    {
        $dataArray = $this->dbService->fetchData();
        return $response->withJson($dataArray);
    }
}

There are numerous advantages to doing this. The main one for me is that there are no surprise dependencies any more. You can look at the constructor and know exactly which classes this class needs to do its job. You can also test it more easily which is beneficial!

I prefer to use one class for each route action as I can ensure that the dependencies that are injected are the correct ones for this action. When using multiple action methods in a controller class, you start needing to inject classes that are only used for just one or two of the actions and this is inefficient, especially if those dependencies are relatively expensive to construct. There are ways around this if you use a more powerful DI container such as Zend-ServiceManager though.

Easily install Swift on Linux

This is the simplest set of steps that I've found so far in order to get Swift on (Ubuntu) Linux. Essentially, you can download a snapshot and you're good to go.

If you're not already on Ubuntu 15:10, create a VM. Vagrant is an easy way to do this:

$ vagrant init ubuntu/wily64
$ vagrant up
$ vagrant ssh

Install the dependencies:

$ sudo apt-get install clang libicu-dev vim

(Vim isn't an actual dependency, but I can't cope with terminal that doesn't have it!)

Grab a Swift snapshot & untar:

$ cd ~
$ curl -O https://swift.org/builds/development/ubuntu1510/swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a/swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a-ubuntu15.10.tar.gz
$ mkdir swift
$ tar xzf swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a-ubuntu15.10.tar.gz -C swift --strip-components 1

I've used the latest snapshot as of this writing: check the website for the latest one as it is updated regularly.

The binaries are now in ~/swift/usr/bin, so add that to your path and you're good to go:

$ echo 'export PATH=~/swift/usr/bin:$PATH' >>~/.profile
$ source ~/.profile

Prove it works:

$ swift --version
Swift version 3.0-dev (LLVM 752e1430fc, Clang 3987718dae, Swift 36739f7b57)
Target: x86_64-unknown-linux-gnu

Now, you just need to learn Swift!

Unit testing with Swift PM

As I tend to work with Swift on Linux, I've been working out how to unit test using the Swift Package manager. This article is my way of remembering what I've learnt so far! Let's do this in the context of writing an a Todo entity from the Todo-Backend project.

Create a package

Creating a package is easy with Swift PM:

mkdir Todo
cd Todo
swift build --init library

(you can replace library with executable when creating an app)

When you run this, Swift PM will create the following:

.
├── Package.swift
├── Sources
│   └── Todo.swift
└── Tests
    ├── LinuxMain.swift
    └── Todo
        └── Todo.swift

Your Swift source files go in Sources and your unit tests go in Tests.

A simple Todo object

Inspecting the Todo-Backend test suite, we can see that a todo item has the following properties:

  • title: a string
  • completed: a boolean
  • order: an integer

Let's build that in Swift and we'll put it in Sources/Todo.swift:

public struct Todo {
    let title: String
    let completed: Bool
    let order: Int
    
    init(title: String, completed: Bool, order: Int) {
        self.title = title
        self.completed = completed
        self.order = order
    }
}

I've chosen to implement this as a value object so that title, completed and order cannot be changed after initialisation. If we need to change them, we'll create a new object from the information in this one.

Setup the unit test code

SwiftPM automatically creates two files for us in the Tests directory: LinuxMain.swift which is a list of all the unit test classes in our package and Todo/Todo.swift which is our test class. Interestingly, the test class extends our main class, which is a fairly common pattern in Swift.

Swift and Swift Package Manager are both under heavy development and not always in sync with each other. With the 12th April 2016 build of Swift 3.0, the skeleton files created by Swift PM for running the tests under Linux are wrong! Let's rewrite them.

Firstly, I prefer my unit test class to be separate, so delete Tests/Todo/Todo.swift and create Tests/Todo/TodoTests.swift. This file holds our unit test class, and we'll write a simple unit test to get us started:

import XCTest
@testable import Todo

class TodoTests: XCTestCase {
	func testInitSetsTitle() {
        let todo = Todo(title: "test", completed: false, order: 1)
        
        XCTAssertEqual(todo.title, "test", "Incorrect title")
	}
}

Our unit test method is testInitSetsTitle and it simply instantiates a Todo and then uses XCTAssertEqual to check that the title of the object matches the string that we set.

Linux specific additions

To test under Linux, we need to do two things. Firstly we need to provide a list all the unit test methods in the test class as a static variable and secondly we need to list our test classes in a file called LinuxMain.swift.

To do the first bit, we add an extension to TodoTests within Tests/Todo/TodoTests.swift:

#if os(Linux)
extension TodoTests {
	static var allTests : [(String, TodoTests -> () throws -> Void)] {
		return [
			("testInitSetsTitle", testInitSetsTitle),
		]
	}
}
#endif 

This block extends the TodoTests class solely for Linux to add a static variable called allTests which is a an array of tuples consisting of the name of the test and the test's method name. By convention, the string name is the same as the actual function name as it seems to be used by the test runner as a label.

Secondly we create Tests/LinuxMain.swift. This file was created by Swift PM, but as I said earlier is wrong, so needs to be replaced with:

import XCTest
@testable import TodoTestSuite

XCTMain([
    testCase(TodoTests.allTests),
])

This file simply instantiates XCTMain with a list of testCases for each test class' allTests variable. This is another thing that really should be sorted via a build system by the time Swift 3 goes stable… As it is today though, it's not hard to keep it updated.

Running the unit tests

To run the unit tests, we simply build the project with swift guid and then run swift test:

$ swift build
Compiling Swift Module 'Todo' (1 sources)

$ swift test
Compiling Swift Module 'TodoTestSuite' (1 sources)
Linking .build/debug/test-Package
Test Case 'Todo.testExample' started.
Test Case 'Todo.testExample' passed (0.0 seconds).
Executed 1 test, with 0 failures (0 unexpected) in 0.0 (0.005) seconds
Total executed 1 test, with 0 failures (0 unexpected) in 0.0 (0.005) seconds

That's it

That's all there is to unit testing in Swift under Linux. There's not a lot of documentation yet, but I found Apple's Writing Test with Swift documentation handy.

Other useful reading is SE-0019: Swift Testing is also worth reading as that documents how testing is implemented in Swift PM and the Additional Considerations for Swift on Linux document in the swift-corelibs-xctest repository.

Overriding Slim 3's error handling

Slim 3 registers two error handers:

This means that if you want to override the default error handlers, you need to override both error handlers with your own code.

Each error handler is a callable. The signatures are:

  • errorHandler: function ($request, $response, $exception)
  • phpErrorHandler: function ($request, $response, $error)

To override a error handler, simply register a new callable with the Container:

$container = $app->getContainer();

$container['errorHandler'] = function ($container) {
    return function ($request, $response, $exception) use ($container) {
        // retrieve logger from $container here and log the error
        $response->getBody()->rewind();
        return $response->withStatus(500)
                        ->withHeader('Content-Type', 'text/html')
                        ->write("Oops, something's gone wrong!");
    };
};

$container['phpErrorHandler'] = function ($container) {
    return function ($request, $response, $error) use ($container) {
        // retrieve logger from $container here and log the error
        $response->getBody()->rewind();
        return $response->withStatus(500)
                        ->withHeader('Content-Type', 'text/html')
                        ->write("Oops, something's gone wrong!");
    };
};

If you don't want to repeat yourself, you can register the `phpErrorHandler` like this:

$container['phpErrorHandler'] = function ($container) {
    return $container['errorHandler'];
};

Note that you can register any PHP callable, so a class with an __invoke() method also works.

Handling PHP notices

As an aside, Slim 2's error handler catches PHP notices for you (which can be infuriating or very useful!). Slim 3 doesn't do this by default, so if you want to catch PHP notices then you need to register your own error handler like this:

set_error_handler(function ($severity, $message, $file, $line) {
    if (!(error_reporting() & $severity)) {
        // This error code is not included in error_reporting, so ignore it
        return;
    }
    throw new \ErrorException($message, 0, $severity, $file, $line);
});

That's all there is to it.