yolk

YolkModule

The protocol every native module must conform to. In practice, you implement the protocol generated by codegen — you rarely implement YolkModule directly.

public protocol YolkModule: Sendable {
    static var moduleName: String { get }
    func handle(method: String, args: [YolkValue]) async throws -> YolkValue
}

Requirements

moduleName

The name TypeScript uses to reach this module. Must match the name used when constructing the generated proxy:

static var moduleName: String { "Storage" }

Codegen sets this automatically via the generated extension.

handle(method:args:)

Dispatch a method call from JS. Receives the method name and arguments as [YolkValue]. Returns a single YolkValue.

Codegen generates a handle implementation that switches on the method name, extracts and validates arguments, calls the domain method, and wraps the return value. You do not write handle yourself.

Implementing a module

The typical pattern is to conform to the generated protocol (e.g. StorageModule) on a Swift actor:

actor AppStorageModule: StorageModule {
    private var store: [String: String] = [:]

    func get(key: String) async throws -> String? {
        store[key]
    }

    func set(key: String, value: String) async throws {
        store[key] = value
    }

    func delete(key: String) async throws {
        store.removeValue(forKey: key)
    }
}

YolkError

Errors thrown from module methods are propagated as Promise rejections on the TypeScript side.

public enum YolkError: Error, Sendable {
    case moduleNotFound(String)
    case methodFailed(module: String, method: String, underlying: Error)
    case invalidArgs(String)
    case runtimeError(String)
    case jsException(String)
}