Azure Functions in Swift! Purely in Swift!

Overview

Azure Functions for Swift ⚡️

ver cliver Swift Package Manager compatible docs-status Swift version License: MIT

Chat

Write Azure Functions in Swift.

This framework supports the new Azure Functions Custom Handlers (starting from 0.6.0) in addition to the traditional custom worker.

Disclaimer: This is a community open source project, not an official Azure project

Documentation

Deploy a sample project to Azure!

Classic worker sample:

Deploy to Azure

Custom Handler sample:

Deploy to Azure

Examples

A Timer Function (Custom Handler):

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
    }
}

An HTTP Function (Classic Worker):

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);
    } 
}

Getting Started

Installation and Requirements

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

Install the latest Azure Functions Core Tools.

Swift Functions Tools

Just like Core Tools, Swift Functions Tools make Swift functions development easier and much more convenient.

On macOS, you can install it from Homebrew 🍺

brew install salehalbuga/formulae/swift-func

on Linux,

Clone the repo the tools repo

git clone https://github.com/SalehAlbuga/azure-functions-swift-tools

Install

make install

It installs a CLI tool called swiftfunc that can be used to create projects, functions and run them locally.

Creating a new Project/Azure Functions app

Run the init command to create a new Azure Functions application:

swiftfunc init myApp [-hw]

It will create a new app in a new folder, and a folder named functions inside the Sources target where Functions should be (/myApp/Sources/myApp/functions). The project created is a Swift package project with the Azure Functions framework dependency.

Pass -hw or --http-worker option to create the project with the Custom Handler template.

Creating a simple HTTP function

Inside the new directory of your project, run the following to create a new HTTP Function named hello:

swiftfunc new http -n hello [-hw]

The new function file will be created in the following path Sources/myApp/functions/hello.swift.

Similar to the init command, pass -hw or --http-worker option to create the new function with the Custom Handler template.

Running the new Functions App

Run swiftfunc run in the project directory to run your Swift Functions project locally. It will compile the code and start the host for you (as if you were running func host start). The host output should show you the URL of hello function created above. Click on it to run the function and see output!

Deploying to Azure ☁️

There are 2 methods to deploy Swift Functions to Azure

Container Functions

To deploy the Function App in a Container, you can either use the Functions Core Tool func deploy command, where it will build the image, push it to a registry and set it in the destination Function App or you can do that manually as shown below.

Build the image (Dockerfile is provided when the project is created)

docker build -t <imageTag> .

If you're using DockerHub then the tag would be username/imageName:version. If you're using ACR (Azure Container Registry) or any other private registry the tag would be registryURL/imageName:version

Then push it

docker push <imageTag>

In Azure portal, create a new Function App with Docker Container as the Publish option. Under Hosting options make sure Linux is selected as OS.

Once the app is created or in any existing Container Function App, under Platform Features, select Container settings and set the registry and select image you pushed.

You can use the buttons below to deploy prebuilt sample project to your Azure subscription

Deploy to Azure

Custom Handler sample:

Deploy to Azure

Hosting on a Linux Consumption Plan

First, you need to set the following App Setting in the Function App on Azure. LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/site/wwwroot/workers/swift/lib/

Then depending if you're developing on a Linux machine or a Mac:

Linux

Login to your Azure account from Azure CLI

az login

When Azure CLI finishes loading your subscription(s) info, run:

swiftfunc publish myswiftfunctions

Swift Function Tools publish command is going to compile, export and publish your Swift Functions project.

macOS

Publishing to a Function App in a Linux Consumption Plan from macOS requires the app to be build in a Linux container first, to do that you can use VSCode Dev Containers. The project needs to be created with the -dc or --dev-container option to have the Swift Function Dev Container added (or you can create a new one and copy the .devcontainer folder to your project). swiftfunc init myFunctionApp -hw -dc

Reopen the folder in dev container (Command-Shift-P, search for and select Remote-Containers: Reopen in Container)

Once the dev container is ready, follow the same Linux steps above to publish the app!

