All posts

One SHA-256, two platforms: expect/actual in Fathom

ยท by Sk Masum Ali

Fathom's core is shared Kotlin Multiplatform code: models, repositories and the feed logic live once, with a native UI on top. Almost everything shares cleanly. This post is about the small part that can't, and a database layout that makes updating content safe.

The one thing that has to differ

Fathom supports content packs. A corrupted or tampered pack must never replace the library, so a pack is checked against its expected size and SHA-256 hash first. Hashing needs a platform crypto API, and the two platforms don't have the same one. The shared module declares what it needs and each platform answers:

// commonMain
expect class Sha256Verifier() {
    fun calculateSha256(bytes: ByteArray): String
}

// androidMain
actual class Sha256Verifier actual constructor() {
    actual fun calculateSha256(bytes: ByteArray): String {
        val hash = MessageDigest.getInstance("SHA-256").digest(bytes)
        return hash.joinToString("") { "%02x".format(it) }
    }
}

The iOS side does the same job with CC_SHA256 from CoreCrypto through Kotlin's C interop. The part that matters is that ContentPackManager, which decides whether a pack is acceptable, lives in commonMain and only ever sees the expect class. It compares the byte size first, then the hash, and returns a failure result on either mismatch.

Two databases, on purpose

Fathom has two SQLDelight databases. One holds the content: traditions, books, passages, themes and explanations. The other holds what belongs to the reader: bookmarks, notes and reading records. They are separate source sets, so the schemas can't drift into each other.

The reason is updates. Content is something I ship and replace. Bookmarks and notes are something the reader made. If both lived in one file, replacing the library would mean carefully preserving the user's tables. With two files, the content database can be swapped without touching the user's data at all.

Command menu

Jump to a page, open a profile, or run an action