DEV Community

Saleh Albuga
Saleh Albuga

Posted on • Updated on

Azure Functions in Swift: From Code to Azure

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

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
Enter fullscreen mode Exit fullscreen mode

on Linux, clone the repo the tools repo and install

git clone https://github.com/SalehAlbuga/azure-functions-swift-tools
Enter fullscreen mode Exit fullscreen mode
make install
Enter fullscreen mode Exit fullscreen mode

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);
    } 
}
Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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

project 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]
Enter fullscreen mode Exit fullscreen mode

-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

Alt Text

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
Enter fullscreen mode Exit fullscreen mode

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.

New Function App - Basics Tab

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

New Function App - Hosting Tab

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/
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

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.

Function App - Basics Tab

Function App - Hosting Tab

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 .
Enter fullscreen mode Exit fullscreen mode

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

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

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)

Code

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!

Top comments (0)