MangaKu App Powered by Kotlin Multiplatform Mobile, Jetpack Compose, and SwiftUI
Module
core
: data and domain layeriosApp
: ios presentation layerandroidApp
: android presentation layerbuildSrc
:androidApp
andcore
dependencies
- Introduction
- Features
- Installation
- Screenshot
- Libraries
- Domain to Presentation
- Expect and Actual
- Project Structure
A few things you can do with MangaKu:
- View Popular Manga
- Easily search for any Manga
- See Manga Detail
- Save your favorite manga
This project have no concern about backward compatibility, and only support the very latest or experimental api's for both android and ios
- Follow the KMM Guide by Jetbrains for getting started building a project with KMM.
- Install Kotlin Multiplatform Mobile plugin in Android Studio
- Clone or download the repo
- Rebuild Project
- To run in iOS, Open Xcode and
pod install
insideiosApp
folder to install shared module and ios dependencies
core
:
iosApp
:
androidApp
:
- Jetpack Compose
- Accompanist
- Koin
- Compose Destinations
- Some Kotlinx & Jetpack Components
In Android, Because both core
and androidApp
written in Kotlin, we can simply collect flow :
private fun getTrendingManga() = viewModelScope.launch {
_trendingManga.value = Result.loading()
browseUseCase.getManga()
.catch { cause: Throwable ->
_trendingManga.value = Result.failed(cause)
}
.collect { result ->
if (result.isNotEmpty())
_trendingManga.value = Result.success(result)
}
}
But in iOS, we have to deal with swift, here i'm using createPublisher()
from KMPNativeCoroutines
to collect flow as Publisher in Combine
:
func fetchTrendingManga() {
trendingManga = .loading
createPublisher(for: browseUseCase.getTrendingMangaNative())
.receive(on: DispatchQueue.main)
.sink { completion in
switch completion {
case .finished: ()
case .failure(let error):
self.trendingManga = .error(error: error)
}
} receiveValue: { value in
self.trendingManga = .success(data: value)
}.store(in: &cancellables)
}
or even better, you can use asyncFunction
/ asyncResult
/ asyncStream
function to collect coroutine flow as new swift's concurrency features, checkout branch feat/experimenting-swift-new concurrency to see the example
combining two powerful concurrency feature from both native framework, how cool is that !?
func fetchTrendingManga() {
Task {
trendingManga = .loading
do {
let nativeFlow = try await asyncFunction(for: browseUseCase.getTrendingMangaNative())
let stream = asyncStream(for: nativeFlow)
for try await data in stream {
trendingManga = .success(data: data)
}
} catch {
trendingManga = .error(error: error)
}
}
}
learn more: https://github.com/rickclephas/KMP-NativeCoroutines
in KMM, there is a negative case when there's no support to share code for some feature in both ios and android, and it's expensive to write separately in each module
so the solution is โจexpect
and actual
โจ, we can write expect
inside commonMain
and write "actual" implementation with actual
inside androidMain
and iosMain
and then each module will use expect
example:
commonMain/utils/DateFormatter.kt
expect fun formatDate(dateString: String, format: String): String
androidMain/utils/DateFormatter.kt
SimpleDateFormat
actual fun formatDate(dateString: String, format: String): String {
val date = SimpleDateFormat(Constants.formatFromApi).parse(dateString)
val dateFormatter = SimpleDateFormat(format, Locale.getDefault())
return dateFormatter.format(date ?: Date())
}
iosMain/utils/DateFormatter.kt
NSDateFormatter
actual fun formatDate(dateString: String, format: String): String {
val dateFormatter = NSDateFormatter().apply {
dateFormat = Constants.formatFromApi
}
val formatter = NSDateFormatter().apply {
dateFormat = format
locale = NSLocale(localeIdentifier = "id_ID")
}
return formatter.stringFromDate(dateFormatter.dateFromString(dateString) ?: NSDate())
}
yes, we can use Foundation
same as what we use in Xcode
core
:
data
mapper
repository
source
local
entity
remote
response
di
domain
model
repository
usecase
browse
detail
mymanga
search
utils
androidApp
:
ui
composables
home
composables
favorite
search
detail
di
utils
iosApp
:
Dependency
App
Main
Resources
ReusableView
Extensions
Utils
Features
Browse
Navigator
Views
Search
Detail
MyManga