Introduction to WebAssembly and Swift at an Advanced Level
\n\nWebAssembly (Wasm) is a low-level binary instruction format designed for execution in the browser and on the server with near-native performance. For Swift developers working with iOS and macOS, Wasm integration opens up opportunities to port high-performance computations (cryptography, image processing, simulations) from the web environment to native applications. In this article, we will explore advanced techniques: direct calling of Wasm functions from Swift, memory management via Wasm linear memory, ABI (Application Binary Interface) optimization, and handling complex data types.
\n\nIt is assumed that you are already familiar with the basics of Swift and have experience compiling C/C++ code to Wasm (e.g., via Emscripten or wasi-sdk). We will focus on the combination of Swift (iOS/macOS) and a compiled Wasm module, using minimal dependencies — only the standard Foundation library and basic Swift capabilities for memory management.
\n\n1. Compiling C/C++ Code to WebAssembly and Preparing the ABI
\n\nThe first step is preparing a Wasm module with a clearly defined ABI. For Swift, it is important that exported functions have flat signatures (only numbers: i32, i64, f32, f64) and do not use complex structures. Below is an example of a C function for calculating the sum of an array:
\n\n// sum.c\n#include <stdint.h>\n\nint32_t sum_array(int32_t* arr, int32_t len) {\n int32_t sum = 0;\n for (int32_t i = 0; i < len; i++) {\n sum += arr[i];\n }\n return sum;\n}\n\n// Function for allocating memory inside Wasm\nint32_t* allocate_array(int32_t size) {\n return (int32_t*)malloc(size * sizeof(int32_t));\n}\n\n\nCompile using wasi-sdk (CLang for Wasm):
\n\nclang \\\n --target=wasm32-wasi \\\n -O3 \\\n -nostartfiles \\\n -Wl,--export=sum_array \\\n -Wl,--export=allocate_array \\\n -Wl,--export=__heap_base \\\n -o sum.wasm sum.c\n\n\nThe --export flag explicitly specifies the exported functions. __heap_base is a standard Wasm symbol indicating the start of the heap, necessary for memory management from Swift.
2. Loading a Wasm Module in Swift and Executing Functions
\n\nTo work with Wasm in Swift on iOS/macOS, use the WebKit framework (WKWebView) or a low-level C API via the JavaScriptCore module. However, for a pure Swift solution without a UI, it is more convenient to use the WasmInterpreter library from SwiftWasm (available via SPM). Let's consider an option using JavaScriptCore — it is built into iOS/macOS.
2.1. Initialization and Loading the Binary File
\n\nimport JavaScriptCore\n\nclass WasmEngine {\n private let context: JSContext\n\n init?(wasmData: Data) {\n guard let ctx = JSContext() else { return nil }\n self.context = ctx\n\n // Create the WebAssembly object\n let wasmObj = ctx.evaluateScript("WebAssembly")\n guard let wasm = wasmObj else { return nil }\n\n // Compile the module\n let compileFunc = wasm.objectForKeyedSubscript("compile")\n guard let compile = compileFunc else { return nil }\n\n let promise = compile.call(withArguments: [wasmData])\n // Handle the promise (simplified for demonstration)\n context.evaluateScript("""\n var wasmModule = null;\n \\(promise?.toString() ?? "").then(m => { wasmModule = m; });\n """)\n }\n\n func callFunction(name: String, args: [JSValue]) -> JSValue? {\n let instance = context.objectForKeyedSubscript("wasmModule")\n let exports = instance?.objectForKeyedSubscript("exports")\n let funcToCall = exports?.objectForKeyedSubscript(name)\n return funcToCall?.call(withArguments: args)\n }\n}\n\n\nImportant: This code is simplified. In a real project, use JSVirtualMachin