Skip to content

Commit fd573e3

Browse files
committed
Document Cancellable ViewModel behaviour
1 parent 441bb98 commit fd573e3

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,46 @@ This will prevent your Swift view models from being deallocated too soon.
225225

226226
> [!NOTE]
227227
> For lists, sets and dictionaries containing view models there is `childViewModels(at:)`.
228+
229+
### Cancellable ViewModel
230+
231+
When subclassing your Kotlin ViewModel in Swift you might experience some issues in the way those ViewModels are cleared.
232+
233+
An example of such an issue is when you are using a Combine publisher to observe a Flow through KMP-NativeCoroutines:
234+
```swift
235+
import Combine
236+
import KMPNativeCoroutinesCombine
237+
import shared // This should be your shared KMM module
238+
239+
class TimeTravelViewModel: shared.TimeTravelViewModel {
240+
241+
private var cancellables = Set<AnyCancellable>()
242+
243+
override init() {
244+
super.init()
245+
createPublisher(for: currentTimeFlow)
246+
.assertNoFailure()
247+
.sink { time in print("It's \(time)") }
248+
.store(in: &cancellables)
249+
}
250+
}
251+
```
252+
253+
Since `currentTimeFlow` is a StateFlow we don't ever expect it to fail, which is why we are using the `assertNoFailure`.
254+
However, in this case you'll notice that the publisher will fail with a `JobCancellationException`.
255+
256+
The problem here is that before the `TimeTravelViewModel` is deinited it will already be cleared.
257+
Meaning the `viewModelScope` is cancelled and `onCleared` is called.
258+
This results in the Combine publisher outliving the underlying StateFlow collection.
259+
260+
To solve such issues you should have your Swift ViewModel conform to `Cancellable`
261+
and perform the required cleanup in the `cancel` function:
262+
```swift
263+
class TimeTravelViewModel: shared.TimeTravelViewModel, Cancellable {
264+
func cancel() {
265+
cancellables = []
266+
}
267+
}
268+
```
269+
270+
KMM-ViewModel will make sure to call the `cancel` function before the ViewModel is being cleared.

0 commit comments

Comments
 (0)