Recipes is a Kotlin Multiplatform (KMP) application for browsing and searching recipes. It demonstrates modern Android and KMP development practices, including the use of Compose Multiplatform, Circuit architecture, and various powerful libraries.
- Search: Quickly find recipes by keyword.
- Categories: Browse recipes organized by categories.
- Favorites: Save your favorite recipes for quick access.
- Recipe Details: View detailed instructions and ingredients for each recipe.
The project leverages a modern and robust tech stack for multiplatform development:
- Kotlin Multiplatform (KMP): Shared business logic and data layers across Android, iOS, Desktop, and Web.
- Compose Multiplatform: Shared UI framework for building beautiful and consistent interfaces on all platforms.
- Circuit: A simple, reactive framework for building UI on Android and multiplatform.
- Metro: A multiplatform, compile-time dependency injection (DI) framework for Kotlin.
- Ktor: For networking and API interactions.
- Coil 3: Image loading for Kotlin Multiplatform.
- Room 3: SQLite object mapping library, now supporting KMP.
- Store 5: A library for managing data loading and caching.
- Data Source: Uses the TheMealDB API (v2) for fetching recipe data. A
MEALDB_API_KEYis required to build — set it as a Gradle property in~/.gradle/gradle.propertiesor as an environment variable.
The project is organized into several modules to ensure a clean separation of concerns:
app: Android-specific application code.ui: Shared UI components using Compose Multiplatform.domain: Shared business logic and presenters (Circuit).model: Shared data models.repository: Shared data repository layer.network: Shared networking layer using Ktor.storage: Shared persistence layer using Room.shared: Dependency injection (DI) and application-level shared logic.iosApp: iOS-specific application code (Swift).webApp: Web-specific application code using Compose for Web.desktopApp: Desktop-specific application code using Compose for Desktop.sqliteWasmWorker: Helper for SQLite WASM on Web.
- Android Studio Panda or newer.
- Xcode (for iOS development).
- Android: Open the project in Android Studio and run the
appconfiguration. - iOS: Open
iosApp/iosApp.xcworkspacein Xcode and run on a simulator or device. - Desktop: Run
./gradlew :desktopApp:run. - Web (JS): Run
./gradlew :webApp:jsBrowserDevelopmentRun. - Web (wasmJS): Run
./gradlew :webApp:wasmJsBrowserDevelopmentRun.
Room stores an identity hash of the schema inside the database file. Change an entity and that hash stops matching, so the app throws on first database access:
IllegalStateException: Room cannot verify the data integrity.
Looks like you've changed schema but forgot to update the version number.
This is deliberate and there is no destructive-migration fallback — a schema change should make you decide between writing a migration and throwing the local data away, rather than silently losing it. Two distinct failures:
- Schema changed,
versionleft alone — the message above. Delete the local database (below). versionbumped with no matchingMigration—A migration from 1 to 2 was required but not found.Write the migration, or delete the local database while still in development.
To confirm you're in the first case, run git diff storage/schemas/. If identityHash changed but
the file is still 1.json, the schema moved without a version bump and every existing local
database is now invalid.
The database is recipe.db on every platform:
| Platform | How to delete it |
|---|---|
| Web | DevTools → Application → Clear site data. To remove only the database: const root = await navigator.storage.getDirectory();await root.removeEntry('recipe.db');then reload. |
| Desktop | Delete the whole app data directory: rm -rf ~/Library/Application\ Support/Recipes on macOS, rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/Recipes" on Linux, rmdir /s %LOCALAPPDATA%\scottolcott\Recipes on Windows. |
| iOS | rm -f "$(xcrun simctl get_app_container booted com.scottolcott.recipe.Recipes data)/Documents/recipe.db"* — or just delete the app. |
| Android | Clear storage in the app's system settings, or adb shell pm clear com.scottolcott.recipe. |
Things that catch people out:
- Keep the trailing
*. The database runs in WAL mode, sorecipe.db-walandrecipe.db-shmsit alongside it. Deleting onlyrecipe.dbleaves a partial database behind. - Desktop data persists across reboots. It lives in the per-user app data directory that
net.harawata:appdirsresolves —~/Library/Application Support/Recipeson macOS,%LOCALAPPDATA%\scottolcott\Recipeson Windows,$XDG_DATA_HOME/Recipes(default~/.local/share/Recipes) on Linux. Deleting it is the only way to reset desktop. - DataStore is stored separately from the database. On desktop and iOS the fetch-history and
search-suggestion
.jsonfiles live in the same directory asrecipe.db; on web they are inlocalStorage, a different bucket from OPFS, so the console snippet above does not touch them. Clearing only the database does recover — each Store rejects empty results and refetches — but Clear site data is the real reset on web. - Favorites do not come back. They exist only in the local database and there is no API to restore them from.
- The database used to be called
my_room.dbon iOS and desktop. If you ran an older build, delete that stale file once. - Desktop used to keep everything in the system temp directory, where it vanished on reboot — which
is why this crash sometimes appeared to fix itself. If you ran an older build, clear the leftovers
once:
rm -f "$TMPDIR"recipe.db* "$TMPDIR"*_history.json "$TMPDIR"search_suggestions.jsonon macOS, the same under/tmpon Linux or%TEMP%on Windows.
This project is licensed under the Apache License 2.0. See the LICENSE file for details.