# Ship an LLM on Android and iOS with LiteRT-LM: Lesson 01. Close a Conversation safely

Lesson id: `litert-lm-ship-android-ios/01-conversation-lifecycle`
Human lesson: https://edgefde.dev/courses/litert-lm-ship-android-ios/01-conversation-lifecycle.html

## Goal

Make Stop and screen close safe by cancelling, waiting for idle, and only then closing the Conversation and Engine.

## Prerequisites

- LiteRT-LM 0.18.0 on Android or iOS
- A model that takes a few seconds to prefill a long prompt

## Inputs

- Existing chat screen that uses `Conversation`

## Outputs and artifacts

- `SafeConversation` (Kotlin)
- `ChatSession` actor (Swift)
- A device test that stops and leaves 100 times

## Steps

1. Read issue #3771 and confirm your LiteRT-LM version still has the same `close()` behavior.
2. Kotlin: send with the callback form of `sendMessageAsync` and complete a `CompletableDeferred` in `onDone` and `onError`.
3. Kotlin: `stop()` calls `cancelProcess()`; `closeSafely()` calls stop, awaits idle with a timeout, then `close()`. On timeout, skip the close.
4. Swift: keep the stream loop in a `Task`. `close()` calls `cancel()`, awaits the task, then sets the conversation to nil.
5. Close the Engine only after the Conversation close returns true.
6. Write a device test that sends a 2,000 token prompt, presses Stop during prefill, and leaves the screen, 100 times.

## Constraints

- Never call `close()` in a `finally` block right after cancelling the collector.
- Kotlin on Android, Swift on iOS.

## Acceptance checks

- 100 out of 100 Stop-then-leave runs finish without a native crash.
- Logs show `close skipped` only when idle did not arrive.

## Key concepts

- #3771: closing a Conversation during prefill can crash because the prefill callback uses a session that was already reset.
- Cancelling a Kotlin coroutine does not stop native work. `cancelProcess()` does.
- `onDone` or `onError` (Kotlin) and the end of the stream loop (Swift) tell you the Conversation is idle.
- Close the Conversation before the Engine.

## Takeaways

- Wrap every Conversation in a small class that knows when it is idle.
- If idle does not come in time, skip the close and try later.
- Run a Stop-then-leave test 100 times on a device before each release.
