Swift is one of the fastest growing programming languages with developers.
Swift has reached a Top 15 ranking faster than any other language we have tracked.
Created for building mobile applications, the language is now popular with backend development.
But for Swift developers beginning to build backend applications, they now find themselves having to manage computing infrastructure to run their applications in the cloud.
Enter serverless cloud platforms… ☁️☁️☁️
These services allow developers to push code, rather than VMs, into the cloud. The platforms allow you to connect external event sources like API requests or message queues to functions in your code. As events occur, your code is instantiated and executed to process each request. Developers are only billed for the milliseconds needed to process each request.
Serverless platforms let you run applications in the cloud without worrying about infrastructure. 😎
Apache OpenWhisk is currently the only serverless platform to support Swift language functions.
Let’s have a look at how you can use Swift with OpenWhisk before diving into how the platform implements this feature to give us some tips and tricks for Swift on OpenWhisk…
Swift On OpenWhisk
Using the CLI
Create a Swift file with the following source code in.
1 2 3 4 5 6 7
Swift actions must consume and return a dictionary. The dictionary passed as the function argument will contain event parameters. Returned dictionary values must support serialisation to JSON.
Create and invoke a new OpenWhisk action using the command-line utility.
1 2 3 4 5 6 7 8 9 10
result flag will only show the action output in the console rather than the full API response.
The source file must have a function called
main. Each invocation executes this function. The function name to invoke can be overridden as shown below.
1 2 3
1 2 3 4 5 6
Choosing the runtime for the action can be set using the
kind flag. If the source file has the
.swift extension this will be automatically set to
Using the Serverless Framework
The Serverless Framework is a popular open-source framework for building serverless applications. It provides CLI tools and a workflow for managing serverless development.
Developers use a YAML file to define their application functions, events and resources. The framework handles deploying the application to their serverless provider.
Having started as a tool for AWS Lambda, the framework recently added multi-provider support. It now also works with Apache OpenWhisk, Azure Functions and Google Cloud Functions.
Let’s look at an example of using this framework to create a new OpenWhisk Swift application. Using a provider name and runtime, the framework can scaffold a new serverless application.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
openwhisk-swift directory contains the boilerplate application ready to deploy. It includes a sample action (
ping.swift) and the configuration file (
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11 12
Install the provider plugin using
npm install and type
serverless deploy to deploy this application.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
For more information on using the Serverless Framework with OpenWhisk, please see this documentation: https://serverless.com/framework/docs/providers/openwhisk/.
How It Works
Swift actions in OpenWhisk can be created from Swift source files, rather than binaries, meaning the platform must run this compilation step.
Swift on Docker
Images for each of the OpenWhisk runtime environments are available on Docker Hub. Creating containers from these images allows you to explore the Swift runtime environment.
For more information on the API exposed by runtime containers to initialise and invoke actions, please see this blog post.
Building Swift actions
Swift runtime environments has a template package available in the
All the Swift sources files provided by the user are written into that package’s
main.swift file. The following source code is appended to
main.swift to support execution within the OpenWhisk runtime. It parses the input parameters from the environment, invokes the registered function name and returns the computation response as a JSON string.
Dependencies for the following packages are included in the existing
Package.swift file. These packages can be used from the action source code without further configuration.
1 2 3 4 5 6 7 8 9 10
During initialisation, the Swift build process is executed to generate the action binary.
This artifact (
/swift3Action/spm-build/.build/release/Action) will be executed for each invocation received by the platform.
Containers used for action runtimes are re-used with subsequent requests. This means any initialisation cost, e.g. compiling Swift source code, will only be incurred once per runtime container.
Runtime containers are evicted from the cache ten minutes after the last activation. Future invocations for that runtime will use a new container and have to run the initialisation step again.
Additionally, runtimes containers cannot process concurrent requests. If a request arrives before the previous one has finished processing, a new environment will need to be initialised.
Improving cold start time
Swift build times are not known for being fast.
Build time is included in the request processing time for each new runtime container provisioned.
In an attempt to reduce this delay, OpenWhisk runs the minimum build steps necessary to compile the source code, rather than a full release build.
During the Docker build for the Swift runtime image, the full release build is executed for the empty action package. This generates object files and other intermediary build outputs which are stored in the build cache.
Logs from the build process are parsed to retrieve the individual compilation and linking commands for the
main.swift file. These commands are written into a new shell script (
When a new Swift runtime container is initialised, the source code for the action is written into the
main.swift file. Rather than running a full re-build, the runtime just executes the shell script containing the compilation and linking steps. This re-uses the cached build objects and reduces compilation time.
Modifying package dependencies
Swift packages uses a manifest file (
Packages.swift) to list package dependencies. Dependencies are automatically downloaded and compiling during the package build process.
The Swift environment used by OpenWhisk uses the package manifest shown above. This includes dependencies for JSON and HTTP libraries.
Swift actions can be created from Swift source code or zip files. Zip files are expanded into the package directory (
/swift3action/spm-build) before initialisation.
If the zip file contains a new package manifest, this will overwrite the default manifest in the environment.
1 2 3 4 5 6 7 8 9 10 11
Running a full build will download new package dependencies and make them available for use in our action.
OpenWhisk uses a shell script (
swiftbuildandlink.sh) to manage the build process during initialisation. This defaults to only running the compiler and linker commands for the
main.swift file, rather than a full release build.
Including a replacement
swiftbuildandlink.sh file in the zip file will allow us to modify the build command used, e.g.
swift build -v -c release.
1 2 3 4
Downloading additional packages will add a significant delay to initialising new runtime containers.
If this is an issue, let’s look at skipping the compile step entirely…
Compiling binaries locally
Swift actions execute a binary that is available at the following path:
The runtime uses the existence of this binary to control running the build process. If the file does not exist, the build step is executed. It ensures that compilation is only ran once per runtime container.
This also means that developers can include a locally compiled Swift binary inside the action zip file. During initialisation, the existence of this file will stop the build process from running.
If you want to use lots of additional Swift packages, the compile time penalty won’t have to be incurred during action invocations. This will dramatically speed up invocation times for “cold” actions.
Binaries must be compatible with the platform environment they are being executed within. OpenWhisk uses Swift 3.0.2 on Linux.
OpenWhisk publishes the runtime environments as Docker images. Using containers from these images to compile our action binaries will ensure the binary is compatible.
These instructions show you how to compile your source code into a compatible platform binary.
1 2 3 4 5 6 7 8 9 10 11 12 13
action.zip file can then be deployed as a new action using the following command-line.
Swift is one of the fastest growing programming languages with developers. People are increasingly using it to develop backend APIs and services. Being able to use Swift on serverless cloud platforms means developers can focus on writing code, rather than managing infrastructure.
Apache OpenWhisk, an open-source serverless platform, supports Swift as a first-class language. Developers can provide Swift source code and have the platform execute these functions in response to external events.
Because OpenWhisk is open-source, we can discover how the platform executes the code using the Swift runtime. Understanding this process allows us to modify the build step to use additional Swift packages within our actions. We can also improve performance by skipping the compilation stage entirely by providing a native binary.