Closed bibutikoley closed 1 year ago
Example of it's use here in case it helps https://github.com/joreilly/Confetti/blob/main/iosApp/iosApp/ContentView.swift
Are you using @StateViewModel?
On Mon 23 Jan 2023, 13:11 Bibuti Koley, @.***> wrote:
The library is not generating the correct @Property https://github.com/Property for the defined StateFlow.
[image: image] https://user-images.githubusercontent.com/20051993/214047814-d50c0b60-238e-4ea3-a0f8-a4e4524a6dee.png
[image: image] https://user-images.githubusercontent.com/20051993/214047942-1e21b725-3ff8-4b66-b856-367fc7efcade.png
Is there a script or command that needs to be executed for the correct code to be generated?
— Reply to this email directly, view it on GitHub https://github.com/rickclephas/KMM-ViewModel/issues/14, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAABRHRTAPYBMOKKY4GFRZTWTZ7R3ANCNFSM6AAAAAAUD2L73I . You are receiving this because you are subscribed to this thread.Message ID: @.***>
@joreilly Yes.. I referred the Confetti Repository. Below is my code snippet.
Hi @bibutikoley! The properties generated by KMP-NativeCoroutines are extensions properties.
Hence they will have their own categorie in the Objective-C header.
Are you able to access the randomNumbers
property from Swift?
If not, could you share your build.gradle(.kts)
file?
Hello @rickclephas, I'm not able to access the randomNumbers property. It gives me an error. "Cannot assign to property: 'randomNumbers' is a get-only property"
Here is my build.gradle.kts file (shared module)
plugins {
kotlin("multiplatform")
kotlin("native.cocoapods")
id("com.android.library")
kotlin("plugin.serialization") version "1.8.0"
//Native Coroutines Dependency
id("com.google.devtools.ksp") version "1.8.0-1.0.8"
id("com.rickclephas.kmp.nativecoroutines") version "1.0.0-ALPHA-3"
}
kotlin {
android()
iosX64()
iosArm64()
iosSimulatorArm64()
cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
version = "1.0"
ios.deploymentTarget = "14.1"
podfile = project.file("../mvi-iosApp/Podfile")
framework {
baseName = "shared"
}
}
sourceSets {
all {
languageSettings.optIn("kotlin.experimental.ExperimentalObjCName")
}
val commonMain by getting {
dependencies {
implementation("com.rickclephas.kmm:kmm-viewmodel-core:1.0.0-ALPHA-3")
implementation("io.ktor:ktor-client-core:2.2.1")
implementation("io.ktor:ktor-client-cio:2.2.1")
implementation("io.ktor:ktor-client-serialization:2.2.1")
implementation("io.ktor:ktor-client-content-negotiation:2.2.1")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.2.1")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting {
dependencies {
implementation("io.ktor:ktor-client-android:2.2.1")
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
}
}
val androidTest by getting
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
dependencies {
implementation("io.ktor:ktor-client-ios:2.2.1")
}
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
}
}
android {
namespace = "com.example.kmparch_mvi"
compileSdk = 33
defaultConfig {
minSdk = 25
targetSdk = 33
}
}
@joreilly @rickclephas Thank You for the support and Sorry for the confusion. I placed the KMMViewModel.swift
file in a wrong directory. The Code seems to be working fine.
But would like to know if inheritance is supported or not?
Interesting error message. Anyway I think it's because viewModel
is the Projection
. Could you try the following?:
print("ViewModel -> \(self.viewModel.randomNumbers)")
What exactly do you mean by "inheritance"? You can subclass your Kotlin ViewModel in Swift (if that's what you mean) like in the sample: https://github.com/rickclephas/KMM-ViewModel/blob/3c73e14db2576edf8b9e8539e80a4fb3f4ff2090/sample/iosApp/KMMViewModelSample/TimeTravelViewModel.swift#L10-L19
@rickclephas By Inheritance I mean,
BaseViewModel : KMMViewModel()
and RandomViewModel : BaseViewModel()
Is this possible?
Yeah that should be possible as well.
@rickclephas Thanks for the help.. closing this issue.
@rickclephas Can we use SharedFlow with this Library?
Something like this for one-time events
No, SharedFlow
s aren't currently supported.
Could you share some more information about this use case? How would you expect to consume/use such a Flow in iOS (and Android)?
@rickclephas I want to perform navigation in the app. (i.e. - One time event). I would request you to kindly clone this repo - https://github.com/bibutikoley/KmpArch
This repo is simply fetching the data from newsapi and displaying it in the list view and tap of the item user navigates to details page.
I'm using NativeCoroutines and KMM-ViewModel for the same. My objective is to make the android and iOS use the same codebase in shared module.
For android it works as expected but unable to achieve it in iOS.
Also, the library does not generate the extension properties if ViewModel is in a different Module.
For. eg. In the repo - If I extend NewsListingViewModel
from BaseViewModel
it works(same module), But If I extend it from KmpViewModel
it does not work(different module).
Thanks for the sample code! How would you normally implement such an architecture in pure Swift? I am struggling to see/understand the Swift version of such an architecture.
Also, the library does not generate the extension properties if ViewModel is in a different Module.
KMP-NativeCoroutines only generates the required Kotlin code.
When using multiple modules you'll need to make sure these modules are exported to your iOS project.
See the export
option in the CocoaPods plugin documentation.
@rickclephas Thanks for looking into the code. The architecture was not meant for pure swift but to achieve a MVI based Architecture with Shared Code(KMM).
The above architecture is referred from this link - Demo - https://github.com/fededri/kmm-demo Explanation of the architecture - https://proandroiddev.com/kotlin-multiplatform-mobile-and-how-share-viewmodel-an-architecture-proposal-b6f86b61abf9.
I'm trying to mix the KMM-ViewModel, NativeCoroutines and The Architecture above to move business logic and ui-states of the app to shared module and the UI(android and iOS) will receive the ui-states directly from the viewmodel and perform respective events.
I would request you to please take a look at the above links and share your feedback.
Also, when working with generics in KMM-ViewModel do we need to cast the value to access it in swift code or is there any other solution available? ref.https://github.com/rickclephas/KMM-ViewModel/issues/15
@rickclephas Is there a way to directly initialize the @ObservedViewModel var newsListingViewModel: NewsListingViewModel
in the 'NewsListView' from the above example?
Currently we are passing the value from with init(viewModel: ObservableViewModel
Please let me know your thoughts on this
You can use the same approach and pass the NewsListingViewModel
directly as well.
However keep in mind that you can only do this once for a specific view model. Once the VM is wrapped you'll have to use the projection instead.
init(viewModel: NewsListingViewModel) {
self._newsListingViewModel = ObservedViewModel(wrappedValue: viewModel)
}
Hi @rickclephas, I tried using the code snippet that you have shared. But unfortunately with this approach the Flow in the viewModel(annotated with NativeCoroutinesState) is not receiving any items emitted by the ViewModel.
import shared
import KMMViewModelSwiftUI
import KMMViewModelCore
struct NewsListView: View {
@ObservedViewModel var newsListingViewModel: NewsListingViewModel
@State private var isActive = false
init(vm: NewsListingViewModel = NewsListingViewModel()) {
self._newsListingViewModel = ObservedViewModel(wrappedValue: vm)
processEvent(event: (vm.observeEvents as? NewsListingEvents))
}
@State private var isShowingDetailView = false
var body: some View {
NavigationView {
VStack {
ScrollView {
Text("Latest News")
.padding(20)
ForEach((newsListingViewModel.observeRenderState as? NewsListingRenderState)?.newsArticleList ?? [], id: \.self) { item in
VStack {
NewsListCell(item: item, onTap: { article in
newsListingViewModel.action(action: NewsListingActions.UserActionsSelectNewsArticle(newsArticle:article))
}).background(
NavigationLink(
destination: NewsDetailsView(newsArticle: item),
isActive: $isShowingDetailView
) {
EmptyView()
}
)
}.padding(.bottom, 16)
.padding([.leading, .trailing], 16)
.background(Color.white)
.clipped()
.shadow(color: Color.gray.opacity(0.3), radius: 2, x: 0, y: 2)
}
}
}
}
}
private func processEvent(event: NewsListingEvents?) {
if event == nil { return }
switch event {
case let event as NewsListingEvents.OpenSelectedNews : do {
print("Event Received -> \(event)")
break
}
default:
print("Event not recognized")
break
}
}
}
In the above eg. vm.observeEvents
is the flow which gets updated by the ViewModel but the item is not received in the UI.
Please check the above code snippet and please share your thoughts.
Thanks!
You are only processing a single event during the initialisation of the view.
Depending on the implementation of the Flow this might be null
or an initial event.
Only the body
of your view will be reevaluated by SwiftUI upon state changes.
I think you are looking for onreceive(_:perform:)
.
The library is not generating the correct @property for the defined StateFlow.
Is there a script or command that needs to be executed for the correct code to be generated?