RecipeApp
RecipeApp is a Kotlin Multiplatform app that showcases recipes from the Spoonacular API using a shared UI with Compose Multiplatform for Android and iOS.
Features
- Home with featured and popular recipes
- Search by text (debounced) and quick chips
- Recipe details
- Favorites with local persistence
- Snackbar-based error handling
- Image loading with placeholders and loading indicators
- Dark Mode support on both Android and iOS
Tech Stack
- Kotlin Multiplatform (KMP)
- Compose Multiplatform
- Voyager (Navigation)
- Koin (Dependency Injection)
- Ktor (Networking)
- Coil 3 (Images)
- SQLDelight (Local storage)
- Coroutines + StateFlow/Flow
- Testing: MockK, Turbine, Kotlin Test, Coroutines Test
Architecture
- MVI + Unidirectional Data Flow
- Contracts per screen: State, Intent, Effect
- Shared domain/data layers across platforms
Project Structure
composeApp/src/commonMain/— Shared UI, ViewModels, use cases, repositorycomposeApp/src/commonTest/— Shared unit testscomposeApp/src/androidMain/— Android-specificcomposeApp/src/iosMain/— iOS-specificiosApp/— iOS launcher projectbuild.gradle.kts,settings.gradle.kts— Build configuration
Setup
Prerequisites
- Android Studio or IntelliJ IDEA with Kotlin plugin
- JDK 11 or higher
- Xcode (for iOS development)
- Gradle 8.14.3 or higher
Spoonacular API Key Configuration
This project uses BuildKonfig and local.properties to securely store API keys.
1. Get Your Spoonacular API Key
- Go to the Spoonacular API page
- Create a free account
- Copy your API key from the dashboard
2. Add API Key to Local Properties
Add your API key to the local.properties file in the project root directory:
# API Keys - DO NOT COMMIT THIS FILE TO GIT!
API_KEY=your_spoonacular_api_key_hereImportant: The local.properties file is already in .gitignore, so it won't be committed to Git. This keeps your API key secure.
3. Build the Project
Run the following command in the terminal:
./gradlew clean buildThis command will run the BuildKonfig plugin and integrate the API key into your project.
4. Usage
The API key is now automatically available in your code. It's accessed via BuildKonfig in AppModule.kt:
import org.example.recipeapp.BuildKonfig
val networkModule = module {
single { createHttpClient() }
single {
SpoonacularApi(
client = get(),
apiKey = BuildKonfig.API_KEY // Automatically read from local.properties
)
}
}🔒 Security Notes
- The
local.propertiesfile is in.gitignore, so it won't be committed to Git - Each developer should create their own
local.propertiesfile - Never write your API key directly into code files
🚀 Running the Project
Android
./gradlew :composeApp:assembleDebugor open the project in Android Studio and press the Run button.
iOS
- Open the
iosApp/iosApp.xcodeprojfile in Xcode - Select a simulator or physical device
- Press the Run button
or in the terminal:
./gradlew :composeApp:iosSimulatorArm64Binaries.framework🧪 Testing
This project includes comprehensive unit tests for all layers of the application.
Test Coverage
- Core Utilities: Result sealed class tests (13 tests)
- Domain Layer: UseCase tests for all business logic (32 tests)
- GetRandomRecipesUseCase
- SearchRecipesUseCase
- GetRecipeDetailUseCase
- AddFavoriteUseCase
- RemoveFavoriteUseCase
- ToggleFavoriteUseCase
- IsFavoriteUseCase
- ObserveFavoritesUseCase
- Data Layer: Repository implementation tests (18 tests)
- RecipeRepositoryImpl
- FavoritesRepositoryImpl
- Presentation Layer: ViewModel tests with MVI pattern (31 tests)
- HomeViewModel
- SearchViewModel
- DetailsViewModel
- FavoritesViewModel
Total: ~94 unit tests covering all critical business logic and UI state management.
Running Tests
Run all unit tests:
./gradlew :composeApp:testDebugUnitTestRun tests with detailed output:
./gradlew :composeApp:testDebugUnitTest --infoRun specific test class:
./gradlew :composeApp:testDebugUnitTest --tests "org.example.recipeapp.presentation.home.HomeViewModelTest"View test reports:
# Open HTML test report
xdg-open composeApp/build/reports/tests/testDebugUnitTest/index.htmlTest Tools
- MockK: Mocking framework for Kotlin
- Turbine: Testing library for Flow and StateFlow
- Kotlin Test: Kotlin's testing framework
- Coroutines Test: Testing utilities for coroutines
Screenshots
Android
| Home | Search | Favorites | Details |
![]() |
![]() |
![]() |
![]() |
iOS
| Home | Search | Favorites | Details |
![]() |
![]() |
![]() |
![]() |







