Azure Functions in Swift: From Code to Azure
Tags: azure, serverless, swift, cloud
Published: 2020-09-17 22:00:46

>This article is part of [#ServerlessSeptember](https://aka.ms/ServerlessSeptember2020). 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/](https://docs.microsoft.com/azure/azure-functions/?WT.mc_id=servsept20-devto-cxaall). 

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](https://medium.com/macoclock/write-publish-azure-function-in-swift-using-custom-handlers-35d937582db7) to take your through developing and publishing Azure Functions in Swift using a custom handler. [Custom handlers](https://docs.microsoft.com/en-us/azure/azure-functions/functions-custom-handlers) are a new way to support custom runtimes for Azure Function, using an HTTP worker.

The [Azure Functions Swift worker](https://github.com/SalehAlbuga/azure-functions-swift) 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-swift

* Azure [Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local#install-the-azure-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](https://github.com/SalehAlbuga/azure-functions-swift#traditional-worker-classic). 
Take a look at the function below:

```swift
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](https://docs.microsoft.com/en-us/azure/azure-functions/functions-custom-handlers) 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

```swift
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:

```bash
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

![project folder structure](https://dev-to-uploads.s3.amazonaws.com/i/r9axqcg40ai3rxvvc43o.png)

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:

```bash
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

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/51bsjmfpk6kba52jks1f.png)

Open the project in Xcode (by opening Package.swift) and take a look at the code. 

# Running the project locally

To run the project:

```bash
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](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](https://azure.microsoft.com/en-us/free/) if you don't have one yet) 

Click on **Create a resource** and search for **Function App**.

![New Function App - Basics Tab](https://dev-to-uploads.s3.amazonaws.com/i/3struxukcnto8psakn9j.png)

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](https://dev-to-uploads.s3.amazonaws.com/i/h5oaqtey2pl4ui0zfg45.png)

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:

```bash
az login
```

You'll get the following output 

```bash
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](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

```bash
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:
* [Docker Desktop](https://www.docker.com/products/docker-desktop)
* [VSCode](https://code.visualstudio.com)

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](https://code.visualstudio.com/docs/remote/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

```bash
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](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension 

![Remote - Containers Extension](https://dev-to-uploads.s3.amazonaws.com/i/vlspodwmx4ee05bkdw21.png)

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](https://dev-to-uploads.s3.amazonaws.com/i/6wiz2yowq26dqfwwzg1q.png)

![Function App - Hosting Tab](https://dev-to-uploads.s3.amazonaws.com/i/a9nruqhzrd82ahbtodrl.png)

At the this point, there are 2 ways to build the image and deploy it.
First, using the [deploy command](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=macos%2Ccsharp%2Cbash#deploy-custom-container) 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

```bash
docker build -t <dockerHubNameOrRegistryUrl>/myswiftfunctions:v1 .
```

Push it to a registry of your choice (DockerHub or [Azure Container Registry](https://azure.microsoft.com/en-us/services/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](https://dev-to-uploads.s3.amazonaws.com/i/3jy19armacgwhrlfq78i.png)

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](https://dev-to-uploads.s3.amazonaws.com/i/31p35dxh200yd0uudamb.png)

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](https://dev-to-uploads.s3.amazonaws.com/i/dhaps2aza8pi40ftjt5w.png)

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](https://github.com/SalehAlbuga/azure-functions-swift). 
Don’t forget to join the Azure Functions [Discord server](http://discord.gg/6rDzSuM)!