A cross-platform desktop application built with Kotlin Multiplatform and Compose Multiplatform that provides full offline/online synchronization with a REST API.
This project includes comprehensive documentation to help you get started and understand the architecture:
Document | Description | Link |
---|---|---|
π README.md | Main documentation (you are here) | README.md |
βοΈ SETUP_GUIDE.md | Step-by-step installation and setup guide | SETUP_GUIDE.md |
ποΈ ARCHITECTURE.md | Technical architecture and design patterns | ARCHITECTURE.md |
π§ͺ TESTING_GUIDE.md | Comprehensive testing scenarios | TESTING_GUIDE.md |
π PROJECT_SUMMARY.md | Complete project overview and summary | PROJECT_SUMMARY.md |
π PRODUCTS_API.md | API endpoint documentation | PRODUCTS_API.md |
π CANDIDATE_INSTRUCTIONS.md | Original project requirements | CANDIDATE_INSTRUCTIONS.md |
- New to the project? Start with SETUP_GUIDE.md
- Want to understand the code? Read ARCHITECTURE.md
- Ready to test? Check TESTING_GUIDE.md
- Need API details? See PRODUCTS_API.md
- Quick overview? Read PROJECT_SUMMARY.md
- β Offline-First Architecture: Full functionality without internet connection
- β Automatic Sync: Queue operations offline, sync when online
- β Real-time Network Detection: Visual indicators for connection status
- β CRUD Operations: Create, Read, Update, Delete products
- β Search & Filter: Fast local search capabilities
- β Queue Management: Persistent queue for offline operations
- β Modern UI: Material Design 3 with Compose Multiplatform
- β Error Handling: Robust error handling with retry mechanism
- β Cross-Platform: Runs on Windows, macOS, and Linux
βββββββββββββββββββββββββββββββββββββββββββββββ
β Presentation Layer β
β (Compose UI, ViewModels, UI Components) β
βββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββ
β Domain Layer β
β (Use Cases, Domain Models, Business β
β Logic) β
βββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββ
β Data Layer β
β (Repositories, API Client, Local Database) β
βββββββββββββββββββββββββββββββββββββββββββββββ
- Local Database: SQLDelight for type-safe SQL
- Network Layer: Ktor Client for HTTP communication
- Dependency Injection: Koin for Dependency Injection
- State Management: StateFlow and SharedFlow
- Offline Queue: Persistent operation queue
- JDK 17 or higher
- Gradle 8.x (included via wrapper)
- Internet connection (for initial setup and dependency download)
π‘ Need detailed setup instructions? Check SETUP_GUIDE.md for step-by-step guidance.
- Clone the repository
git clone <your-repo-url>
cd ProductSyncApp
- Configure your User ID
Open composeApp/src/desktopMain/kotlin/com/android/technicaltest/data/repository/ProductRepository.kt
and replace the placeholder:
val userId = "YOUR_USER_ID_HERE" // Replace with your actual userId
- Build and run
./gradlew run
π For detailed installation steps, see SETUP_GUIDE.md
π§ͺ To verify everything works, follow TESTING_GUIDE.md
The base URL is configured in ApiClient.kt
:
private const val BASE_URL = "https://multitenant-apis-production.up.railway.app"
The local database is stored at:
- Windows:
C:\Users\<username>\.productsync\productsync.db
- macOS:
/Users/<username>/.productsync/productsync.db
- Linux:
/home/<username>/.productsync/productsync.db
ProductSyncApp/
βββ composeApp/
β βββ src/
β β βββ commonMain/
β β β βββ kotlin/com/android/technicaltest/
β β β β βββ data/
β β β β β βββ local/ # Database & local storage
β β β β β βββ remote/ # API client & DTOs
β β β β β βββ repository/ # Data repositories
β β β β βββ domain/
β β β β β βββ model/ # Domain models
β β β β β βββ usecase/ # Business logic use cases
β β β β βββ ui/
β β β β β βββ component/ # Reusable UI components
β β β β β βββ screen/ # Screen composables
β β β β β βββ viewmodel/ # ViewModels
β β β β βββ util/ # Utilities
β β β β βββ di/ # Dependency injection
β β β β βββ App.kt # Main app composable
β β β βββ sqldelight/ # SQL schema definitions
β β βββ desktopMain/
β β βββ kotlin/com/android/technicaltest/
β β βββ Main.kt # Desktop entry point
β β βββ data/local/
β β βββ DatabaseFactory.jvm.kt
β βββ build.gradle.kts
βββ build.gradle.kts
βββ settings.gradle.kts
βββ gradle.properties
The app works seamlessly offline:
- All data is stored locally in SQLDelight database
- Operations (Create, Update, Delete) are queued when offline
- Queue persists through app restarts
- Automatic sync when connection is restored
Real-time connection status:
- Green: Online and synced
- Blue: Syncing in progress
- Red: Offline mode
- Orange: Sync error (will retry)
Intelligent operation queuing:
- Operations are executed in order
- Failed operations are retried
- Retry count tracking
- Error logging for debugging
Two-way sync mechanism:
- Push: Local changes β Server
- Pull: Server data β Local database
- Conflict Resolution: Server data takes precedence
1. Disconnect internet
2. Create a new product
3. Verify product appears in list with cloud-off icon
4. Check pending operations counter
5. Reconnect internet
6. Wait for auto-sync or click sync button
7. Verify product synced (cloud-off icon disappears)
1. Disconnect internet
2. Edit an existing product
3. Verify changes saved locally
4. Verify unsynced indicator appears
5. Reconnect internet
6. Verify changes sync to server
1. Disconnect internet
2. Delete a product
3. Verify product marked for deletion
4. Reconnect internet
5. Verify product deleted from server
1. Create operations while offline
2. Close the application
3. Reopen the application
4. Verify queued operations persist
5. Connect to internet
6. Verify operations sync automatically
1. Work offline with multiple operations
2. Reconnect to internet
3. Observe auto-sync process
4. Verify all operations completed
5. Check for any errors
# Clean build
./gradlew clean
# Build project
./gradlew build
# Run application
./gradlew run
# Run tests
./gradlew test
# Create distribution package
./gradlew packageDistributionForCurrentOS
The app can be packaged for distribution:
# Create native installer for current OS
./gradlew packageDistributionForCurrentOS
Output locations:
- Windows:
composeApp/build/compose/binaries/main/msi/
- macOS:
composeApp/build/compose/binaries/main/dmg/
- Linux:
composeApp/build/compose/binaries/main/deb/
Technology | Purpose | Version |
---|---|---|
Kotlin | Programming Language | 2.2.20 |
Compose Multiplatform | UI Framework | 1.9.0 |
Ktor Client | HTTP Client | 3.3.0 |
SQLDelight | Local Database | 2.1.0 |
Kotlinx Coroutines | Async Operations | 1.10.2 |
Kotlinx DateTime | Date/Time Handling | 0.7.1 |
Material 3 | Design System | Latest |
Abstracts data sources (local DB, remote API) behind a clean interface.
Encapsulates business logic in single-responsibility classes.
Separates UI logic (ViewModel) from UI rendering (Composables).
StateFlow for reactive state management.
ApiClient and NetworkMonitor as application-wide singletons.
π For detailed architecture information, see ARCHITECTURE.md
This project includes comprehensive test scenarios covering:
- β Functional testing (CRUD operations)
- β Offline/online synchronization
- β Edge cases and error handling
- β Performance benchmarks
- β UI/UX validation
# Test 1: Launch application
./gradlew run
# Test 2: Create product while online
# Click + button β Enter title β Create
# Test 3: Test offline mode
# Disconnect internet β Create product β Reconnect β Verify sync
π§ͺ For complete testing guide with scenarios, see TESTING_GUIDE.md
CREATE TABLE ProductEntity (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
description TEXT,
userId TEXT NOT NULL,
createdAt TEXT NOT NULL,
updatedAt TEXT NOT NULL,
isSynced INTEGER NOT NULL,
lastSyncedAt TEXT
);
CREATE TABLE QueuedOperationEntity (
id INTEGER PRIMARY KEY AUTOINCREMENT,
operationType TEXT NOT NULL,
productId INTEGER,
title TEXT,
description TEXT,
userId TEXT NOT NULL,
createdAt TEXT NOT NULL,
retryCount INTEGER NOT NULL,
lastError TEXT,
status TEXT NOT NULL
);
1. Database file locked
Solution: Close all instances of the application
2. Network timeout
Solution: Check internet connection or increase timeout in ApiClient.kt
3. Sync fails repeatedly
Solution: Check server status and API credentials
Clear queue: Delete ~/.productsync/productsync.db and restart
4. Build fails
Solution:
- Run ./gradlew clean
- Check JDK version (must be 17+)
- Delete .gradle folder and rebuild
π§ For detailed troubleshooting, see SETUP_GUIDE.md#troubleshooting
Refer to PRODUCTS_API.md
for complete API documentation.
Endpoint | Method | Description |
---|---|---|
/products/:userId |
GET | Get all products |
/products/:userId/:id |
GET | Get product by ID |
/products/:userId |
POST | Create product |
/products/:userId/:id |
PUT | Update product |
/products/:userId/:id |
DELETE | Delete product |
π For complete API documentation, see PRODUCTS_API.md
This project is created for technical assessment purposes.
For questions or issues:
- Email: musthofaaliu@gmail.com
- Repository: https://github.com/panicDev/Technical-Test
- Documentation: See all docs in the Documentation section above
Question Type | Resource |
---|---|
How to install? | SETUP_GUIDE.md |
How does it work? | ARCHITECTURE.md |
How to test? | TESTING_GUIDE.md |
API endpoints? | PRODUCTS_API.md |
Project overview? | PROJECT_SUMMARY.md |
Build issues? | SETUP_GUIDE.md#troubleshooting |
- Kotlin Multiplatform Team
- Compose Multiplatform Community
- SQLDelight Contributors
- Ktor Framework Team
Made with β€οΈ using Kotlin Multiplatform
- Kotlin Multiplatform Docs
- Compose Multiplatform Docs
- SQLDelight Documentation
- Ktor Client Documentation
- README.md - You are here
- SETUP_GUIDE.md - Installation guide
- ARCHITECTURE.md - Technical documentation
- TESTING_GUIDE.md - Testing scenarios
- PROJECT_SUMMARY.md - Complete overview
- PRODUCTS_API.md - API reference
- CANDIDATE_INSTRUCTIONS.md - Requirements
π Ready to start? Go to SETUP_GUIDE.md