BlazeDB: Swift Native Embedded Storage Engine (open source)

BlazeDB: Swift Native Embedded Storage Engine (open source)

I posted about BlazeDB here a while back when it was still an experiment. It's come a long way since then and I'm open sourcing it.

GitHub - Mikedan37/BlazeDB: Swift-native embedded database for local app storage, encrypted page persistence, and deterministic crash recovery. · GitHub (MIT)

The basic idea: you define a struct, conform it to BlazeStorable, and store it. Queries use KeyPaths, everything is encrypted at rest, and writes go through a WAL so committed data survives crashes.


let db = try BlazeDBClient.open(named: "myapp", password: "your-password")

let counters = db.typed(Counter.self)

try counters.insert(Counter(name: "Alice", count: 10))

try counters.insert(Counter(name: "Bob", count: 20))

let results = try counters.query()

.where(\.count, greaterThan: 15)

.all()

It's schemaless by default so you don't have to define anything upfront. But there's also a raw record layer if you want to work with dynamic data directly:


let record = BlazeDataRecord([

"name": .string("Alice"),

"age": .int(30)

])

try db.insert(record)

let results = try db.query()

.where("status", equals: .string("open"))

.orderBy("created_at", descending: true)

.limit(10)

.execute()

.records

One of the things I'm most happy with is the SwiftUI integration. There's a @BlazeQuery property wrapper that observes the database and re-runs your query when data changes. Your views just stay in sync:


struct RecordList: View {

@BlazeQuery(db: db, limit: 100) var results: [BlazeDataRecord]

var body: some View {

List(results, id: \.id) { record in

Text(record.description)

}

}

}

Changes get batched over a 50ms window so it's not re-querying on every single write.

I built this for my own developer tools and personal projects. I use CoreData at work and it's fine, but for my own stuff I kept wanting something with less setup. And I like SQLite but I got tired of writing SQL in Swift. I just wanted to define a struct, store it, query it with KeyPaths, and have encryption on by default so I don't have to think about it.

Everything is AES-256-GCM encrypted at the page level, and the WAL stores only encrypted frames. Getting that working on macOS was straightforward. CryptoKit handles it. Linux was a different story. swift-crypto, endianness differences, platform assumptions that break once you're off Apple hardware. I got the core engine running there, but honestly it hasn't seen the same real-world testing that macOS has. That's still a work in progress.

I put a lot of effort into the testing to make sure the durability claims actually hold up: chaos tests, concurrency tests with a couple hundred simultaneous writers, crash recovery validation. I didn't want to put "crash safe" in the README and just hope it's true.

Where I'd like to take this eventually is using BlazeDB as a shared storage layer across client and server, all in Swift. There's early Vapor integration in the repo already. The idea is you could use the same engine and same models in your iOS app and your Vapor backend for personal tools, single-user apps, local-first stuff. Not trying to build a Postgres competitor. But for something like a single-user app with a Swift backend where you want the same storage on both sides and everything encrypted, that's the use case I care about.

The repo also has migrations if you need them, CLI tooling for inspecting and diagnosing databases, and docs covering the architecture and benchmarks.

macOS 15+, iOS 15+, Linux. Swift 6 concurrency compliant.

8 Likes

I'm curious if you tried to run it with Android and interop with KMM

1 Like

I dug into this after your question because I was curious myself.

BlazeDB’s storage engine already had a portable core, but the observation layer was still tied to SwiftUI. I’ve since extracted that into a platform-agnostic BlazeLiveQuery abstraction, so the architecture now maps cleanly to the Repository + ViewModel pattern commonly used on Android.

We also added:

  • Android cross-compilation validation for the core (BLAZEDB_LINUX_CORE) in CI
  • CorePathSmoke and MVVMPattern examples to validate the portable architecture outside SwiftUI
  • Documentation describing how the SwiftUI model maps to Android MVVM

What this doesn’t mean is that BlazeDB has native Kotlin or KMM support today. There’s still no Kotlin SDK, JNI bridge, or Compose integration. Android today is the Swift-on-Android core path, not a Kotlin-native library.

So the short answer is: the architecture is now in place and we’ve validated the portable core, but the Kotlin/KMM integration layer is still future work.

2 Likes

@asaptf Quick update since my June reply, this part has moved.

There’s now an experimental Android/KMM path in the repo:

  • Swift core → Android bridge → JNI → Kotlin expect/actual
  • sample KMM module under Examples/android/
  • CI cross-compiles the bridge and runs put/query on an Android emulator, plus an iOS simulator smoke test

So the answer to your original question is now closer to: yes, we tried it, and the interop path works in CI.

What it still is not:

  • no published Kotlin SDK
  • no Maven Central / CocoaPods package
  • no Compose-first API

It’s basically validation scaffolding for now, intentionally frozen until there’s a real packaging/use-case pull.

Docs are in Docs/android-status.md and Docs/GettingStarted/KMM_GETTING_STARTED.md.

If you’re trying this from a real KMM app, I’d be especially curious what packaging shape would actually be useful.

So the project seems very AI generated which by itself may be fine, however I would strongly suggest leaning into using swift-java for the language interop rather than hand rolling lots of raw pointer management like here BlazeDB/Examples/android/app/src/main/cpp/blazedb_jni_shim.c at 8b3ab9867aec3a78a80bf4043b7a5d8b486745fc · Mikedan37/BlazeDB · GitHub. Even coding agents are not great at consistently handling the JNI patterns in optimal ways and repeatedly, getting rid of the glue code with something that consistently generates correct bindings is a better path, even when using coding agents extensively IMHO.

Fair point on the JNI layer. I did use AI extensively while building BlazeDB, but I’d push back a bit on characterizing the project itself as AI-generated. The architecture and design decisions have been intentional; AI was definitely used as a development tool.

The Android/JNI path in particular was experimental scaffolding, not intended as the production interop design. I agree swift-java is likely a better direction if I take that surface further. I appreciate your perspective on the interop layer.

1 Like

Thanks for clarifying, yeah it's fine and useful to use these tools with appropriate guidance - sorry if I overstated the use of it there, skimming the generated JNI layer made me think it was used a bit more nonchalantly than it actually is in the rest of the project most likely :+1:

Hope you'll be able to leverage the interop package in the future, if you hit any issues please let us know!

1 Like

No worries; thanks for clarifying and contributing. If there’s enough pull to take the Android surface further, I’ll evaluate swift-java instead of extending the JNI glue by hand. :saluting_face: