rickclephas / kmp-observableviewmodel Goto Github PK
View Code? Open in Web Editor NEWLibrary to use AndroidX/Kotlin ViewModels with SwiftUI
License: MIT License
Library to use AndroidX/Kotlin ViewModels with SwiftUI
License: MIT License
There's an issue when for example in shared part:
init { viewModelScope.coroutineScope.launch { while (true) { delay(2000) _homeUiState.update { it.copy(error = if (it.error == null) "error" else null) } } } }
When in SwiftUI using this view model as: @EnvironmentViewModel private var homeViewModel: HomeViewModel
and if you implement in SwiftUI screen:
init() { print("code 1 - init") }
.onChange(of: homeViewModel.homeUiState.error) { error in if error != nil { showAlert = true } else { showAlert = false } }
The screen will always be reinitialized. You will always get code 1 - init after state change.
@rickclephas we chatted a little about this and think you mentioned you might add JVM target at some point and just creating this to track. I can run projects I have that do include JVM target but just get error when syncing.
I'm not sure if this is an issue with Kotlin/Native
itself but I have a BaseViewModel with generic type T
which is used to construct a stateflow:
abstract class BaseViewModel<T: Any>(initialState: T) : KMMViewModel() {
protected val scope = viewModelScope.coroutineScope
private val _state = MutableStateFlow(viewModelScope, initialState)
@NativeCoroutinesState val state = _state.asStateFlow()
protected fun setState(newState: T) {
_state.value = newState
}
}
All viewmodels in the project subclass this like so:
class CurrenciesViewModel(
private val getCurrencies: GetCurrencies
) : BaseViewModel<CurrenciesViewModel.State>(State.Idle) {
init {
loadCurrencies()
}
sealed class State {
object Idle : State()
data class Content(
val currencies: Map<String, List<Currency>>,
) : State()
}
}
When using this in Xcode, viewModel.state
in Swift has a type of Any
, which means i have to cast it:
viewModel.state as! CurrenciesViewModel.State
Any idea why?
Thanks in advance
KMM-ViewModel 1.0.0-ALPHA20
Kotlin Multiplatform 1.9.23
When using KMM-ViewModel, I have a crash in XCode Preview:
== PREVIEW UPDATE ERROR:
CrashReportError: Fatal Error in ObservableViewModelPublishers.swift
MSCO Cash Register Simulator crashed due to fatalError in ObservableViewModelPublishers.swift at line 26.
ObservableViewModel has been deallocated
First of all thanks for all the awesome libs.
I have added this dependency in my shared folder.
api("com.rickclephas.kmm:kmm-viewmodel-core:1.0.0-ALPHA-9")
and created this class
class AccountViewModel : KMMViewModel() {}
and I am getting this error
Cannot access 'androidx.lifecycle.ViewModel' which is a supertype of 'com.example.account.AccountViewModel'. Check your module classpath for missing or conflicting dependencies
Am I missing some dependency or some configuration?
Issue:
I get a crash while pressing “submit” on keyboard of a TextField, that uses a published var from the viewModel.
“Thread 1: Simultaneous accesses to 0x600002e2b358, but modification requires exclusive access” on ObservableViewModel:25
Setup:
iOS viewModel is extending shared.ViewModel
on the viewModel side, the Published var used as input text binding for Textfield is defined like this:
@Published var username: String = "" {
didSet {
isUsernameValid = validateUsername(email: username)
validatePasswordLogin()
}
}
ViewModel var inside the View is annotated with @StateViewModel
struct LoginView: View {
@StateViewModel var viewModel: LoginViewModel
var body: some View {
TextField(text: $viewModel.username, label: {
Text("test")
})
}
}
Proposed fix:
Since simultaneous access could be avoided by doing the writing async, i tried the following and it fixed the crash - no idea if its a worthy solution here.
ObservableViewModel:35 - the function that does the setting of keypath, i wrapped that line inside a DispatchQueue.main.async
public func set<T>(_ keyPath: WritableKeyPath<ViewModel, T>, to newValue: T) {
DispatchQueue.main.async {
self._viewModel[keyPath: keyPath] = newValue
}
}
Versions on the iOS side:
pod 'KMMViewModelCore', :git => 'https://github.com/rickclephas/KMM-ViewModel.git', :commit => '047d8c597715d57be2951d121f686c2886e5a39f'
pod 'KMMViewModelCoreObjC', :git => 'https://github.com/rickclephas/KMM-ViewModel.git', :commit => '047d8c597715d57be2951d121f686c2886e5a39f'
pod 'KMMViewModelSwiftUI', :git => 'https://github.com/rickclephas/KMM-ViewModel.git', :commit => '047d8c597715d57be2951d121f686c2886e5a39f'
I have a KMMViewModel
subclass that contains a child view model that's also a KMMViewModel
subclass. The child view model is passed to a child view that is collapsible. I can collapse the view fine, but the app crashes when I try to expand it again.
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x104d49454 objc_retain + 16
1 iosApp 0x1049d2520 observableViewModel<A>(for:) + 104
2 iosApp 0x1049d1414 ObservedViewModel.init(wrappedValue:) + 68
3 iosApp 0x1049cd000 ChildView.init(viewModel:) + 180 (RootScreen.swift:30)
4 iosApp 0x1049cc914 closure #1 in RootScreen.body.getter + 1004 (RootScreen.swift:19)
5 iosApp 0x1049ccce0 partial apply for closure #1 in RootScreen.body.getter + 16
6 SwiftUI 0x1062294d0 0x105654000 + 12408016
7 iosApp 0x1049cc4bc RootScreen.body.getter + 344 (RootScreen.swift:10)
open class RootViewModel : KMMViewModel() {
@NativeCoroutinesState
val childViewModel: MutableStateFlow<ChildViewModel> = MutableStateFlow(viewModelScope, ChildViewModel("Child"))
}
open class ChildViewModel(name: String) : KMMViewModel() {
@NativeCoroutinesState
val name: MutableStateFlow<String> = MutableStateFlow(viewModelScope, name)
}
struct RootScreen: View {
@StateViewModel var viewModel = RootViewModel()
@State private var isExpanded: Bool = true
var body: some View {
HStack {
Button(
action: { isExpanded.toggle() },
label: { Image(systemName: isExpanded ? "chevron.right" : "chevron.left") }
)
if (isExpanded) {
ChildView(viewModel: viewModel.childViewModel)
}
Spacer()
}
.padding()
}
}
struct ChildView: View {
@ObservedViewModel var viewModel: ChildViewModel
init(viewModel: ChildViewModel) {
_viewModel = ObservedViewModel(wrappedValue: viewModel)
}
var body: some View {
Text(viewModel.name)
}
}
https://github.com/humblehacker/KMMViewModelCrashExample
>
to collapse the viewIt looks like when the view is collapsed, the child view model is destroyed. So when it's expanded again it crashes when we try to set the associated object on a dangling pointer.
Is there a better way to model this kind of relationship that might avoid this problem?
Just letting you know that the Android includes the generated BuildConfig
class. Libraries should avoid exporting these as they can easily clash with in application projects.
You can disable it globally by setting android.defaults.buildfeatures.buildconfig = false
in your gradle.properties
when i try to use any function of the shared viewmodel from the jvm(desktop) target i get this error, even though i added the coroutines as the log suggests i still get the same error
Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android' and ensure it has the same version as 'kotlinx-coroutines-core
Hi @rickclephas, here is a bit of context about the problem:
*Version: 1.0.0-ALPHA-8
*I've followed the README and for android everything is working OK. However, I added the package to Xcode and when I built the project it thrown an exception. Here is an extract of it:
The following Kotlin source sets were configured but not added to any Kotlin compilation: androidAndroidTestRelease androidTestFixtures androidTestFixturesDebug androidTestFixturesRelease You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'. See https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#connecting-source-sets Task :shared:compileKotlinIosSimulatorArm64 FAILED e: Could not find "org.jetbrains.kotlin.native.platform.CoreFoundationBase" in [/Users/pabloski/projects/ShoppingListClientMobile, /Users/pabloski/.konan/klib, /Users/pabloski/.konan/kotlin-native-prebuilt-macos-aarch64-1.7.20/klib/common, /Users/pabloski/.konan/kotlin-native-prebuilt-macos-aarch64-1.7.20/klib/platform/ios_simulator_arm64]
I then tried adding extension Kmm_viewmodel_coreKMMViewModel: KMMViewModel { }
but I receive this error in compile time Cannot find type 'Kmm_viewmodel_coreKMMViewModel' in scope
Thank you in advance.
In the following example, the ViewModel is instantiated and passed to a NavigationPath first and later is set to a View.
Unfortunately this throws the following error:
KMMViewModelCore/ObservableViewModel.swift:34: Fatal error: ObservableViewModel has been deallocated
Is this caused by the path
being @Published
? Is there a way around it?
var body: some Scene {
WindowGroup {
NavigationStack(path: $navController.path) {
RootView()
.navigationDestination(for: KMMViewModel.self) { destination in
ViewFactory.viewForDestination(destination)
}
}
.environmentObject(navController)
}
}
class ViewFactory {
@ViewBuilder
static func viewForDestination(_ destination: KMMViewModel) -> some View {
if let vm = destination as? TestViewModel {
TestView(viewModel: vm)
} else {
EmptyView()
}
}
}
class NavController: ObservableObject {
I var path = NavigationPath()
func show<VM>(_ vm: VM) where VM: KMMViewModel {
path.append(vm)
}
...
}
navController.show(TestViewModel() as KMMViewModel)
Environment:
I defined the viewModel in shared module and exported the module to xcframework and imported to other ios app project.
It is work without calling any function in viewModel.
Then I added packages of KMMViewModel and also the KMM Native Coroutines.
However when I follow the readme that create the file KMMViewModel.swift
import KMMViewModelCore
import shared
extension Kmm_viewmodel_coreKMMViewModel: KMMViewModel { }
It cause Cannot find type 'Kmm_viewmodel_coreKMMViewModel' in scope.
How to solve this?
First of all, many thanks for your efforts in creating KMM-ViewModel :)
Are there any plans on providing integration of any kind with SavedStateHandle and Parcelize? In our project we have our own custom implementation of shared navigation logic that is integrated in our KMMViewModels. Having SavedStateHandle + Parcelize would make us able to use it to pass arguments/results while navigating
Edit: though it would probably require integration with something like navController so I'm not sure if SavedStateHandle in KMMViewModel would help us at all. But could come in handy in some other cases also.
I'm using the latest Android Studio Canary and KMM/KMP plugins.
This won't compile and throws the following error.
class TestViewModel : KMMViewModel() {
@NativeCoroutinesState
var test = MutableStateFlow(viewModelScope, value = "Hello World!")
}
:shared:compileDebugKotlinAndroid
./TestViewModel.kt:9:16 Cannot inline bytecode built with JVM target 11 into bytecode that is being built with JVM target 1.8. Please specify proper '-jvm-target' option
I've tried to search for a solution and tried everything but nothing works.
Hi @rickclephas - any chance you can publish this to sonatype under as an open source project?
You can use github workflows for free to build and push it to sonatype.
Here is one of my KMM open source projects as an example
https://github.com/Reedyuk/blue-falcon
Thanks
Hey thanks for the great library! :)
I'm having issues with creating a Binding Variable for SwiftUI. My KMM code is the following:
class CommonAuthViewModel : KMMViewModel(), KoinComponent {
@NativeCoroutinesState
var isLoggedIn: StateFlow<Boolean> =
MutableStateFlow(viewModelScope, false)
fun changeState() {
isLoggedIn.value = true
}
}
In XCode I faced the following error:
In my view I initialized the ViewModel like the following:
@StateViewModel var authViewModel = AuthViewModel()
I'm also confused from XCode it shows that this variable is a Binding
I want to thank you for this library and also thanking for the good communication with the community, that is not a matter of course and I appreciate that very much!
Hi.
I've been following @joreilly's Confetti sample and I have my shared view model defined like
class MyViewModel() : KMMViewModel() {
val states: StateFlow<Int> = flow {
var count = 1
while (true) {
delay(1000)
println("EMITTING $count")
emit(count++)
}
}
.stateIn(this, SharingStarted.Eagerly, 0)
}
@NativeCoroutinesState
val MyViewModel.state: StateFlow<MainState> get() = states
This is consumed on the swift side like
import SwiftUI
import Combine
import KMMViewModelCore
import KMMViewModelSwiftUI
import KMPNativeCoroutinesCore
struct MyScreen: View {
@StateViewModel private var viewModel = .init()
var body: some View {
MyView(viewModel: $viewModel)
}
}
struct MyView: MyView {
@ObservedViewModel var viewModel: MyViewModel
init(viewModel: ObservableViewModel<MyViewModel>.Projection) {
self._viewModel = ObservedViewModel(viewModel)
}
var body: some View {
let _ = print("STATE CONSUMED", viewModel.state)
}
}
From the logger, it looks like only the first-ever state is consumed even though subsequent states are emitted
STATE CONSUMED 0
EMITTING 1
EMITTING 2
EMITTING 3
EMITTING 4
EMITTING 5
Is this the intended way to use @StateViewModel
/@ObservedViewModel
? Not sure if I'm missing something here.
Hi Rick, first of all great work. I'm using Compose for iOS in my KMM project hence I can't use this library currently. So do you have any date/month in your mind by which there will be support for Jetbrains Compose? Thanks
Hello I have in viewmodel variables like
private val _loginState = MutableStateFlow<LoginStatus>(viewModelScope, LoginStatus.NonAuthenticated) @NativeCoroutinesState val loginState = _loginState.asStateFlow()
but in iOS when I have created viewmodel I can't have access to this variable, functions are visible, calling that functions is working well.
Can somebody help me where I have issue?
It would be nice to provide an example with MVI architecture and declarative UI for both Android and iOS.
Hello, awesome project!
I'm using this package together with NativeCoroutines
and everything works great on both platforms but not in the Xcode preview. The preview will not load at all and crashes with this error log.
Incident Identifier: 7471A224-E102-4E3A-8665-6BC7B1FD3D4A
CrashReporter Key: 8B36B5A8-1965-D9C2-C099-610507FD1EF2
Hardware Model: MacBookAir10,1
Process: ios [2276]
Path: /Users/USER/Library/Developer/Xcode/UserData/Previews/Simulator Devices/3D6F16D8-2B4B-47FC-A471-DED791F64349/data/Containers/Bundle/Application/8492D742-B9D7-4F60-AB2F-CD4098EE7443/ios.app/ios
Identifier: duck.hansson.odd.ios
Version: 1.0 (1)
Code Type: ARM-64 (Native)
Role: Foreground
Parent Process: launchd_sim [72630]
Coalition: com.apple.CoreSimulator.SimDevice.3D6F16D8-2B4B-47FC-A471-DED791F64349 [16757]
Responsible Process: SimulatorTrampoline [64885]
Date/Time: 2023-05-17 20:01:11.3450 +0200
Launch Time: 2023-05-17 20:01:11.1845 +0200
OS Version: macOS 13.3.1 (22E261)
Release Type: User
Report Version: 104
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Triggered by Thread: 0
Kernel Triage:
VM - (arg = 0x0) pmap_enter retried due to resource shortage
VM - (arg = 0x0) pmap_enter retried due to resource shortage
VM - (arg = 0x0) pmap_enter retried due to resource shortage
VM - (arg = 0x0) pmap_enter retried due to resource shortage
VM - (arg = 0x0) pmap_enter retried due to resource shortage
Thread 0 Crashed:: Dispatch queue: BSXPCCnx:com.apple.dt.xcode-previews.systemservices (BSCnx:client:com.apple.dt.uv.agent-preview-nonui-service)
0 libsystem_kernel.dylib 0x1b17e3fa8 __pthread_kill + 8
1 libsystem_pthread.dylib 0x1b183812c pthread_kill + 256
2 libsystem_c.dylib 0x18012873c abort + 124
3 Shared 0x103910cec konan::abort() + 12
4 Shared 0x10393ded8 (anonymous namespace)::terminateWithUnhandledException(ObjHeader*)::$_1::operator()() const + 16
5 Shared 0x10393dcf0 void (anonymous namespace)::$_0::operator()<(anonymous namespace)::terminateWithUnhandledException(ObjHeader*)::$_1>((anonymous namespace)::terminateWithUnhandledException(ObjHeader*)::$_1) + 80
6 Shared 0x10393da00 (anonymous namespace)::terminateWithUnhandledException(ObjHeader*) + 12
7 Shared 0x10393d9bc (anonymous namespace)::processUnhandledException(ObjHeader*) + 64
8 Shared 0x1039532b4 kotlin::ProcessUnhandledException(ObjHeader*) + 188
9 Shared 0x103955e2c Kotlin_ObjCExport_trapOnUndeclaredException + 36
10 Shared 0x1036e5b14 objc2kotlin_kfun:duck.hansson.odd.shared.module.SharedModule#(){}duck.hansson.odd.shared.viewmodel.HomeViewModel + 252
11 ios 0x102392240 ContentView.init() + 120 (ContentView.swift:13)
12 ContentView.1.preview-thunk.dylib 0x10369a09c static ContentView_Previews.__preview__previews.getter + 52 (ContentView.swift:28)
13 ios 0x10239270c protocol witness for static PreviewProvider.previews.getter in conformance ContentView_Previews + 12
14 SwiftUI 0x108fb9428 0x108610000 + 10130472
15 ios 0x102392750 protocol witness for static _PreviewProvider._previews.getter in conformance ContentView_Previews + 40
16 SwiftUI 0x108fb9cc0 0x108610000 + 10132672
17 PreviewsInjection 0x1024bf134 0x10248c000 + 209204
18 PreviewsInjection 0x1024bf098 0x10248c000 + 209048
19 PreviewsInjection 0x1024b5d08 0x10248c000 + 171272
20 PreviewsInjection 0x1024ae2d4 0x10248c000 + 139988
21 PreviewsInjection 0x1024add14 0x10248c000 + 138516
22 PreviewsInjection 0x1024adf2c 0x10248c000 + 139052
23 BoardServices 0x184a2116c +[BSXPCServiceConnectionProxy invokeMethod:onTarget:withMessage:forConnection:] + 1136
24 BoardServices 0x184a2ff48 __63-[BSXPCServiceConnectionEventHandler connection:handleMessage:]_block_invoke + 536
25 BoardServices 0x184a53fcc BSXPCServiceConnectionExecuteCallOut + 232
26 BoardServices 0x184a2fbe0 -[BSXPCServiceConnectionEventHandler connection:handleMessage:] + 148
27 BoardServices 0x184a53554 -[BSXPCServiceConnection _connection_handleMessage:fromPeer:withHandoff:] + 512
28 libdispatch.dylib 0x180132ee4 _dispatch_call_block_and_release + 24
29 libdispatch.dylib 0x180134708 _dispatch_client_callout + 16
30 libdispatch.dylib 0x18013c77c _dispatch_lane_serial_drain + 776
31 libdispatch.dylib 0x18013d414 _dispatch_lane_invoke + 448
32 libdispatch.dylib 0x180143e4c _dispatch_main_queue_drain + 824
33 libdispatch.dylib 0x180143b04 _dispatch_main_queue_callback_4CF + 40
34 CoreFoundation 0x18039a784 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 12
35 CoreFoundation 0x180394de4 __CFRunLoopRun + 1912
36 CoreFoundation 0x180394254 CFRunLoopRunSpecific + 584
37 GraphicsServices 0x188eb7c9c GSEventRunModal + 160
38 UIKitCore 0x1050b2ff0 -[UIApplication _run] + 868
39 UIKitCore 0x1050b6f3c UIApplicationMain + 124
40 SwiftUI 0x10956734c 0x108610000 + 16085836
41 SwiftUI 0x1095671ec 0x108610000 + 16085484
42 SwiftUI 0x108d5f474 0x108610000 + 7664756
43 ios 0x1023936a4 static iosApp.$main() + 40 (iosApp.swift:11)
44 ios 0x10239374c main + 12
45 dyld_sim 0x10257d514 start_sim + 20
46 dyld 0x102671f28 start + 2236
Thread 1:
0 libsystem_pthread.dylib 0x1b1833634 start_wqthread + 0
Thread 2:
0 libsystem_pthread.dylib 0x1b1833634 start_wqthread + 0
Thread 3:: GC Timer thread
0 libsystem_kernel.dylib 0x1b17df694 __psynch_cvwait + 8
1 libsystem_pthread.dylib 0x1b18389e4 _pthread_cond_wait + 1220
2 libc++.1.dylib 0x180280a84 std::__1::condition_variable::__do_timed_wait(std::__1::unique_lockstd::__1::mutex&, std::__1::chrono::time_point<std::__1::chrono::system_clock, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l>>>) + 96
3 Shared 0x10394bcbc void kotlin::RepeatedTimerkotlin::steady_clock::Run<kotlin::gc::internal::GCSchedulerDataWithTimerkotlin::steady_clock::GCSchedulerDataWithTimer(kotlin::gc::GCSchedulerConfig&, std::__1::function<void ()>)::'lambda'()>(kotlin::gc::internal::GCSchedulerDataWithTimerkotlin::steady_clock::GCSchedulerDataWithTimer(kotlin::gc::GCSchedulerConfig&, std::__1::function<void ()>)::'lambda'()&&) + 556
4 Shared 0x10394c4d4 void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_deletestd::__1::__thread_struct>, void ()(kotlin::ScopedThread::attributes, void (kotlin::RepeatedTimerkotlin::steady_clock::&&)(kotlin::gc::internal::GCSchedulerDataWithTimerkotlin::steady_clock::GCSchedulerDataWithTimer(kotlin::gc::GCSchedulerConfig&, std::__1::function<void ()>)::'lambda'()&&) noexcept, kotlin::RepeatedTimerkotlin::steady_clock&&, kotlin::gc::internal::GCSchedulerDataWithTimerkotlin::steady_clock::GCSchedulerDataWithTimer(kotlin::gc::GCSchedulerConfig&, std::__1::function<void ()>)::'lambda'()&&), kotlin::ScopedThread::attributes, void (kotlin::RepeatedTimerkotlin::steady_clock::)(kotlin::gc::internal::GCSchedulerDataWithTimerkotlin::steady_clock::GCSchedulerDataWithTimer(kotlin::gc::GCSchedulerConfig&, std::__1::function<void ()>)::'lambda'()&&) noexcept, kotlin::RepeatedTimerkotlin::steady_clock, kotlin::gc::internal::GCSchedulerDataWithTimerkotlin::steady_clock::GCSchedulerDataWithTimer(kotlin::gc::GCSchedulerConfig&, std::__1::function<void ()>)::'lambda'()>>(void) + 260
5 libsystem_pthread.dylib 0x1b1838428 _pthread_start + 116
6 libsystem_pthread.dylib 0x1b1833648 thread_start + 8
Thread 4:: GC thread
0 libsystem_kernel.dylib 0x1b17df694 __psynch_cvwait + 8
1 libsystem_pthread.dylib 0x1b18389e4 _pthread_cond_wait + 1220
2 libc++.1.dylib 0x1802809f4 std::__1::condition_variable::wait(std::__1::unique_lockstd::__1::mutex&) + 24
3 Shared 0x103944d8c std::__1::invoke_result<kotlin::gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep(kotlin::mm::ObjectFactorykotlin::gc::ConcurrentMarkAndSweep&, kotlin::gc::GCScheduler&)::$_3>::type kotlin::ScopedThread::Run<kotlin::gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep(kotlin::mm::ObjectFactorykotlin::gc::ConcurrentMarkAndSweep&, kotlin::gc::GCScheduler&)::$_3>(kotlin::ScopedThread::attributes, kotlin::gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep(kotlin::mm::ObjectFactorykotlin::gc::ConcurrentMarkAndSweep&, kotlin::gc::GCScheduler&)::$_3&&) + 300
4 Shared 0x103944f08 void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_deletestd::__1::__thread_struct>, void ()(kotlin::ScopedThread::attributes, kotlin::gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep(kotlin::mm::ObjectFactorykotlin::gc::ConcurrentMarkAndSweep&, kotlin::gc::GCScheduler&)::$_3&&), kotlin::ScopedThread::attributes, kotlin::gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep(kotlin::mm::ObjectFactorykotlin::gc::ConcurrentMarkAndSweep&, kotlin::gc::GCScheduler&)::$_3>>(void) + 232
5 libsystem_pthread.dylib 0x1b1838428 _pthread_start + 116
6 libsystem_pthread.dylib 0x1b1833648 thread_start + 8
Thread 5:
0 libsystem_pthread.dylib 0x1b1833634 start_wqthread + 0
Thread 6:
0 libsystem_pthread.dylib 0x1b1833634 start_wqthread + 0
Thread 7:
0 libsystem_pthread.dylib 0x1b1833634 start_wqthread + 0
Thread 8:: com.apple.uikit.eventfetch-thread
0 libsystem_kernel.dylib 0x1b17dc190 mach_msg2_trap + 8
1 libsystem_kernel.dylib 0x1b17ed258 mach_msg2_internal + 76
2 libsystem_kernel.dylib 0x1b17e4398 mach_msg_overwrite + 540
3 libsystem_kernel.dylib 0x1b17dc500 mach_msg + 20
4 CoreFoundation 0x18039a4a8 __CFRunLoopServiceMachPort + 156
5 CoreFoundation 0x180394ad4 __CFRunLoopRun + 1128
6 CoreFoundation 0x180394254 CFRunLoopRunSpecific + 584
7 Foundation 0x180b994bc -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 208
8 Foundation 0x180b996e0 -[NSRunLoop(NSRunLoop) runUntilDate:] + 60
9 UIKitCore 0x105152714 -[UIEventFetcher threadMain] + 404
10 Foundation 0x180bbede0 NSThread__start + 704
11 libsystem_pthread.dylib 0x1b1838428 _pthread_start + 116
12 libsystem_pthread.dylib 0x1b1833648 thread_start + 8
Thread 0 crashed with ARM Thread State (64-bit):
x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x0000000000000000
x4: 0x0000600002b16a00 x5: 0x0000000000000000 x6: 0x0000000000000000 x7: 0x0000000000000000
x8: 0x0000000102708240 x9: 0x4055bf549d901fe4 x10: 0x000000016da6ee58 x11: 0x0000000158104558
x12: 0x0000000000000000 x13: 0x00000000000007fd x14: 0x00000000a5428870 x15: 0x00000000a5228072
x16: 0x0000000000000148 x17: 0x0000600002c100f0 x18: 0x0000000000000000 x19: 0x0000000000000006
x20: 0x0000000102708240 x21: 0x0000000000000103 x22: 0x0000000102708320 x23: 0x0000000102392700
x24: 0x000000016da6fd10 x25: 0x00000001023a8738 x26: 0x00000001bc8f5978 x27: 0x4000600000108d20
x28: 0x00000001bc8f5918 fp: 0x000000016da6f870 lr: 0x00000001b183812c
sp: 0x000000016da6f850 pc: 0x00000001b17e3fa8 cpsr: 0x40001000
far: 0x000004fdd18e4100 esr: 0x56000080 Address size fault
Binary Images:
0x10266c000 - 0x1026fbfff dyld () /usr/lib/dyld
0x10257c000 - 0x1025cbfff dyld_sim () <4eba7f04-0a30-3166-8a68-9125b8a1d5f9> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim
0x10238c000 - 0x1023a7fff duck.hansson.odd.ios (1.0) <58777694-be21-3b1c-8b91-846ec6038bd7> /Users/USER/Library/Developer/Xcode/UserData/Previews/Simulator Devices/3D6F16D8-2B4B-47FC-A471-DED791F64349/data/Containers/Bundle/Application/8492D742-B9D7-4F60-AB2F-CD4098EE7443/ios.app/ios
0x10248c000 - 0x1024d3fff com.apple.dt.PreviewsInjection (14.3) <2b6de7fe-add3-34d3-beb7-5907656cc116> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/PreviewsInjection.framework/PreviewsInjection
0x1036b4000 - 0x103ccffff duck.hansson.odd.shared.Shared (1.0) /Users/USER/Library/Developer/Xcode/UserData/Previews/Simulator Devices/3D6F16D8-2B4B-47FC-A471-DED791F64349/data/Containers/Bundle/Application/8492D742-B9D7-4F60-AB2F-CD4098EE7443/ios.app/Frameworks/Shared.framework/Shared
0x108610000 - 0x109bd3fff com.apple.SwiftUI (4.4.36.1.102) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/SwiftUI.framework/SwiftUI
0x102a10000 - 0x102a77fff libswiftUIKit.dylib () /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/swift/libswiftUIKit.dylib
0x102534000 - 0x102537fff com.apple.UIKit (1.0) <7e2b5d6b-224b-39a1-af92-5f3909a01490> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/UIKit.framework/UIKit
0x1028b4000 - 0x1028fbfff com.apple.DocumentManager (1.0) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/DocumentManager.framework/DocumentManager
0x104634000 - 0x105d17fff com.apple.UIKitCore (1.0) <6726ae46-2599-3f92-adca-c48a0512d4ea> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore
0x102fd0000 - 0x1030bffff com.apple.ShareSheet (1885) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/ShareSheet.framework/ShareSheet
0x102764000 - 0x1027effff com.apple.PrintKitUI (1.0) <74ff1df9-3f27-335a-9673-8991789c7e21> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/PrintKitUI.framework/PrintKitUI
0x102b6c000 - 0x102d03fff com.apple.WebKitLegacy (8615) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Cryptexes/OS/System/Library/PrivateFrameworks/WebKitLegacy.framework/WebKitLegacy
0x10de3c000 - 0x10f2f3fff com.apple.JavaScriptCore (8615) <6c92ecb5-f484-3925-b8ac-132e313d11cc> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Cryptexes/OS/System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore
0x117588000 - 0x119d5ffff com.apple.WebCore (8615) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Cryptexes/OS/System/Library/PrivateFrameworks/WebCore.framework/WebCore
0x10a700000 - 0x10aa67fff libANGLE-shared.dylib () <094a5d7d-536f-3c65-bf0f-42037de47f30> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Cryptexes/OS/System/Library/PrivateFrameworks/WebCore.framework/Frameworks/libANGLE-shared.dylib
0x102964000 - 0x1029bbfff com.apple.WebGPU (8615) <84b58cce-be36-3c7e-8f54-801c8cb3c7fa> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Cryptexes/OS/System/Library/PrivateFrameworks/WebGPU.framework/WebGPU
0x1077cc000 - 0x10821ffff libwebrtc.dylib () /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Cryptexes/OS/System/Library/PrivateFrameworks/WebCore.framework/Frameworks/libwebrtc.dylib
0x102610000 - 0x102623fff com.apple.RecapPerformanceTesting (17) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/RecapPerformanceTesting.framework/RecapPerformanceTesting
0x10ac74000 - 0x10addffff com.apple.chronokit (1.0) <646ed4dc-664b-37b1-99c5-350381e4621b> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/ChronoKit.framework/ChronoKit
0x10aeb8000 - 0x10afd7fff com.apple.widgetkit (1.0) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/WidgetKit.framework/WidgetKit
0x102544000 - 0x10255bfff com.apple.dt.PreviewsOSSupportUI (14.3) <5bab1eb4-c81c-3e3a-8edc-b15bcacdb085> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/PreviewsOSSupportUI.framework/PreviewsOSSupportUI
0x102f68000 - 0x102f93fff com.apple.dt.PreviewsServicesUI (14.3) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/PreviewsServicesUI.framework/PreviewsServicesUI
0x102ebc000 - 0x102ee3fff com.apple.BaseBoardUI (617.107) <173814de-7bee-3fd3-a6cc-3fd9f89a0bd1> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/BaseBoardUI.framework/BaseBoardUI
0x1032dc000 - 0x103303fff com.apple.chronouiservices (1.0) <0d419660-ac2f-30c9-8a2c-99f6052e7224> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/ChronoUIServices.framework/ChronoUIServices
0x103344000 - 0x10335ffff com.apple.MaterialKit (1.0) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/MaterialKit.framework/MaterialKit
0x103214000 - 0x10326bfff com.apple.internal.ActivityUIServices (1.0) <4c169598-dc07-3244-8fd2-c66a18c93f5d> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/ActivityUIServices.framework/ActivityUIServices
0x1034d4000 - 0x103517fff com.apple.PlatterKit (1.0) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/PlatterKit.framework/PlatterKit
0x103434000 - 0x10344bfff libswiftExtensionKit.dylib () <3b6af021-32be-3dfc-9feb-cca7b5deb936> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/swift/libswiftExtensionKit.dylib
0x102f1c000 - 0x102f33fff com.apple.ExtensionKit (97) <7c6fe311-0f0c-32d2-b323-5f0e25540283> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/ExtensionKit.framework/ExtensionKit
0x10b588000 - 0x10b697fff com.apple.preferences-framework (1) <57984a86-c974-3534-8fa5-c3d2e29b4be7> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/Preferences.framework/Preferences
0x10b370000 - 0x10b40bfff com.apple.BacklightServicesHost (1.0) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/BacklightServicesHost.framework/BacklightServicesHost
0x103484000 - 0x103497fff com.apple.settingsandcoreapps.SettingsFoundation (1.0) <6b8b3380-36ef-39a4-9171-22a55f2dc229> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/SettingsFoundation.framework/SettingsFoundation
0x103418000 - 0x103423fff libobjc-trampolines.dylib () <806ae646-0aee-359b-883f-e4018780ec94> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc-trampolines.dylib
0x103698000 - 0x10369bfff ContentView.1.preview-thunk.dylib () /Users/USER/Library/Developer/Xcode/DerivedData/ios-gqblrasochcoajatuyocronxorua/Build/Intermediates.noindex/Previews/ios/Intermediates.noindex/ios.build/Debug-iphonesimulator/ios.build/Objects-normal/arm64/ContentView.1.preview-thunk.dylib
0x1b17db000 - 0x1b1812fe7 libsystem_kernel.dylib () /usr/lib/system/libsystem_kernel.dylib
0x1b1831000 - 0x1b183dff7 libsystem_pthread.dylib () <73f649ed-142d-3aad-b3af-3fe33548b725> /usr/lib/system/libsystem_pthread.dylib
0x1800b6000 - 0x180130ffb libsystem_c.dylib () <6b3ced39-3f0d-3bb2-a2b1-34062cf5c8eb> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/libsystem_c.dylib
0x184a1c000 - 0x184a6efff com.apple.BoardServices (1.0) <170a1733-6f68-37eb-bc4b-1379497bffc0> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/BoardServices.framework/BoardServices
0x180131000 - 0x180177fff libdispatch.dylib () <392b7c55-8c38-3dea-b7af-c3d7f518e987> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/libdispatch.dylib
0x180315000 - 0x1806c3fff com.apple.CoreFoundation (6.9) <132e87d0-14ac-310c-a5e9-3d9c921cc8ea> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
0x188eb4000 - 0x188ebcfff com.apple.GraphicsServices (1.0) <8e24edb2-1c99-3652-9ef0-e66191675515> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices
0x18026e000 - 0x1802efff7 libc++.1.dylib (*) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libc++.1.dylib
0x18073a000 - 0x180f8bfff com.apple.Foundation (6.9) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation
EOF
The code that crashed the preview is this view binding of a view model. If it's removed the preview works.
struct ContentView: View {
@ObservedViewModel var viewModel = SharedModule().homeViewModel
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text(viewModel.state.query)
}
.padding()
}
}
I'm using Koin for dependency injection and I've confirmed it's not the issue as this code works in the preview.
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text(SharedModule().homeViewModel.state.query)
}
.padding()
}
}
Compose for iOS just got Alpha yesterday. Is there any way that this library will support
shared view model that can be used in the shared module in compose Multiplatform without having to access it in native
Android (jetpack compose and android lifecycles) and iOS (swift and swift ui)
So, is there any chance this project will continue with those changes?
1.8.10
1.6.4
1.0.0-ALPHA-4
1.0.0-ALPHA-5
I tried KMM-ViewModel with a basic example of a ViewModel usage, but I get the following error. Not sure if this is something I'm doing or an incompatibility with the current build.
Uncaught Kotlin exception: kotlin.IllegalStateException: KMMViewModel can't be wrapped more than once
at 0 iosApp 0x109c2c99b kfun:kotlin.Throwable#<init>(kotlin.String?){} + 107
at 1 iosApp 0x109c262c7 kfun:kotlin.Exception#<init>(kotlin.String?){} + 103
at 2 iosApp 0x109c26627 kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 103
at 3 iosApp 0x109c26b47 kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 103
at 4 iosApp 0x109e7c359 kfun:com.rickclephas.kmm.viewmodel.ViewModelScopeImpl#objc:setSendObjectWillChange: + 265
at 5 iosApp 0x109e7cc44 _636f6d2e7269636b636c65706861732e6b6d6d3a6b6d6d2d766965776d6f64656c2d636f72652f55736572732f72756e6e65722f776f726b2f4b4d4d2d566965774d6f64656c2f4b4d4d2d566965774d6f64656c2f6b6d6d2d766965776d6f64656c2d636f72652f7372632f6170706c654d61696e2f6b6f746c696e2f636f6d2f7269636b636c65706861732f6b6d6d2f766965776d6f64656c2f566965774d6f64656c53636f70652e6b74_knbridge34 + 292
iOS:
struct ContentView: View {
@StateViewModel private var contentViewModel = NotesViewModel()
var body: some View {
VStack {
AddNoteView(contentViewModel: contentViewModel)
NotesView(contentViewModel: contentViewModel)
}
}
}
...
shared:
class NotesViewModel: KMMViewModel() {
private val noteRepository = NoteRepository()
@NativeCoroutinesState
val notes = noteRepository.todoList
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
...
Similar to KMP-NativeCoroutines, is it possible to publish the swift implementations to Cocopods?
Given the following property in a Kotlin view model class:
@NativeCoroutineState
val price: MutableStateFlow<Double>
it is exposed to Swift as:
Binding<Double>
But if I make it nullable:
@NativeCoroutineState
val price: MutableStateFlow<Double?>
I get this in Swift:
Binding<KotlinDouble?>
which requires ugly conversion on the Swift side.
Is there something I can do to improve this?
I'm playing around a bit with KMM-Viewmodel
and SwiftUI and for example sharing state for a TextField
works great but updating a boolean does not re-render the view in SwiftUI.
This is my example ViewModel that holds the showPassword
boolean and some extra state.
open class LoginViewModel : KMMViewModel() {
var email: String = ""
var password: String = ""
var showPassword: Boolean = false
fun changeEmail(email: String) {
this.email = email
}
fun changePassword(password: String) {
this.password = password
}
fun toggleShowPassword() {
showPassword = !showPassword
}
}
This is how I toggle the showPassword
boolean in my iOS app and I expected the view to re-render and the two TextFields and the icon to switch but that doesn't happen.
...
VStack(spacing: Padding.l) {
TextField(
"Email",
text: $loginViewModel.email
)
Group {
if loginViewModel.showPassword {
TextField(
"Password",
text: Binding(get: {loginViewModel.password}, set: loginViewModel.changePassword)
)
} else {
SecureField(
"Password",
text: Binding(get: {loginViewModel.password}, set: loginViewModel.changePassword)
)
}
}
Button(
action: { loginViewModel.toggleShowPassword() },
label: {
Image(systemName: loginViewModel.showPassword ? "eye" : "eye.slash")
}
)
}
Is this behaviour expected and just not yet supported or am I doing something wrong?
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?
Hello!
I need to map a list of items that come from the shared View Model into objects that implement the Identifiable
protocol (needed for showing them in a List or pass them to Maps and display markers).
This is the exposed state from the ViewModel in Kotlin:
// ui state data class
data class HomeUiState(
val isLoading: Boolean = true,
val closeVenues: List<Venue> = emptyList(),
val errorMessage: String? = null
)
// state defined in ViewModel.kt inside shared module
@NativeCoroutinesState
val uiState = _uiState.asStateFlow().stateIn(
viewModelScope, SharingStarted.WhileSubscribed(), HomeUiState()
)
And, in my iOS app, I'm trying to map it like this:
class HomeViewModel : shared.HomeViewModel {
@Published var venues: [UiVenue] = []
override init() {
super.init()
venues = uiState.closeVenues.compactMap { venue in
venue.toUiVenue() // the UiVenue struct implements Identifiable protocol
}
}
}
...
// ContentView.swift
@ObservedViewModel var viewModel = HomeViewModel()
var body: some View {
List(viewModel.venues) { venue in
Text("Hello \(venue.name)").foregroundColor(Color.black)
}.onAppear {
viewModel.getCloseVenues()
}
}
However, the list is not rendering. I've tried using @State also, but nothing happens. The uiState.closeVenues
returns a simple [Venue]
object, not a Combine Observable.
However, if I use directly viewModel.uiState.closeVenues
from the view, it works fine 🤔
Environment
PS: this library is great, it has saved me a lot of time
At present, my project supports up to Kotlin 1.6.21 version. Due to the upgrade of Kotlin version, it has a great impact on the project. Can you provide a version of Kotlin 1.6.21?
First off all, KMM-Viewmodel
is great and even better in combination with KMP-NativeCoroutines
, so thanks a lot for creating both.
I'm currently trying to update a MutableStateFlow
inside a suspending function, but doing that gives me the following warning since updates to the ui need to be done on the main thread:
[SwiftUI] Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
So here is my suspending function and how I call it:
class LoginViewModel : KMMViewModel() {
@NativeCoroutinesState
var loginLoading = MutableStateFlow(viewModelScope, false)
suspend fun login() {
loginLoading.update { true }
val res = loginUseCase(email.value, password.value)
res.fold(
onSuccess = {
appViewModel.dismissAuthFlow()
appViewModel.setAccount(it)
},
onFailure = {
appViewModel.setAccount(null)
}
)
loginLoading.update { false }
}
}
In SwiftUI I use a Task to run the async function:
Button(
action: {
Task {
try await viewModel.login()
}
},
label: {
ZStack {
if viewModel.loginLoading {
ProgressView()
.colorInvert()
} else {
Text("Log in")
}
}
}
)
I have experimented a bit on how to get it to work and wrapping all updates to the ui with withContext(Dispatchers.Main)
does the trick. The working suspending function would look like this:
suspend fun login() {
withContext(Dispatchers.Main) {
loginLoading.update { true }
}
val res = loginUseCase(email.value, password.value)
withContext(Dispatchers.Main) {
res.fold(
onSuccess = {
appViewModel.dismissAuthFlow()
appViewModel.setAccount(it)
},
onFailure = {
appViewModel.setAccount(null)
}
)
loginLoading.update { false }
}
}
Is there a better way to update the ui from a suspending function than manually dispatching the updates on the main thread, to me it looks like this could be handled by the MutableStateFlow
implementation for iOS.
When creating a ViewModel in a parent view and passing the reference to a child view which wraps it with any of the property wrappers from KMM-Viewmodel
, the app crashes on a rerender of the child view with the error:
Uncaught Kotlin exception: kotlin.IllegalStateException: KMMViewModel can't be wrapped more than once
I know that the error is intentionally thrown since the stored ViewModel is already wrapped and not recreated on rerender of the child, but to me this looks like a common pattern that should be supported.
Why is it currently not supported and is it technically possible?
This is my setup, where the parent handles navigation between some screens and also holds the ViewModels for each screen.
I don't want to recreate the ViewModels on each recreation of the view, as I want to save the state between rerenders.
class NavigationViewModel: ObservableObject {
@Published var destination: Destination = .home
let mapVM = MapViewModel()
enum Destination: Hashable {
case home
case map(MapViewModel)
case profile
}
}
struct MainNavigationView: View {
@ObservedObject var viewModel: MainNavigationViewModel
var body: some View {
VStack {
Group {
// this is from the swiftui-navigation library, it just shows the view for the current `destination`
IfCaseLet($viewModel.destination, pattern: /MainNavigationViewModel.Destination.home) { $vm in
HomeView()
}
IfCaseLet($viewModel.destination, pattern: /MainNavigationViewModel.Destination.map) { $vm in
MapView(
viewModel: vm
)
}
IfCaseLet($viewModel.destination, pattern: /MainNavigationViewModel.Destination.profile) { $vm in
ProfileView()
}
}
.frame(maxWidth: .infinity,maxHeight: .infinity)
}
}
}
The Subview wraps the passed ViewModel with ObservedViewModel
struct MapView: View {
@ObservedViewModel var viewModel: MapViewModel
var body: some View {
Text("SubView")
}
}
I cannot implement ViewModel on Android. iOS works fine. Tried it on ALPHA-12 and ALPHA-10.
Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
class me.blanik.sample.SignInViewModel, unresolved supertypes: com.rickclephas.kmm.viewmodel.KMMViewModel
Adding -Xextended-compiler-checks argument might provide additional information.
plugins {
kotlin("multiplatform")
kotlin("native.cocoapods")
id("com.android.library")
id("com.google.devtools.ksp") version "1.9.0-1.0.11"
id("com.rickclephas.kmp.nativecoroutines") version "1.0.0-ALPHA-13"
kotlin("plugin.serialization") version "1.7.20"
}
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
androidTarget()
jvmToolchain(11)
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("../iosApp/Podfile")
framework {
baseName = "shared"
}
}
sourceSets {
val multiplatformSettingsVersion = "1.0.0"
val kmmViewModelVersion = "1.0.0-ALPHA-12"
all {
languageSettings.optIn("kotlin.experimental.ExperimentalObjCName")
}
val commonMain by getting {
dependencies {
implementation("com.russhwolf:multiplatform-settings-no-arg:$multiplatformSettingsVersion")
implementation("com.russhwolf:multiplatform-settings-serialization:$multiplatformSettingsVersion")
implementation("com.russhwolf:multiplatform-settings-coroutines:$multiplatformSettingsVersion")
implementation("com.rickclephas.kmm:kmm-viewmodel-core:$kmmViewModelVersion")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting {
dependencies {
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
}
}
val androidUnitTest 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 {
}
}
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 = "me.blanik.sample"
compileSdk = 33
defaultConfig {
minSdk = 28
}
// @TODO: Remove workaround for https://issuetracker.google.com/issues/260059413
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
package me.blanik.sample.android
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import me.blanik.sample.Greeting
import me.blanik.sample.SignInViewModel
class MainActivity : ComponentActivity() {
private val viewModel: SignInViewModel() by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
GreetingView(Greeting().greet())
GreetingView(viewModel.email.value)
}
}
}
}
}
@Composable
fun GreetingView(text: String) {
Text(text = text)
}
@Preview
@Composable
fun DefaultPreview() {
MyApplicationTheme {
GreetingView("Hello, Android!")
}
}
package me.blanik.sample
import com.rickclephas.kmm.viewmodel.*
import kotlinx.coroutines.flow.*
open class SignInViewModel: KMMViewModel() {
private val _email = MutableStateFlow(viewModelScope, "")
private val _password = MutableStateFlow(viewModelScope, "")
val email = _email.asStateFlow()
val password = _password.asStateFlow()
fun setEmail(email: String) {
_email.value = email
}
fun setPassword(password: String) {
_password.value = password
}
}
I've used the 1.0.0-ALPHA-7 version of the library and followed the README to define a view model in my shared KMM module. When building the project I get the following error:
Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath: class com.rickclephas.kmm.viewmodel.KMMViewModel, unresolved supertypes: androidx.lifecycle.ViewModel
I'm not sure why I'm getting this error because as far as I can tell the KMM-ViewModel transitively adds the dependency for the Android ViewModel.
I'm getting a lot of errors in production relating to ktor calls being cancelled in the viewmodel when the viewModelScope
is cleared.
I have the following stack trace:
Non-fatal Exception: io.github.jan.supabase.exceptions.HttpRequestException
HTTP request to http://localhost?user_id=eq.<user_id>&reading_status=eq.FINISHED&finish_date=gte.2024-01-01T00%3A00&select=%2A (GET) failed with message: Job was cancelled
io.github.jan.supabase.network.KtorSupabaseHttpClient.request (KtorSupabaseHttpClient.kt:55)
io.github.jan.supabase.network.KtorSupabaseHttpClient$request$1.invokeSuspend (KtorSupabaseHttpClient.kt:12)
kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:102)
kotlinx.coroutines.EventLoop.processUnconfinedEvent (EventLoop.common.kt:65)
kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined (DispatchedTask.kt:241)
kotlinx.coroutines.DispatchedTaskKt.dispatch (DispatchedTask.kt:159)
kotlinx.coroutines.CancellableContinuationImpl.dispatchResume (CancellableContinuationImpl.kt:470)
kotlinx.coroutines.CancellableContinuationImpl.cancel (CancellableContinuationImpl.kt:213)
kotlinx.coroutines.CancellableContinuationImpl.parentCancelled$kotlinx_coroutines_core (CancellableContinuationImpl.kt:220)
kotlinx.coroutines.ChildContinuation.invoke (JobSupport.kt:1447)
kotlinx.coroutines.JobSupport.notifyCancelling (JobSupport.kt:1473)
kotlinx.coroutines.JobSupport.tryMakeCancelling (JobSupport.kt:796)
kotlinx.coroutines.JobSupport.makeCancelling (JobSupport.kt:756)
kotlinx.coroutines.JobSupport.cancelImpl$kotlinx_coroutines_core (JobSupport.kt:672)
kotlinx.coroutines.JobSupport.parentCancelled (JobSupport.kt)
kotlinx.coroutines.ChildHandleNode.invoke (JobSupport.kt:1436)
kotlinx.coroutines.JobSupport.notifyCancelling (JobSupport.kt:1473)
kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath (JobSupport.kt:901)
kotlinx.coroutines.JobSupport.tryMakeCompleting (JobSupport.kt:864)
kotlinx.coroutines.JobSupport.cancelMakeCompleting (JobSupport.kt:697)
kotlinx.coroutines.JobSupport.cancelImpl$kotlinx_coroutines_core (JobSupport.kt:668)
kotlinx.coroutines.JobSupport.cancelInternal (JobSupport.kt)
kotlinx.coroutines.JobSupport.cancel (JobSupport.kt:618)
kotlinx.coroutines.JobKt__JobKt.cancel (Job.kt:560)
kotlinx.coroutines.JobKt.cancel (Job.kt)
kotlinx.coroutines.JobKt__JobKt.cancel$default (Job.kt:559)
kotlinx.coroutines.JobKt.cancel$default (Job.kt)
androidx.lifecycle.CloseableCoroutineScope.close (ViewModel.kt:51)
androidx.lifecycle.ViewModel.closeWithRuntimeException (ViewModel.kt:252)
androidx.lifecycle.ViewModel.clear (ViewModel.java:189)
androidx.lifecycle.ViewModelStore.clear (ViewModelStore.kt:69)
androidx.navigation.NavControllerViewModel.clear (NavControllerViewModel.kt:33)
androidx.navigation.NavController$NavControllerNavigatorState.markTransitionComplete (NavController.kt:359)
androidx.navigation.NavController.unlinkChildFromParent$navigation_runtime_release (NavController.kt:163)
androidx.navigation.NavController$NavControllerNavigatorState.markTransitionComplete (NavController.kt:352)
androidx.navigation.compose.ComposeNavigator.onTransitionComplete (ComposeNavigator.kt:82)
androidx.navigation.compose.NavHostKt$NavHost$15.invokeSuspend (NavHost.kt:314)
kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33)
kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:104)
androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch (AndroidUiDispatcher.android.kt:81)
androidx.compose.ui.platform.AndroidUiDispatcher.access$setScheduledFrameDispatch$p (AndroidUiDispatcher.android.kt)
androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch (AndroidUiDispatcher.android.kt)
androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run (AndroidUiDispatcher.android.kt:57)
android.os.Handler.handleCallback (Handler.java:942)
android.os.Handler.dispatchMessage (Handler.java:99)
android.os.Looper.loopOnce (Looper.java:226)
android.os.Looper.loop (Looper.java:313)
android.app.ActivityThread.main (ActivityThread.java:8757)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1067)
These lines in particular make it look like the ViewModel is being cleared, and the coroutine that launches the database call is being cancelled and the data is lost:
androidx.lifecycle.ViewModel.closeWithRuntimeException (ViewModel.kt:252)
androidx.lifecycle.ViewModel.clear (ViewModel.java:189)
androidx.lifecycle.ViewModelStore.clear (ViewModelStore.kt:69)
androidx.navigation.NavControllerViewModel.clear (NavControllerViewModel.kt:33)
androidx.navigation.NavController$NavControllerNavigatorState.markTransitionComplete (NavController.kt:359)
androidx.navigation.NavController.unlinkChildFromParent$navigation_runtime_release (NavController.kt:163)
androidx.navigation.NavController$NavControllerNavigatorState.markTransitionComplete (NavController.kt:352)
androidx.navigation.compose.ComposeNavigator.onTransitionComplete (ComposeNavigator.kt:82)
androidx.navigation.compose.NavHostKt$NavHost$15.invokeSuspend (NavHost.kt:314)
Users are seeing this on app startup on the home screen, and seemingly randomly. Some have reported that killing and restarting the app fixes it temporarily. Some have reinstalled and said that works for a little while, but the issue seems to be happening intermittently and I'm not able to reproduce it consistently on my end.
Any idea what might be causing this?
The readme claims that it's possible to subclass my view model in Swift:
class TimeTravelViewModel: shared.TimeTravelViewModel {
... but when I try I get the error Cannot inherit from non-open class 'MyViewModel' outside of its defining module. Any advice?
Hi! Thanks for creating KMMViewModel! 🙇🏼
I have started using KMMViewModel like this, also I have setup the extension for Kmm_viewmodel_coreKMMViewModel
like mentioned in the docs.
class LoginViewModel() : KMMViewModel() {
@NativeCoroutinesState
val uiState = MutableStateFlow(LoginUIState())
}
on iOS I was able to initialise the VM in my View
struct LoginScreen: View {
@ObservedViewModel var viewModel : LoginViewModel
init() {
self.viewModel = LoginViewModel()
}
var body: some View {
viewModel.uiState // this never updates
}
}
The problem is that the uiState
never updates even if the ViewModel emits new values to the state flow , am i doing something wrong here ?
Interfaces are useful in case of faking in previews.
For example:
interface ItemViewModel {
@NativeCoroutinesState val title: MutableStateFlow<String>
@NativeCoroutinesState val isEditing: StateFlow<Boolean>
@NativeCoroutinesState val canSave: StateFlow<Boolean>
}
class ItemViewModelImpl(
private val heavyDependency1: HeavyDependency1,
private val heavyDependency2: HeavyDependency2,
...
private val heavyDependency101: HeavyDependency101,
): KMMViewModel(), ItemViewModel {
override val title = MutableStateFlow("")
override val isEditing = MutableStateFlow(false)
override val canSave = MutableStateFlow(false)
}
class ItemViewModelFake(
title: String,
isEditing: Boolean = false,
canSave: Boolean = false,
): KMMViewModel(), ItemViewModel {
override val title = MutableStateFlow(title)
override val isEditing = MutableStateFlow(isEditing)
override val canSave = MutableStateFlow(canSave)
}
@Composable
fun Item(viewModel: ViewModel /*for the sake of simplicity without DI*/) {
// some UI
}
And use them for previews:
@Preview
@Composable
private fun ItemPreview() {
MaterialTheme {
Item(
ViewModelFake(title = "123")
)
}
}
@Preview
@Composable
private fun ItemEditingCannotSavePreview() {
MaterialTheme {
Item(
ViewModelFake(title = "123", isEditing = true)
)
}
}
@Preview
@Composable
private fun ItemEditingCanSavePreview() {
MaterialTheme {
Item(
ViewModelFake(title = "123", isEditing = true, canSave = true)
)
}
}
What about SwiftUI?
Hi!
I just switch to KMM ViewModel to make multiplatform viewmodels, and I encountered this weird issue:
On Android, to dispatch network requests I normally use:
viewModelScope.launch {
println("Hello")
}
So it's logical to replace it by this using KMM ViewModel:
viewModelScope.coroutineScope.launch {
println("Hello")
}
But the body of the launch call is never executed... (I first had real network requests but added prints and understood that they were not called with KMM implementation)
I found a workaround using GlobalScope.launch
which works, but might cause lifecycle issues, so I would prefer to get a working viewModelScope.coroutineScope.launch
.
I looked at the native implementation of Android coroutineScope and the difference I see is that on KMM ViewModel we have:
CoroutineScope(SupervisorJob() + Dispatchers.Main)
While on AndroidX it's implemented this way:
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
So this might be a place to look for a fix.
Anyone else is encountering this issue?
I'm using Koin DI to inject singleton ViewModels into my iOS app - I have the following LoginScreen defined:
struct LoginScreen: View {
@ObservedViewModel var viewModel: LoginViewModel = Koin.instance.get()
...
}
I created a SplashScreen that also depends on the LoginViewModel, but this code fails with the error kotlin.IllegalStateException: KMMViewModel can't be wrapped more than once
struct SplashScreen: View {
@ObservedViewModel var loginViewModel: LoginViewModel = Koin.instance.get()
...
}
If I make the ViewModel manually (so it's not the same object as the one in LoginScreen), it doesn't get the error when run, but loses the benefit of being a singleton between the two screens.
struct SplashScreen: View {
@ObservedViewModel var loginViewModel: LoginViewModel = LoginViewModel(authRepository: Koin.instance.get())
...
}
There are a couple options here - I could pass the ViewModel from one screen to the next, but that tightly couples one view to the one before it, which I don't want. I could also create a SplashScreenViewModel
and use that, but there are other places in my app that are going to need to share instances of the same ViewModel, so this would become an issue again very quickly.
This is not an issue on Android, you can inject a singleton ViewModel into as many screens as you want without getting this error.
Is this something that could be fixed at the library level, or is there another way to access singleton ViewModels in multiple screens?
First of all, awesome work with NativeCoroutines and this package.
Hi Rick. I'm trying to use Kmm-ViewModel in my project. I am getting the following error. Any idea how I can resolve this?
Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
class com.rickclephas.kmm.viewmodel.KMMViewModel, unresolved supertypes: androidx.lifecycle.ViewModel
Adding -Xextended-compiler-checks argument might provide additional information.
Edited:
I figured it out. I was missing viewmodel dependency in the android module.
Hey,
on android side Koin DI
allows passing constructor parameters to VM like item id etc. But on iOS, this is not possible. If I try to initialize ViewModel
inside init block of my View
, it doesn't compile saying get-only value can't be assigned. I'm not very familiar with swift and might be doing something wrong, if thats the case some guidance would be appreciated
Something without KMM because @hhariri doesn't like it 😜
Hi, I've tried to add the KMM-ViewModel to my project, however I've faced one big issue. I've prepared a branch that presents this issue: AKJAW/Timi-Multiplatform@cb8888f
Here's the code for the ViewModel:
import com.rickclephas.kmm.viewmodel.stateIn
import kotlinx.coroutines.flow.stateIn as normalStateIn
class TestViewModel : KMMViewModel() {
val intViewModelScopeFlow: StateFlow<Int> = createFlow()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
val intCoroutinesCopeFlow: StateFlow<Int> = createFlow()
.normalStateIn(viewModelScope.coroutineScope, SharingStarted.WhileSubscribed(), 0)
private fun createFlow() = flow {
var i = 1
while (true) {
delay(1000)
emit(i++)
}
}
}
From the source code it seems that com.rickclephas.kmm.viewmodel.stateIn uses the equivalent of what I've used
/**
* @see kotlinx.coroutines.flow.stateIn
*/
@Suppress("NOTHING_TO_INLINE")
public actual inline fun <T> Flow<T>.stateIn(
viewModelScope: ViewModelScope,
started: SharingStarted,
initialValue: T
): StateFlow<T> = stateIn(viewModelScope.coroutineScope, started, initialValue)
But the StateFlow which uses the normalStateIn is not correctly updated on iOS:
struct TaskListScreen: View {
@StateViewModel var testViewModel = TestViewModel()
var body: some View {
NavigationView {
VStack {
Text("ViewModelScope \(testViewModel.intViewModelScopeFlowNativeValue)")
Text("CoroutinesScope \(testViewModel.intCoroutinesCopeFlowNativeValue)")
}
}
}
}
On Android both of the StateFlows behave in the same way:
Column {
val viewModel = viewModel<TestViewModel>()
val vm = viewModel.intViewModelScopeFlow.collectAsState().value
Text("ViewModelScope $vm")
val normal = viewModel.intCoroutinesCopeFlow.collectAsState().value
Text("CoroutinesScope $normal")
}
The expected result would be that both com.rickclephas.kmm.viewmodel.stateIn and kotlinx.coroutines.flow.stateIn behave the same on iOS, which is currently not the case. Having to pass KMMViewModel.viewModelScope around everytime a StateFlow is created is problematic, because code will be tied to this library even though it doesn't need to be. It will also make testing code harder because as far as I see Faking ViewModelScope might not be so easy (I might be wrong). Still, I think it's better for the library API surface be limited to only the ViewModel and allow the flexibility of CoroutineScope.
Edit:
Played around with tests using the ViewModel scope, and it is not a pleasant experience :D You're forced to use Dispathers.setMain otherwise the test will always fail because we cannot change the dispatcher without using viewModel.coroutineScope + Dispatcher, however this change in production code will break iOS.
AKJAW/Timi-Multiplatform@9cf040b#diff-52ecb25b704f2601f1256c922a3fc5b45569daaf8ea3b9d51434907cdbbe7967
Additional thing is that if we need to inject the ViewModelScope, we need to create an object / class which inherits KmmViewModel and pass in the viewModelScope field. You cannot create your own implementation of ViewModelScope, because the coroutineScope extention function is tied to the implementation detail of ViewModelScopeImpl, and will throw an exception if a different ViewModelScope implementation is used
AKJAW/Timi-Multiplatform@9cf040b#diff-7c5b828548356898f03b363ec2ede9591723cb5791cab7c7a761dae664dd3170
I've seen comments in the kts files about this bug -> https://issuetracker.google.com/issues/260059413 is causing the project to be using java 11.
Is the java toolchain upgradable to 1.8 now?
Thanks!
When using the environmentViewModel(_ viewModel: KMMViewModel)
function, the app crashes:
ContentView()
.environmentViewModel(viewModel)
Interesting, using the alternative environmentViewModel(_ projectedValue: ObservableViewModel<KMMViewModel>.Projection)
works:
ContentView()
.environmentViewModel(_viewModel.projectedValue)
Great library by the way!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.