Do you need training or consultancy? Get in touch!

Serverless Swift on OpenWhisk

I'm interested in serverless computing and as I write Swift, the OpenWhisk platform comes up high when you Google. This turns out to be a really good choice as OpenWhisk is Open Source so I can read the source code (and have done!). In principle, I can also run my own instance of it if I need to to for regulatory reasons, or just to avoid vendor lock-in.

Commercially, the whole point of Serverless (aka Functions as a Service) is that it deal with everything infrastructure related other than the function I am writing, and so I actually host my OpenWhisk functions with IBM's Bluemix.

In a serverless environment we write separate functions that are event driven. Each function can even be in a different language as they are all independent. Also, our functions are stateless which, as an API person, I'm comfortable with. There's more than one way to trigger a function, but I've started with the simplest: an HTTP request.

Far more information is in the docs and I can't recommend the OpenWhisk-Team Slack channel enough; very helpful people on there.

This is my intro post on getting going with OpenWhisk mainly so that all the info I need is in one place!

Notes on Getting Started

There's plenty of blog posts about getting started with OpenWhisk on Bluemix, so this is mostly an aide-memoir for myself as I had to go back on a couple of things that I didn't understand the first time.

Create a Bluemix account:

  • Log into your Bluemix account or create one.
  • OpenWhisk is only provisioned in the US South region
  • Make a note of your organisation and space, you'll need them later!
    (If you're setting up Bluemix for the first time, call your first space "dev")

Set up OpenWhisk:

You should now have a working wsk command line tool. wsk is remarkably helpful. Add -h and it'll give you help.

First Swift action

As OpenWhisk is serverless, we have a single entry function to an action. In Swift has this signature:

This means that we receive a dictionary of arguments (which are called parameters elsewhere in OpenWhisk) and must return a dictionary. (In Swift, a dictionary is what is called an associative array or hash in other languages.). The returned dictionary is the data returned to the caller.

First action

To create an action, we need a swift source file. This can have any name, but my general rule of thumb is to name it the same as the action name. As OpenWhisk looks quite APIish, we'll create a "ping" action and so our file is called ping.swift



Always put your actions in packages. These work a little like namespaces in that you can group together related actions, triggers, rules and what not. You can also attach default parameters to packages that are then available to every action which can be very useful. Interestingly, you can "import" a namespace into another one (known as binding) and when you do so, you can override the parameter values for that package for this specific binding.

To create a package called "P1":

Upload to OpenWhisk

To upload your function to OpenWhisk:

(You can also use create in place of update, but as update will create the action if it doesn't
exist, you may as well just always use update.)

Viewing all actions

To view your actions, list them:

In this case, I have one action, list. It's fully qualified name is "/19FT_dev/P1/ping" and as it's part of a private package, it's private and written in Swift 3. The language information is provided as OpenWhisk supports Java, NodeJS and Python actions in addition to Swift.

Running the action

There are many ways to run the action. The first way is to use the wsk tool's action invoke command:

The blocking parameter tells the command to wait until the action completes before returning. If you leave it out, then the action is invoked, but you don't get the result as it's in "fire and forget" mode.

Alternatively, you can use curl.

To do this, you make a POST request to{NAMESPACE}/actions/{ACTION}?blocking=true with your API key in the Authorization header. As Basic Auth require the credentials to be Base64 encoded, the easiest way to get the information in the right format is:

You also need your namespace, which has the format of {organisation name}_{space name} as you can see in the fully qualified action name in the output of wsk action list. In my case, this is: 19FT_dev.

We can then use this with our curl command:

As you can see, you get a lot of info back, but the key bit is in the response -> result property:

As you don't want to share your API key with anyone, there are other ways to call this action via HTTP: Web Action and API Gateway. We'll explore these in a separate post.

Something wrong? Viewing the logs

If something goes wrong, the place to look is the logs. To get an ongoing up to date view, open a new terminal window and run this in it:

The works a lot like tail -f. Invoke your action and you'll see the information for it.

Alternatively, to view the last log, read LornaJane's "One-Line Command For Newest OpenWhisk Logs" article.

The command you need is:

That's a bit of mouthful, so put it in a script or alias it.


That's it. Getting started with Swift actions on OpenWhisk is remarkably easy and lots of fun. If you want to poke around a more fully featured app, have a look at my DrinkChooser project.

Keyboard shortcut to resize Finder columns

I like to use Finder in Column mode (⌘+3). i.e. this view:

Column view

One feature of this view is that you can resize all the columns to fit by alt+double clicking on the move handle between each column. There doesn't appear to be a keyboard shortcut for this operation though, so I created one using Keyboard Maestro.

Keyboard Maestro can move the mouse around the screen and click with it which is exactly what I need. There's a "Click at Found Image" action which seemed like it was just what I needed. I took a screenshot of the handle and set up the action to look for it in the topmost window and then discovered that Click at Found Image fails if it finds more than one image. Most of the time there's at least two columns visible in my Finder windows and so this was never going to work. I needed a different solution.

The solution was to use the "For Each" action iterates over a collection and one of the choices for the collection is found images. I have set up this KM macro:

Resize finder columns

The image I am looking for is dragged into the image well and set to be found in the front window, searched from left to right. Once the image is found, a list of actions can be run. In this case, I double click with the alt modifier slightly to the right and down a bit from the top left hand corner of my screenshot of the handle, which automatically right-sizes all the columns in the window for me. As I only need this done one, I break the loop after double clicking the first handle.

Finally, I assigned it to the keyboard shortcut ⌥⌘R so that I can run it whenever I need to.

Right-sized column view

Stand-alone usage of Zend-InputFilter

Any data that you receive needs to be checked and validated. There are number of ways to do this including PHP's filter_var, but I prefer Zend-InputFilter. This is how to use it as a stand-alone component.


Firstly, we install it using Composer:

$ composer require zendframework/zend-inputfilter
$ composer require zendframework/zend-servicemanager

You don't have to have ServiceManager, but it makes working with InputFilter much easier, so it's worth installing.

Create the InputFilter

The easiest way to create an InputFilter is to use the provided Factory class. Let's consider an Author entity that has the properties: author_id, name, biography & date_of_birth. We can create an input filter like this:

use Zend\InputFilter\Factory as InputFilterFactory;

class Author
    protected $author_id;
    protected $name;
    protected $biography;
    protected $date_of_birth;

    // ...

    protected function createInputFilter()
        $factory = new InputFilterFactory();
        $inputFilter = $factory->createInputFilter([
            'author_id' => [
                'required' => true,
                'validators' => [
                    ['name' => 'Uuid'],
            'name' => [
                'required' => true,
                'filters' => [
                    ['name' => 'StringTrim'],
                    ['name' => 'StripTags'],
            'biography' => [
                'required' => false,
                'filters' => [
                    ['name' => 'StringTrim'],
                    ['name' => 'StripTags'],
            'date_of_birth' => [
                'required' => false,
                'validators' => [
                    ['name' => 'Date'],
                        'name' => 'LessThan',
                        'options' => [
                            'max' => date('Y-m-d'),
                            'inclusive' => true,

        return $inputFilter;

The createInputFilter() method takes an associative array where the key is the name of the input and then the value is a specification. There are a number of elements in the specification, but we usually just specify required, filters and validators.

required This can be either true or false. If false, then the validators do not execute, but the filters do.
filters An optional array of Zend-Filters. A filter modifies the supplied data before it is passed to the validators (if any). The filtered data is used by the rest of the application. In this example, we have added two filters: StringTrim & StripTags.
validators An optional array of Zend-Validators. A validator will test the filtered value for the input and fail if the data is not valid. If any validator fails, then the entire InputFilter is invalid.

This particular input filter requires that author_id and name are present, but that biography and date_of_birth are optional. The author_id must be a UUID, the name & biography must not have leading or trailing whitespace or no HTML tags and the date_of_birth, if present, must be a valid date in the past.

Using the InputFilter

To use the InputFilter, we set the data and then call isValid(). This can be done in a validate() method that looks like this:

Use Crell\ApiProblem\ApiProblem;
use Error\Exception\ProblemException;

Class Author
    // ...

     * Create an author
     * @param  array $data
     * @return Author
     * @throws ProblemException
    public static function createAuthor($data)
        $inputFilter = $this->createInputFilter();

        if ($inputFilter->isValid()) {
            return new Author($inputFilter->getValues());

        $problem = new ApiProblem('Validation failed');
        $problem['errors'] = $inputFilter->getMessages();

        throw new ProblemException($problem);

In this case, it's an API, so the data has come from a PUT or POST request. We call setData() to pass the array of data into the InputFilter and then call isValid(). If the data is valid, we can return a newly instantiated Author object that is constructed with the filter data. If the validation fails, then we throw a ProblemException which needs an ApiProblem instance, so we create one for it.

To find out which validators failed, getMessages() provides a nested array which is very useful for passing back to the API client.

As an example, this is what failure looks like:

$ curl -i -X "POST" "http://localhost:8888/authors" \
     -H "Accept: application/json" \
     -H "Content-Type: application/json" \
     -d $'{ "name": "", "author_id": "1234" }'

HTTP/1.1 400 Bad Request
Host: localhost:8888
Connection: close
X-Powered-By: PHP/7.0.14
Content-type: application/problem+json

    "errors": {
        "author_id": {
            "valueNotUuid": "Invalid UUID format"
        "name": {
            "isEmpty": "Value is required and can't be empty"
    "title": "Validation failed",
    "type": "about:blank",
    "status": 400


That's all there is to it. Zend-InputFilter is a very flexible data filter and validator and works really well for APIs, such as those written in Slim.

Rendering problem details in Slim

As I've already noted, in the project I'm currently building, I'm rendering errors in my Slim Framework API using RFC 7807: Problem Details for HTTP APIs via Larry Garfield's ApiProblem component and rka-content-type-renderer.

One place where we need to integrate this approach into Slim is in the error handlers. Let's look at NotFound. To ensure that we return a response in the right format, we need to implement our own NotFound handler:


namespace App\Handler;

use Crell\ApiProblem\ApiProblem;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class NotFound
     * Invoke not found handler
     * @param  ServerRequestInterface $request  The most recent Request object
     * @param  ResponseInterface      $response The most recent Response object
     * @return ResponseInterface
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response)
        $problem = new ApiProblem(
            'Not Found',

        $renderer = new RKA\ContentTypeRenderer\ApiProblemRenderer();
        return $renderer->render($request, $response, $problem);

The NotFound handler must return a Response. To do this, we create a new ApiProblem object with the title "Not Found" and set the type to HTML page that defines the 404 status code. We then instantiate the ApiProblemRenderer and call its render method. The renderer will then return a Response object in either XML or JSON based on the Accept header, with the correct Content-Type header. We then set the status code and return it.

To register our new handler, we use the container. If you're using the skeleton application as your base, then this goes in dependencies.php:


// Error handlers
$container['notFoundHandler'] = function () {
    return new App\Handler\NotFound();

Slim will now use our handler whenever a NotFoundException is raised.

This is it in action:

$ curl -i -H "Accept: application/xml" http://localhost:8888/foo
HTTP/1.1 404 Not Found
Host: localhost:8888
Connection: close
X-Powered-By: PHP/7.0.14
Content-type: application/problem+xml

<?xml version="1.0"?>
  <title>Not Found</title>

We can of course apply this to the other error handers: NotAllowed, Error & PhpError which all follow the same pattern as we have done for NotFound.

Rendering ApiProblem with PSR-7

In the API I'm currently building, I'm rendering errors using RFC 7807: Problem Details for HTTP APIs. As this is a Slim Framework project, it uses PSR-7, so I updated rka-content-type-renderer to support problem.

RFC 7807 defines a standard for sending details of an error in an HTTP response message. It supports both XML and JSON formats. From the RFC, an example response is:

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en

    "type": "",
    "title": "You do not have enough credit.",
    "detail": "Your current balance is 30, but that costs 50.",
    "instance": "/account/12345/msgs/abc",
    "balance": 30,
    "accounts": [

Only title and type are required, though status should also be set. Full information is in the RFC, which is one of the easier ones to read.

In PHP, Larry Garfield has created the crell/ApiProblem component. The code to implement the above message is:

Use Crell\ApiProblem\ApiProblem;

$problem = new ApiProblem("You do not have enough credit.", "");
  ->setDetail("Your current balance is 30, but that costs 50.")

$problem['balance'] = 30;
$problem['accounts'] = array(

$jsonString = $problem->asJson();
$xmlString = problem->asXml();


The only tricky bit is working out if we need to send back JSON or XML. This is called content negotiation and we read the Accept header to find out what the client wants. However, the Accept header has a complicated format with quality levels as you can see from RFC 7321, section 5.3.2. Fortunately, we can use Negotiation by Will Durand to deal with this, which is how rka-content-type-renderer works.

rka-content-type-render now has an ApiProblemRenderer which will read the Accept header and work out the if the client would prefer JSON or XML. If it can't determine, it will default to JSON.

In Expressive or Slim, it's used like this:

use Crell\ApiProblem\ApiProblem;
use RKA\ContentTypeRenderer\ApiProblemRenderer;
use Zend\Diactoros\Response;

$app->get('/', function ($request, $response, $next) {
    $problem = new ApiProblem(

    $renderer = new ApiProblemRenderer();
    return $renderer->render($request, new Response(), $problem);

This is it in action, with an XML accept header:

$ curl -i -H "Accept: application/vnd.akrabat.api+xml" http://localhost:8888/
HTTP/1.1 403 Forbidden
Host: localhost:8888
Connection: close
X-Powered-By: PHP/7.0.14
Content-type: application/problem+xml

<?xml version="1.0"?>

And with a JSON one:

$ curl -i -H "Accept: application/vnd.akrabat.api+json" http://localhost:8888/
HTTP/1.1 403 Forbidden
Host: localhost:8888
Connection: close
X-Powered-By: PHP/7.0.14
Content-type: application/problem+json

    "title": "Unauthorised",
    "type": "",
    "status": 403

Ideally, the client should specify application/problem+json in their accept header, but in practice, I've never seen that happen, which is why rka-content-type-renderer works out the preferred format based on the media types specified.

Making Slack accessible on macOS

I've written before about how I tend to use my Mac via the keyboard as much as possible to minimise pain in my arm.

Possibly the best application on macOS to facilitate this is Shortcat which enables me to point and click at any UI element in any native Mac app. I rely on it a lot and it makes nearly every app I use accessible to me.

Recently, Slack updated their desktop app so that it is now built on top of Electron which broke Shortcat compatibility and made me sad as clicking links in Slack meant getting out the Wacom tablet. However, recently, I discovered a way to re-enable it that may work for other Electron apps too.

There's a switch to Chromium, --force-renderer-accessibility, which turns on accessibility access!

This means that I can enable accessibility integration with Shortcat from the command line using:

This enables Shortcat to work as you can see here:

Accessible Slack

In this image, I have enabled Shortcat (the small text box at the bottom of the window) and then typed "slimphp". Shortcat has highlighted where I can click and by pressing return, I'll select the link I want and it will open in a browser.

Note though, that I’ve found that if I change teams then Shortcat cannot "see" anything until I type something (and then backspace to remove it), after which Shortcat then works for that team. It seems that this only needs to be done once per launch per team though.

I also use Alfred, so I have made a workflow to open Slack with this command line argument which means that I can start Slack in my normal way using Alfred and it's accessibility enabled.

This is the workflow: Run-Slack-with-Accessibility.alfredworkflow. I hope that it helps.

A note on framework performance

A question came up recently wondering why Slim Framework was around 5 times slower than plain PHP.

All frameworks are by definition slower than no-code as there's more going on.

i.e. an index.php of:

    header('Content-Type: application/json');
    echo json_encode(['result' => 1]);

is going to be much faster than a Slim application with this code:

    use \Slim\App;
    include 'vendor/autoload.php';
    $config = include 'Config/Container.php';
    $app = new App($config);
    $app->get('/do-it', function($request, $response){
        return $response->withJson(['result' => 1]);

This is not an apples-to-apples comparison of course as the Slim application is doing a lot more than the plain PHP one. It supports routing so that it can respond to different URLs (even ones with dynamic parameters in that URL) and HTTP methods, along with the ability to handle errors. It also a set of separate classes so that you can replace specific parts of our code with your own if you want it to do something different while still using the rest of our code.

To actually compare the performance of Slim to plain PHP, you need to make the plain PHP version have the same functionality as the Slim version and you'll discover that the performance difference is much smaller. Of course, custom written, application specific code will always be faster than generic code though. Slim is a micro framework, so already is pretty tight (more to do though and something I'm looking at for 4.0), but by definition, it supports more routing features than you may need.

However, you have to write it all that code yourself and, to be honest, parsing requests, sending responses, dealing with URL routing, formatting errors based on the Accept header, and so on are solved problems that don't add any value to your app if you write them yourself. You're much better off concentrating your efforts on the parts of your app that are unique to your app as that's where it brings value.

The definitive resource on this is Paul Jones' articles. Start with A Siege On Benchmarks. This was back in 2009 and somehow he managed to not test Slim! However, even if you take the fastest framework he measured (ZF 0.2!), you can see that at 180.56 requests per second, that's around 4.5 times slower than the baseline PHP of 829.82 requests/second.

Finally, the speed of this code will pale in comparison to the rest of your application's code, especially if you access a database or a web s service!

I'm interested in making Slim the fastest it can be, but the benefits of having a reliable framework that provides good routing middleware functionality out of the box cannot be overstated.

Homestead per-project crib sheet

I wanted a drop-dead simple way to try and replicate a problem someone was having on the Slim forums. I couldn't reproduce with php -S which is my go-to for this sort of thing, so I thought I'd try Homestead.

I had recently listend to a Voices of the Elephpant episode with Taylor Otwell & Joe Ferguson where Joe mentioned that Homestead worked on a per-project basis too. I didn't know this, so tried it out. The docs are fine, but there's a lot there that covers the global installation option when I just want to get up and running on a per-project basis.This is my crib sheet:

1. Create project

We just need a project that uses Composer. You probably have one already. If not, Slim Framework is a good choice!

2. Add Homestead to the project

The make command creates VagrantFile and a Homestead.yaml for configuration.

3. Deal with IP address and hostname

By default, the Homestead vagrant box is set up on with the hostname You can change this in Homestead.yaml.

Add the IP address to /etc/hosts. This only needs to be done once if you don't change the defaults.

All done

We're all done, so we can use vagrant up to run our new website Go to in a browser to see it. To shut down, use vagrant halt or vagrant destroy.

Slim home page

Automatic OCR with Hazel and PDFPen

I have a useful scanner as part of my networked HP printer that will scan directly to a shared directory on my computer. Once there, I want the file to be renamed to the current date and the document OCR'd so that I can search it.

To do this, I use Hazel and PDFPen and this is a note to ensure that I can remember to do it again if I ever need to!

Firstly, rename the file. My scanner names each file with the prefix scan, so the Hazel rule is quite simple:

If all the following conditions are met:
	Name starts with scan

Do the following to the matched file or folder:
	Rename with pattern: [date created][extension]

This is the screenshot:


Having renamed the file, we can use PDFPen's AppleScript support to perform an OCR of the document:

If all the following conditions are met:
	Extension is pdf
	Date Last Modified is after Date Last Matched

Do the following to the matched file or folder:
	Run AppleScript embedded script

The embedded AppleScript is:

tell application "PDFpen"
	open theFile as alias
	tell document 1
		repeat while performing ocr
			delay 1
		end repeat
		delay 1
		close with saving
	end tell
end tell

This is the screenshot of it in Hazel:


That's it. Scanning a document now results in a dated, OCR'd PDF file in my Scans folder.

Using Phive to manage PHPUnit

I recently came across the Phive project and have had a play with it. Phive is part of and is intended to manage development tools such as PHPUnit in preference to using Composer's dev dependencies. The main advantages of Phive are that it uses the phar file of the tool and only keeps one copy of each version rather than downloading a new copy into each project.

How it works

Phive stores one copy of each phar file within ~/.phive/ and then for each project, it creates a symlink. For example, to install PHPUnit into a project, you simply change directory to the root of your project and type:

$ phive install phpunit

This will download the phpunit phar file (if it's not already downloaded) to your ~/.phive directory, It will then create a tools directory in your project with the symlink to phpunit.phar.

$ ls -l tools
total 8
lrwxrwxr-x  1 rob  staff  42  3 Jan 07:34 phpunit -> /Users/rob/.phive/phars/phpunit-5.7.5.phar

You can now run PHPUnit using ./tools/phpunit, however if you would rather install to a different directory, e.g. bin, then use the --target switch:

$ phive install --target bin/ phpunit

The phive.xml file

Phive also creates a phive.xml file to keep track of what it has installed. You should add this file to your git repository and ignore the installed files in tools.

$ echo -e "phpunit" >> tools/.gitignore
$ git add phive.xml tools/.gitignore
$ git commit -m "Add phpunit via Phive"

Other users of the project can now install all the Phive tools using:

$ phive install

Installing for global use

Phive also supports global installation with the -g switch:

$ phive install -g phpunit
Phive 0.6.2 - Copyright (C) 2015-2017 by Arne Blankerts, Sebastian Heuer and Contributors
Copying phpunit-5.7.5.phar to /usr/local/Cellar/php71/7.1.0_11/bin/phpunit

You may need sudo privileges, but for my HomeBrew installation, this wasn't necessary.

GPG signatures required

Note that Phive only works with projects that also release a GPG signature for their tools. This is good security, but currently a significant limitation if you use anything else other than the few tools listed on

The most significant missing tool for me is PHP_CodeSniffer. There's an open issue on their bug tracker but as it's been open for 6 months now, I assume that it isn't important for that project which is frustrating.

As such, this limits the usefulness of Phive for me today, but it's certainly a project to watch.