Skip to content

odaridavid/Smoovie-KMP

Repository files navigation

Smoovie

codecov Android iOS

A Kotlin Multiplatform app for browsing and exploring movies, powered by the TMDB API. Targets Android and iOS with a shared Compose Multiplatform UI.

Get it on Google Play

Browse popular movies and TV shows, search the TMDB catalog, drill into details (cast, trailers, reviews, seasons, where to watch), and bookmark titles to a local watchlist. Region-aware results are driven by a Settings picker that defaults to your system locale.

Project Structure

The app is a single Gradle module (:composeApp) with one package per feature under composeApp/src/commonMain/kotlin/dev/odaridavid/smoovie/. Each feature package is self-contained (screen + ViewModel + UI models + data/ + domain/); platform-native pieces live in androidMain/ and iosMain/. The Firebase Cloud Function in functions/ proxies TMDB.

Architecture

The TMDB API token never ships in the mobile app. Requests flow:

App  ──(X-Firebase-AppCheck)──▶  Cloud Function (Firebase)  ──(Bearer token)──▶  TMDB

The Cloud Function (functions/src/index.ts) verifies the Firebase App Check token (proving the request came from an authentic build), attaches the TMDB bearer token from Google Secret Manager, and forwards GET /3/* to api.themoviedb.org. The mobile app's TMDB_BASE_URL points at the deployed function URL.

Setup

Running the app from source requires your own Firebase project. The committed google-services.json / GoogleService-Info.plist and the TMDB_BASE_URL in AppConfig.kt all point at the production Smoovie project, and only its owner can register debug App Check tokens against it — so cloning and running as-is will fail with "Failed to load" on every TMDB call.

If you just want to try the app, install it from Google Play. To build from source, work through all three steps below.

1. Deploy your own TMDB proxy

Create a Firebase project (Blaze plan — 2nd-gen functions require it; app-scale usage is $0), grab a TMDB API token, and deploy the Cloud Function. Full walkthrough in functions/README.md. You'll end up with a function URL like https://tmdb-proxy-<hash>-<region>.a.run.app.

2. Wire your project into the app

  • Add Android + iOS apps to your Firebase project, then download the generated google-services.json (Android) and GoogleService-Info.plist (iOS) and replace the committed files at composeApp/google-services.json and iosApp/iosApp/GoogleService-Info.plist.
  • Update TMDB_BASE_URL in AppConfig.kt to point at your function URL.

3. Register your debug build for App Check

App Check rejects requests from non-authentic builds. For your local debug build to be allowed, register its debug token once per device/simulator install:

  • Android: run the debug build on your device, then in Logcat filter for Enter this debug secret and copy the UUID.
  • iOS: run the debug build, then in the Xcode Debug Area look for the line printed under ===== Firebase App Check Debug Token ===== and copy the UUID.

Paste the UUID into Firebase Console → App Check → Apps → ⋮ → Manage debug tokens (in your project from step 1), give it a name, save. The token persists in the app's local storage.

Dependencies

Library Purpose
Ktor Multiplatform HTTP client
kotlinx.serialization JSON parsing, typed nav route args
kotlinx.coroutines Async / StateFlow
Compose Multiplatform Shared UI
AndroidX Lifecycle ViewModel + viewModelScope (KMP)
AndroidX Navigation Compose Typed KMP navigation + shared transitions
Room KMP Local persistence for the watchlist
androidx.sqlite (bundled) Bundled SQLite driver for KMP
Koin Multiplatform dependency injection
Coil 3 Image loading (backdrops, posters, profiles)
KSP Room annotation processing
multiplatform-settings Cross-platform KV persistence (filter + region + consent prefs)
Firebase App Check Verifies requests come from authentic builds (Play Integrity / App Attest)
Firebase Crashlytics Android crash reporting (opt-in via settings consent prompt)
Napier Multiplatform logging behind the observability.Logger interface
Play In-App Review Android in-app review prompt (paired with SKStoreReviewController on iOS)
ktlint-gradle Code style enforcement
Kover Code coverage reports (HTML + XML, used by CI)
Develocity Build scans — build/test insights published to scans.gradle.com

Build and Run

Android

Run from the IDE toolbar or from the terminal:

# macOS / Linux
./gradlew :composeApp:assembleDebug

# Windows
.\gradlew.bat :composeApp:assembleDebug

iOS

Open /iosApp in Xcode and run, or use the run configuration in Android Studio / Fleet.

Tests

./gradlew :composeApp:allTests

Lint

./gradlew :composeApp:ktlintCheck    # check (what CI runs)
./gradlew :composeApp:ktlintFormat   # auto-fix

Build Scans

The Develocity Gradle plugin publishes build scans — shareable reports of what a build did (task timings, cache hits, test results, environment). Add --scan to any build to publish one:

./gradlew :composeApp:assembleDebug --scan

The scan URL is printed at the end of the build. Scans publish automatically in CI; locally they only publish when you pass --scan. The terms of use are pre-accepted in settings.gradle.kts, so publishing is unattended.

Pre-commit hook

A hooks/pre-commit script runs ktlintCheck before every commit and blocks it if there are violations. Activate it once after cloning:

git config core.hooksPath hooks

If the hook blocks a commit, run ./gradlew :composeApp:ktlintFormat to auto-fix, re-stage the changes, then commit again.

Privacy Policy and Support

Both pages live under docs/ and are served via Firebase Hosting.

Privacy policy (docs/privacy-policy.html) — linked from the Google Play and App Store listings:

Support (docs/support.html) — linked from the App Store listing (the Play Console uses the developer support email instead):

To redeploy after editing:

firebase deploy --only hosting
rec.MP4

About

Discover movies and TV shows on Android & iOS - built with Compose Multiplatform, Ktor, Room, and a Firebase-proxied TMDB backend

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages