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