Bindings

Azure Functions offer a variety of Bindings and Triggers

The trigger, input bindings and output bindings of a Function are set in its initializer. Azure Functions in Swift must subclass the Function class from the framework.

Custom Handler (HTTP Worker)

When using the Custom Handler mode you can use all Azure Functions bindings and triggers by setting the functionJsonBindings property to the JSON config of the bindings/triggers in Azure Functions docs. You can also use the framework supported Trigger/Binding types listed below.

Traditional Worker (Classic)

Currently the following are supported by this mode. More bindings will be implemented and many improvements will be made in the future.

Swift Type Azure Functions Binding Direction
HttpRequest HTTP Trigger in
HttpResponse Output HTTP Response out
TimerTrigger Timer Trigger in
Message datatype String (binding defined by Table in constructor) Input and Ouput Table in, out
Message datatype String (binding defined by Queue in constructor) Output Queue Message out
Message datatype String (binding defined by Queue in constructor) Queue Trigger in
Blob (the blob data prob is either String or Data) Input Blob in
String or Data Output Blob out
Blob Blob Trigger in
ServiceBusMessage Service Bus Output Message out
ServiceBusMessage Service Bus Trigger in

Custom Handler (HTTP Worker)

import AzureFunctions
import Vapor

class QueueFunction: Function {

    required init() {
        super.init()
        self.name = "QueueFunction"
        self.functionJsonBindings = [
                [
                    "connection" : "AzureWebJobsStorage",
                    "type" : "queueTrigger",
                    "name" : "myQueueTrigger",
                    "queueName" : "myqueue",
                    "direction" : "in"
                ]
            ]
        // or
        //self.trigger = Queue(name: "myQueueTrigger", queueName: "myqueue", connection: "AzureWebJobsStorage")
        
        app.post([PathComponent(stringLiteral: name)], use: run(req:))
    }
    
