Introduction: Why iOS/macOS developers need WebAssembly?
WebAssembly (Wasm) is a binary instruction format that was originally created for browsers, but quickly found its use in native development. For Swift developers working on iOS and macOS, WebAssembly integration opens up unique opportunities: you can reuse high-performance code written in C/C++, Rust, or Go without rewriting it in Swift, and run it at near-native speed. This is especially relevant for working with graphics, cryptography, simulations, and complex algorithms.
In this article, we will cover how to compile code into Wasm, integrate it into a Swift project for iOS/macOS, pass data, and handle errors. All examples are written in Swift 5.9+ and are targeted at Xcode 15+.
1. Compiling code into WebAssembly for a Swift project
The first step is to obtain a Wasm module. The most popular way is to compile C/C++ code using Emscripten or WasmEdge. Let's look at an example in C.
Create a file math_ops.c:
// math_ops.c#include <emscripten.h>
EMSCRIPTEN_KEEPALIVEint add(int a, int b) { return a + b;}
EMSCRIPTEN_KEEPALIVEdouble multiply(double a, double b) { return a * b;}
EMSCRIPTEN_KEEPALIVEint factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1);}Compile it into Wasm using Emscripten (install via Homebrew: brew install emscripten):
emcc math_ops.c -o math_ops.wasm -s WASM=1 -s EXPORTED_FUNCTIONS='["_add", "_multiply", "_factorial"]' -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -O3As a result, you will get the file math_ops.wasm and math_ops.js (the js wrapper won't be needed; we will work directly with Wasm).
2. Integrating the Wasm module into Swift (iOS/macOS)
To work with WebAssembly in native Swift, we will use the WasmKit or SwiftWasm framework. We recommend WasmKit — it supports iOS 15+ and macOS 12+.
2.1. Installing WasmKit via Swift Package Manager
Add the dependency in Package.swift or via Xcode: File → Add Packages → https://github.com/swiftwasm/WasmKit.
2.2. Loading and executing the Wasm module
Create a Swift file WasmEngine.swift:
import WasmKitimport Foundation
class WasmEngine { private var instance: Instance? private var store: Store? /// Loads the Wasm module from a file func loadModule(from url: URL) throws { let data = try Data(contentsOf: url) let module = try Module(data: data) let engine = Engine() store = Store(engine: engine) let imports = Imports() // Here you can add function imports from Swift (see section 3) instance = try Instance(module: module, imports: imports, store: store!) } /// Calls an exported function func callFunction(_ name: String, args: [Value]) throws -> [Value] { guard let instance = instance else { throw WasmError.moduleNotLoaded } let function = instance.exports[name] as! Function return try function(args) }}
enum WasmError: Error { case moduleNotLoaded case functionNotFound}2.3. Usage in a SwiftUI application
import SwiftUI
struct ContentView: View { @State private var result: String = "0" private let engine = WasmEngine() var body: some View { VStack(spacing: 20) { Text("Result: \\(result)") .font(.title) Button("Calculate factorial of 5") { do { let wasmURL = Bundle.main.url(forResource: "math_ops", withExtension: "wasm")!