Azure Functions in Swift: From Code to Azure
Saleh Albuga
Posted on September 17, 2020
This article is part of #ServerlessSeptember. You'll find other helpful articles, detailed tutorials, and videos in this all-things-Serverless content collection. New articles from community members and cloud advocates are published every week from Monday to Thursday through September.
Find out more about how Microsoft Azure enables your Serverless functions at https://docs.microsoft.com/azure/azure-functions/.
Serverless is changing the way we design and implement back ends. Whether you already use Server Side Swift or thinking of a simple way to write lightweight APIs for your iOS app, Swift Azure Functions definitely have a place in your stack!
A couple of months ago, I wrote an article to take your through developing and publishing Azure Functions in Swift using a custom handler. Custom handlers are a new way to support custom runtimes for Azure Function, using an HTTP worker.
The Azure Functions Swift worker is a framework that supports both worker implementations, the classic one and custom handler.
In this article, I'm going to take you through the different options of building and publishing Swift Azure Functions, covering developing on macOS and Linux and deploying as Container Function app or hosting in a Consumption plan.
First Things First
Here is the requirements checklist
Swift 5.2 or later or Xcode 11 or later on macOS
Swift installation: https://swift.org/getting-started/#installing-swiftAzure Functions Core Tools
Swift Functions Tools
Just like Core Tools, Swift Functions Tools (swiftfunc) make Swift functions development easier and much more convenient. It helps in creating projects, functions, running them locally and publishing to Azure.
On macOS, install it from Homebrew đș
brew install salehalbuga/formulae/swift-func
on Linux, clone the repo the tools repo and install
git clone https://github.com/SalehAlbuga/azure-functions-swift-tools
make install
If you already have Swift Functions Tools installed, make sure you have the latest version as the project is being actively updated. on macOS run
brew upgrade salehalbuga/formulae/swift-func
and on Linux, repeat the installation step.
Which way to go?
As mentioned earlier, there are a couple of development and deployment options.
Development Options
Classic worker
In this mode, writing Swift functions is similar to writing JavaScript or Python Azure Functions. You'll use the defined binding types.
Take a look at the function below:
import Foundation
import AzureFunctions
class HttpFunction: Function {
required init() {
super.init()
self.name = "HttpFunction"
self.trigger = HttpRequest(name: "req")
}
override func exec(request: HttpRequest, context: inout Context, callback: @escaping callback) throws {
let res = HttpResponse()
var name: String?
if let data = request.body, let bodyObj: [String: Any] = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
name = bodyObj["name"] as? String
} else {
name = request.query["name"]
}
res.body = "Hello \(name ?? "buddy")!".data(using: .utf8)
return callback(res);
}
}
The trigger and bindings are set in the constructor and the function logic is located in the exec
method. Outputs are set in context
object.
Custom handler
As docs defines them:
Custom handlers are lightweight web servers that receive events from the Functions host. Any language that supports HTTP primitives can implement a custom handler.
Take a look at the function below
import Foundation
import AzureFunctions
import Vapor
class TimerFunction: Function {
required init() {
super.init()
self.name = "TimerFunction"
self.functionJsonBindings =
[
[
"type" : "timerTrigger",
"name" : "myTimer",
"direction" : "in",
"schedule" : "*/5 * * * * *"
]
]
//or
//self.trigger = TimerTrigger(name: "myTimer", schedule: "*/5 * * * * *")
app.post([PathComponent(stringLiteral: name)], use: run(req:))
}
func run(req: Request) -> InvocationResponse {
var res = InvocationResponse()
res.appendLog("Its is time!")
return res
}
}
In this mode, as you can see in the function initializer, you can set the trigger/bindings in JSON (Dictionary) format in functionJsonBindings
property, just like youâd do manually when you create a function.json.
You can also use the frameworkâs defined binding types by setting the other properties (trigger, inputBindings, and outputBindings)
The implmentation currently uses Vapor HTTP server. The framework provides the function invocation Request and Response models used by Azure Functions host when working with customer handlers. The models conform to Content
protocol from Vapor.
All you need to do is to initialize an InvocationResponse
instance and set the outputs and return it!
Deployment options
- Docker Container: where the Swift Functions are deployed in a custom Docker container. Container Functions can be hosted on an App Service Plan or a Premium plan.
Don't worry, the Dockerfile is provided by Swift Function CLI tools when the project is created. You'll only need to build the image :]
- Hosted on a Consumption Plan: here the functions will be deployed to Azure as a zip package, not in a container.
Swift Functions Tools
swiftfunc
is going to be your friend, if youâre familiar with Azure Functions Core Tools then you're there!
- init: create Functions projects
- new: create functions from templates
- run: run a Functions project locally
- publish: Publish to Azure (for hosting on Consumption plans)
Let's write some functions!
Creating a Functions project
In a terminal run the following to create a new project:
swiftfunc init myFunctionApp [-hw]
Note the option
-hw
or--http-worker
to specify that the new project will be created as a Custom Handler project. It's optional. Without it the project will run in the classic worker mode.
Your new project has the following folder structure
It is basically a SwiftPM project with the Swift Azure Functions framework dependency and a couple of extra files for the purpose of it.
cd
into the project folder and create a new HTTP function by running the following:
swiftfunc new http -n helloWorld [-hw]
-hw
option usage is the same as above. Projects created to run in Custom Handler mode, need to have this option passed to the new function command as well
Now you have an HTTP triggered function
Open the project in Xcode (by opening Package.swift) and take a look at the code.
Running the project locally
To run the project:
swiftfunc run
Swift Functions Tools will compile the code, export the project and start the Azure Functions Host for you (as if you were running func start
)
Click on the link from the Host output to navigate to your function
http://localhost:7071/api/helloWorld
Pass a name param in query string or send a POST request with a name param in the body!
Congrats! You created your first Azure Function app in Swift. Letâs deploy that!
Deploying to Azure âïž
This is the fun part where you get to see your code running on Azure! I'm going to take you through both deployment options: hosting on Consumption plan and Container Functions!
For more info about hosting options https://docs.microsoft.com/en-us/azure/azure-functions/functions-scale
Hosting on Consumption Plan
Linux
On Linux, deploying to an Azure Function app hosted on a consumption plan is easy. (macOS requires an extra step described in the next section)
Create a new project in the custom handler mode as you did above
Open Azure Portal (Get a free Azure subscription if you don't have one yet)
Click on Create a resource and search for Function App.
Fill the basic required info, Resource Group, App Name, Region and select Code for Publish.
Selecting a Runtime Stack is required at this point, select Node.js for now. It will be changed later when you deploy.
Click Next
In the Hosting tab, make sure Linux is the selected OS and change Plan type to Consumption (Serverless)
Click Review + create and then Create after the validation is done.
Great, now you have a new Function App hosted in a Consumption Plan!
Next, we need to add an App Setting to the app, to append the directory of the shipped Swift libraries to shared library loader. These are shipped by Swift Functions Tool while preparing the publish package.
From the Configuration blade, click on New application setting and add the following setting:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/site/wwwroot/workers/swift/lib/
That's LD_LIBRARY_PATH
for Name and $LD_LIBRARY_PATH:/home/site/wwwroot/workers/swift/lib/
is the Value
After adding it, click Save
Finally, the fun part (for real this time!). In the project directory run the command below to login to Azure from Azure CLI:
az login
You'll get the following output
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code [0000000] to authenticate.
Navigate to https://microsoft.com/devicelogin, enter the provided code and select your account when prompted.
When Azure CLI finishes loading your subscription(s) info, run the below to publish the app
swiftfunc publish myswiftfunctions
swiftfunc publish
is going to compile, export and publish your Swift Functions project.
When it finishes, Function Core Tools output will have a link to your function, click on it and try it!
macOS
Publishing a project from macOS in this case requires an extra step.
Swift Functions are hosted on Linux on Azure, if you're using a Linux machine for development, then the deployment process is pretty straightforward as you've seen. Because we're shipping the same Linux Swift libraries installed on the machine. (Why? Because of Swift's ABI compatibility status).
But this won't work from macOS as the installed libraries are macOS binaries.
For this section, you will need:
To solve this, I have created a customized VSCode Dev Container!
Dev Containers provide development environments inside containers! In this case, the Swift Functions dev container is going to be the environment that you're going to deploy from.
If you haven't read about Visual Studio Code Development Containers yet, you're missing a lot! Do it now!
I'll take you step-by-step.
As described above, create a Functions project but this time passing the -dc
option as shown below
swiftfunc init myFunctionApp -hw -dc
The
-dc
or--dev-container
option tells swiftfunc to add the dev container folder to your project
Create an HTTP function and open the project folder is VSCode and install the Remote - Containers extension
Restart VSCode if needed and press Command-Shift-P, search for and select Remote-Containers: Reopen in Container
VSCode is going to build the container and connect to it. The Swift Functions dev container is going to have the current project directory content and all required dependencies (Core Tools, Azure CLI, Swift Functions Tools, etc). Once ready, add a New Terminal in VSCode, and publish the project with the same steps described in the previous section (Linux):
- Login from Azure CLI
- Create a Function App on Azure
- Add the
LD_LIBRARY_PATH
App Setting with the this value$LD_LIBRARY_PATH:/home/site/wwwroot/workers/swift/lib/
- Run the swiftfunc publish command!
Container Functions
The other option to deploy Swift Functions is in a Container.
Create your project and functions and then create a Function App on Azure with Publish set to Container and Plan type set to App Service Plan or Premium.
At the this point, there are 2 ways to build the image and deploy it.
First, using the deploy command from Azure Functions Core Tools
The second one is the 'traditional' one, where you'll build the image, push it to a registry and set it in the Function App configuration. Let's go quickly over the steps.
The framework provides you with all you need to deploy a Container Function. The provided Dockerfile is ready to go!
Build the image
docker build -t <dockerHubNameOrRegistryUrl>/myswiftfunctions:v1 .
Push it to a registry of your choice (DockerHub or Azure Container Registry)
bash
`
docker push <dockerHubNameOrRegistryUrl>/myswiftfunctions
You can deploy a prebuilt sample to your Azure account using the link below, if you donât have Docker installed!
https//aka.ms/SwiftFunc-Deploy
Open the Container Settings blade
Choose your image source and enter the image name and tag and click Save
Once the Function App finishes pulling the image and starting the container (you can check the status from the logs in the blade in the screenshot above). You can see your function in the Functions blade
Click on it and navigate to Code + Test blade. From the dropdown list you can see the files of your function, function.json and the function code file (helloWorld.swift in this example)
Click on Get function URL, copy it and try it!
Now you've seen all options of developing and deploying Azure Function in Swift
Amazing! Youâve made it this far! đȘ This project is being actively maintained and updated. For any issues or bug reports, please feel free to file an issue on GitHub.
Donât forget to join the Azure Functions Discord server!
Posted on September 17, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.