SwiftStockfish¶
A Swift Package Manager wrapper around the Stockfish
chess engine: a clean StockfishEngine UCI API plus a version-aware NNUE network
manager.
On Apple platforms the engine links a prebuilt, multi-arch
Stockfish.xcframework; on Linux and Android the same Stockfish source is
compiled from source. Either way a small C++ bridge drives Stockfish's UCI loop
over an in-process queue, and the Swift surface is identical on every platform.
- Wraps Stockfish source version 19 (
StockfishNetworks.stockfishVersion). - GPL-3.0 — this package ships and links Stockfish, so the whole package is a GPL-3.0 work (see Licensing).
Components¶
| Type | Role |
|---|---|
StockfishEngine |
a live engine you talk to in UCI — send(_:) commands, read the output AsyncStream |
StockfishNetworkLoader |
downloads, verifies (SHA-256), and prunes the NNUE evaluation networks |
Requirements¶
Run the loader before creating the engine
Stockfish verifies its NNUE nets on the first go/ucinewgame and calls
exit(EXIT_FAILURE) if one is missing or invalid — terminating the
entire host process, not a catchable Swift error.
StockfishEngine.init? preflights the nets and returns nil instead of
letting that happen, but the preflight can only pass if the directory is
already correct — so always run StockfishNetworkLoader.ensure(in:) (and
await it) before StockfishEngine(networkDirectory:). Raw CStockfish
consumers get no preflight.
One engine per process
The bridge enforces the single-instance rule with a lifecycle gate: creating a
second StockfishEngine(...) blocks the calling thread until the first is
fully torn down. Never create or tear down an engine on the main thread/actor —
the create can block and shutdown() joins threads. Always shutdown() or
release the first engine before creating another; a leaked engine hangs the next
create forever.
Quick start¶
import Foundation
import SwiftStockfish
// 1. Ensure the NNUE nets exist BEFORE the engine is created.
let dir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
.appendingPathComponent("stockfish-nets")
try await StockfishNetworkLoader().ensure(in: dir) { p in
print("\(p.file): \(p.bytesDownloaded)/\(p.totalBytes)")
}
// 2. Create the engine, pointed at that directory.
guard let engine = StockfishEngine(networkDirectory: dir) else {
fatalError("engine failed to start")
}
// 3. Read UCI output as an AsyncStream, send UCI commands.
Task {
for await line in engine.output {
print("sf>", line)
if line == "uciok" { engine.isReady() }
}
}
engine.uci()
engine.send("position startpos")
engine.send("go depth 20")
See Also¶
Licensing¶
Stockfish is licensed under the GNU General Public License, version 3. This package ships Stockfish and links it into its output, so the entire SwiftStockfish package is a GPL-3.0 work and is distributed under GPL-3.0. If you consume this package in an application, that linkage carries GPL-3.0 obligations — treat SwiftStockfish as the separately distributable GPL component.