This is a wordle clone I made to get my feet wet with the newly emerging kotlin multiplatform ecosystem.
Desktop, android, and iOS platforms are supported.
You can find the android and ios versions on their respective app stores via my website.
- Daily Mode: This mode works the same way wordle works, where the word is the same for everyone that day (according to device date).
- Sharing: You can share your game results the same way you can with wordle.
- Example share text:
What in da word!?
Date: 2024-12-21 📅
Mode: Daily 📆
3/6
0m 38s
🟨⬛⬛🟨⬛
⬛⬛🟨🟨🟩
🟩🟩🟩🟩🟩
- Infinity Mode: This works the same way as daily mode except you may play over and over again, not limited by the day!
- Timer: Each game is timed to see how fast you can solve the puzzle, which you can see in the sessions tab and in the shareable text.
- Definitions: The app fetches definitions from a free dictionary api and displays them to you after game completion.
- Dictionary: The app keeps track of unlocked words as you play, where you can view all the definitions for that word and all your game sessions for that word.
To run the desktop version:
- clone this project
- open it in android studio
- open the cmd prompt
- ensure your current path is at the project root and type
./gradlew run. - Enjoy!
- 👤 This is a compose multiplatform project, so all ui is shared.
- I only had to write 1 line of swift:
ComposeView().ignoresSafeArea(edges: .all)
- 💾 Local database: I used SqlDelight.
- I tried using Room first, but had difficulty setting it up/writing tests with it and the alpha kmp version made me abandon ship to a more mature sqldelight.
- SqlDelight is wonderful. I love the paradigm shift of generating kotlin from sql instead of generating sql from kotlin.
- SqlDelight leaves something to be desired in the object mapping department, to where I had a tough time using
JOINs and mapping those results to kotlin objects.
- 💉 Dependency Injection: I used Koin.
- I prefer manual dependency injection over dependency injection frameworks, but koin did make the whole android context easier to deal with.
- I enjoy this library and used it for clean architecture style data sources and http client to make testing easier (or if I want to swap out one of these implementations later).
- 🌐 Http Client: I used Ktor
with Kotlinx Serialization
- This is how I fetch definitions from that free dictionary api
- This was a delightful experience. I ran into 0 issues working with these.
- 💾 Local datastore: I used Androidx Datastore
- With this, I was able to store shared preferences for toggling dark mode, showing the startup animation, using system theme for light/dark mode instead of dark mode override.
- It was very easy to observe these preferences, create delegates out of them, and even make my own reactive composable for reactive app theme changes:
@Composable
fun <T> rememberPreference(
dataStoreDelegate: DataStoreDelegate<T>,
defaultOverride: T? = null
): State<T> {
return dataStore
.data
.map { preferences: Preferences ->
preferences[dataStoreDelegate.key] ?: defaultOverride ?: dataStoreDelegate.defaultValue
}
.collectAsStateWithLifecycle(defaultOverride ?: dataStoreDelegate.defaultValue)
}
- 👨🔬 Unit Testing: I used kotlin.test
- This was straight forward common code testing (source sets in gradle still kind of confuse me, but that is not directly related to this library).
- runTest() is super neat as it skips delays. I like that.
- Wonderful for testing database operations.
- 🧭 Navigation: I used Compose Navigation
- In the past, this library left something to be desired with routes, but now with type-safe routing, this was actually kinda pleasant to work with. 🌟
- 💫 Shared Element Transition: I managed to get in a shared element transition when on the dictionary screen, navigating to dictionary detail, the word travels to the next location.
- 🍫 App Bar Navigation: Dynamic bottom app bar/side navigation rail based on screen size and device rotation!