    func run(req: Request) -> InvocationResponse {
        ...

Traditional Worker (Classic)

import AzureFunctions

class HttpFunction: Function {
    
    required init() {
        super.init()
        self.name = "HttpFunction"
        self.trigger = HttpRequest(name: "req")
        self.inputBindings = [Blob(name: "fileInput", path: "container/myBlob.json", connection: "AzureWebJobsStorage")]
        self.outputBindings = [Queue(name: "queueOutput", queueName: "myQueue", connection: "AzureWebJobsStorage")]
    }
    
    override func exec(request: HttpRequest, context: inout Context, callback: @escaping callback) throws {
   ...

Writing Swift Functions

Traditional Worker (Classic)

Based on your Function's trigger type the worker will call the appropriate exec overload. For instance, if the Function is timer-triggered, then the worker will call

exec(timer:context:callback:)

If it was an HTTP-triggered one:

exec(request:context:callback:)

You can see the list of available overloads in Xcode.

Input and Output bindings are available in the context as Dictionaries, where you can access/set the values using the binding names specified in the constructor. For example:

let tableVal = context.inputBindings["myTableInput"]
context.outputBindings["myQueueOutput"] = "new item!"

Custom Handler (HTTP Worker)

The framework uses Vapor 4.0 HTTP server. The Function class has the app property, thats the Vapor app instance you can use to register your functions's HTTP route.

class myFunction: Function {
    
    required init() {
        super.init()
        self.name = "myFunction"
        self.functionJsonBindings = [
            [
                "connection" : "AzureWebJobsStorage",
                "type" : "queueTrigger",
                "name" : "myQueueTrigger",
                "queueName" : "myqueue",
                "direction" : "in"
            ]
        ]
        
        app.post([PathComponent(stringLiteral: name)], use: run(req:))
    }
    
    func run(req: Request) -> InvocationResponse {
        var res = InvocationResponse()
        if let payload = try? req.content.decode(InvocationRequest.self) {
            res.appendLog("Got \\(payload.Data?["myQueueTrigger"] ?? "")")
        }
        return res
    }
}

The framework also provides the function invocation Request and Response models needed for Azure Function host, which conform to Content protocol from Vapor, along with helper methods.

Invocation Request:

/// Trigger/Bindings data (values).
var data: [String:AnyCodable]?
/// Trigger/Bindings metadata.
var metadata: [String:AnyCodable]?

Invocation Request:

/// Output bindings values dictionary
var outputs: [String:AnyCodable]?
/// Functions logs array. These will be logged when the Function is executed
var logs: [String] = []
/// The $return binding value
var returnValue: AnyCodable?

Framework Updates

As the framework is being actively updated, update the framework and the tools if you're having any issues or want to have the latest features and improvements.

To update the framework:

swift package update

To update the tools on macOS

brew upgrade salehalbuga/formulae/swift-func

on Linux

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

Storage Connections and other settings

In the generated main.swift you can define your debug AzureWebJobsStorage and optionally any other connections/environment vars. Additionally, you can change the default Extension Bundle id and version.

//
//  main.swift
//  
//
//  Auto Generated by SwiftFunctionsSDK
//
//  Only set env vars or register/remove Functions. Do Not modify/add other code
//

import AzureFunctions

let registry = FunctionRegistry()

registry.AzureWebJobsStorage = "yourConnection" //Remove before deploying. Do not commit or push any Storage Account keys
registry.EnvironmentVariables = ["queueStorageConnection": "otherConnection"]

// Optionally you can change the default ExtensionBundleId and version 
registry.ExtensionBundleId = "Microsoft.Azure.Functions.ExtensionBundle"
registry.ExtensionBundleVersion = "[1.*, 2.0.0)"

registry.register(hello.self)
...

Be sure not to commit any debugging Storage Account keys to a repo

Logging

Traditional Worker (Classic)

You can log using the log method in context object

context.log(_)

Custom Handler (HTTP Worker)

Logs are returned in the InvocationResponse obj. You can append logs:

res.appendLog(_)

Code Execution Note

Traditional Worker (Classic)

When your Function is done executing the logic you should call the provided callback passing the $return output binding value or with true if none.

callback(res)
Comments
  • Support for Windows or Linux?

    Support for Windows or Linux?

    Do you have plans to support this project on Windows (probably via WSL) or Linux? I'd be interested in helping work through the issues and getting it to work. Thank you for building this!

    opened by panesofglass 3
  • Tag releases according to SemVer

    Tag releases according to SemVer

    From the project's list of releases, it looks like new versions are being tagged with a v prefix (e.g. v0.1.5). However, Swift Package Manager expects tagged releases to correspond to a valid Semantic Version number.

    According to the Semantic Versioning specification:

    Is “v1.2.3” a semantic version?

    No, “v1.2.3” is not a semantic version. However, prefixing a semantic version with a “v” is a common way (in English) to indicate it is a version number. Abbreviating “version” as “v” is often seen with version control. Example: git tag v1.2.3 -m "Release version 1.2.3", in which case “v1.2.3” is a tag name and the semantic version is “1.2.3”.

    Although Swift Package Manager is currently able to resolve tags with v prefixes for exact requirements, I've seen issues crop up in other libraries (e.g. https://github.com/groue/GRDB.swift/issues/364).

    To maintain compatibility with existing versions, I'd recommend making this change prospectively, such that all future versions use SemVer tags.

    opened by mattt 2
  • Getting

    Getting "Item has already been added. Key in dictionary: 'TERM' Key being added: 'TERM'" when running the functions project

    Getting this error when running the functions project (swiftfunc run): Item has already been added. Key in dictionary: 'TERM' Key being added: 'TERM'

    This issue happens with the latest V2 Core Tools version. I fixed in the latest version of Swift Functions CLI Tools. If you had this issue please upgrade. brew upgrade salehalbuga/formulae/swift-func

    Thanks to @helje5 for reporting the issue! 😄

    bug 
    opened by SalehAlbuga 1
  • 0.6.2

    0.6.2

    • An option to set exec path in the exported host/WorkerConf files to the default value "/home/site/wwwroot/.."
    • Publish command was added to CLI tools to publish & run Swift functions in a consumption plan
    • Fixing an error caused by breaking change in a dependency
    opened by SalehAlbuga 0
  • Function code exceptions handling + refactor + handling json strings + remove unnecessary logging

    Function code exceptions handling + refactor + handling json strings + remove unnecessary logging

    • Function code exception don't crash the worker anymore
    • Internal names refactor
    • Handling JSON strings in Service Bus Message
    • Remove unnecessary logging
    • Allowing running in debug with unregistered functions
    • Bug fixes
    opened by SalehAlbuga 0
  • Supporting custom env vars for connections + Service Bus binding bug fixes

    Supporting custom env vars for connections + Service Bus binding bug fixes

    -Adding support for custom environment variables to be able to add multiple connections -ServiceBus binding bug fixes: --Add missing overload exec(sbMessage:context:callback:) --Rename data to message in ServiceBusMessage type

    opened by SalehAlbuga 0
  • attribute using property wrappers

    attribute using property wrappers

    Unlike C#, Swift does not support Attributes to mark bindings types and directions

    You should be able to use the new property wrappers in Swift 5.1 to annotate things.

    enhancement 
    opened by helje5 1
Releases(0.6.3-beta)
  • 0.6.3-beta(Sep 19, 2020)

  • 0.6.2(Aug 19, 2020)

    • An option to set exec path in the exported host/WorkerConf files to the default value "/home/site/wwwroot/.."
    • Publish command was added to CLI tools to publish & run Swift functions in a consumption plan 🎉⚡️
    • Fixing an error caused by breaking change in a dependency
    Source code(tar.gz)
    Source code(zip)
  • 0.6.1(Jun 25, 2020)

  • 0.6.0(Jun 8, 2020)

    • Update swift-grpc to alpha.12
    • Support Azure Functions Custom Handlers (HTTP worker) using Vapor with predefined InvocationRequest & InvocationResponse object.
    • Support specifying custom Extension bundle id and version
    • Support providing bindings using a dictionary (just like function.json) instead the framework types only. Mixing them also supported.
    • Update generated Dockerfiles to run projects in Functions runtime V3.
    • Bump version to 0.6.0
    Source code(tar.gz)
    Source code(zip)
  • 0.6.0-beta.3(Jun 7, 2020)

    • Warn when running locally with unregistered functions (as it's not supported when exporting functions)
    • Minor syntax refactoring
    • Support specifying custom Extension bundle id and version
    • Vapor 4.8.0
    • Doc markup
    Source code(tar.gz)
    Source code(zip)
  • 0.6.0-beta.2(Jun 5, 2020)

  • v0.1.5(May 30, 2020)

  • v0.1.4(Dec 26, 2019)

  • v0.1.3(Dec 7, 2019)

    • Bindings mapping issue fix that affected blob output and other bindings.
    • Auto-generating HttpResponse binding when it's missing
    • Bug fixes
    Source code(tar.gz)
    Source code(zip)
  • v0.1.2(Nov 30, 2019)

  • v0.1.1(Nov 30, 2019)

    • Function code exceptions don't crash the worker anymore
    • Worker errors handling
    • Internal names refactor
    • Handling JSON strings in Service Bus Message
    • Remove unnecessary logging
    • Allowing running in debug with unregistered functions
    • Bug fixes
    Source code(tar.gz)
    Source code(zip)
  • v0.1.0(Nov 29, 2019)

Owner
Saleh Albuga
OSS Eng @ Microsoft. | iOS Developer originally. Swift (& Server-Side Swift), & NodeJs.
Saleh Albuga
RadioTimeKit - The Swift SDK for TuneIn RadioTimeKit is a Swift package to use the TuneIn API.

RadioTimeKit - The Swift SDK for TuneIn RadioTimeKit is a Swift package to use the TuneIn API. The goal for development was to have a Swift SDK to get

Frank Gregor 2 Jun 20, 2022
This is swift project example to connect VNPTSmartCA SDK using Swift Language.

Example source code to integrate with VNPTSmartCA iOS SDK To run the example project, clone repository, and run pod install Requirements Installation

null 1 Feb 14, 2022
Home-assistant-swift-sdk - Used to integrate the Home Assistant APIs with your Swift-based apps.

home-assistant-swift-sdk This open-source library allows you to interact with a Home Assistant instance in your Swift-based (e.g., iOS, macOS, etc.) a

Alexander Golden 0 Dec 31, 2021
Official Appwrite Swift SDK 🦅🍎

Appwrite Swift SDK This SDK is compatible with Appwrite server version 0.11.x. For older versions, please check previous releases. This is the Swift S

Appwrite 27 Dec 25, 2022
Swift SDK for Blockfrost.io API

Swift5 API client for Blockfrost Swift 5 SDK for Blockfrost.io API. Installation • Usage • API Endpoints Installation Swift package manager dependenci

blockfrost.io 10 Dec 24, 2022
WalletConnect Swift SDK v2

Wallet Connect v.2 - Swift Swift implementation of WalletConnect v.2 protocol for native iOS applications. Requirements iOS 13 XCode 13 Swift 5 Usage

WalletConnect Labs 16 Mar 30, 2022
Swift framework for authenticating with the Spotify API

SpotifyLogin SpotifyLogin is a Swift 5 Framework for authenticating with the Spotify API. Usage of this framework is bound under the Developer Terms o

Spotify 344 Jan 4, 2023
⚡️ A fully-featured and blazing-fast Swift API client to interact with Algolia.

The perfect starting point to integrate Algolia within your Swift project Documentation • Community Forum • Stack Overflow • Report a bug • FAQ • Supp

Algolia 192 Dec 23, 2022
A library for creating Stream Deck plugins in Swift.

StreamDeck A library for creating Stream Deck plugins in Swift. Usage Your plugin class should inherit from StreamDeckPlugin, which handles the WebSoc

Emory Dunn 15 Jan 2, 2023
iOS Mobile Automation With Swift

ios-mobile-automation Since I could hardly find any resources on iOS automation

Ahmethan G. 0 Dec 19, 2021
Simple proxy in Swift for converting between HTTP and API Gateway Lambda payloads

SwiftLambdaProxy A simple proxy that can convert HTTP requests to Lambda API Gat

Jānis Kiršteins 1 Jan 3, 2022
JustTrivia - Trivia Application Created in Swift

JustTrivia - Trivia Application Created in Swift Usage Please use a small number

null 0 Dec 30, 2021
Task-Manager - Task Manager App With Swift

Task-Manager It's typical task manager where user can assign the importance, def

Andrey Buchevskiy 1 Jan 10, 2022
📲 The curated list of iOS Developer interview questions and answers, Swift & Objective-C

Awesome iOS interview questions and answers ?? Get started by picking interview's language and start preparing right now Install the app Prepare for t

Dasha Korneichuk 996 Dec 28, 2022
A very simple way to implement Backbone.js style custom event listeners and triggering in Swift for iOS development.

Swift Custom Events A very simple way to implement Backbone.js style custom event listeners and triggering in Swift for iOS development. This provides

Stephen Haney 98 Dec 25, 2022
MpesaSDK - Swift SDK for the M-Pesa API (Mozambique)

M-Pesa SDK Swift package for M-Pesa API (Mozambique) Ready Methods/APIs C2B B2B

Algy Ali 16 Jul 29, 2022
GoraInterviewTask - Gora Interview Task with swift

goraInterviewTask Implements some endpoints from https://jsonplaceholder.typicod

null 0 Jan 20, 2022
Swiftcord - Swift wrapper for Discord's API. Maintained fork of Azoy's Sword

Swiftcord - A Discord Library for Swift Requirements macOS, Linux, iOS, watchOS,

Sketch 37 Nov 30, 2022
MbientLab 2 Feb 5, 2022