> ## Documentation Index
> Fetch the complete documentation index at: https://docsmlx.otterly.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Streaming & UX

> Afficher les réponses du modèle au fil de l'eau dans une app SwiftUI

Le streaming change surtout l'expérience utilisateur. Le temps de génération ne devient pas magique, mais l'utilisateur voit que quelque chose se passe.

Pour une app Swift, c'est souvent la différence entre une fonctionnalité perçue comme fluide et une fonctionnalité perçue comme bloquante.

***

## Deux modes à connaître

| Méthode               | À utiliser quand...                                      |
| --------------------- | -------------------------------------------------------- |
| `respond(to:)`        | vous avez besoin du résultat complet avant de l'utiliser |
| `streamResponse(to:)` | vous voulez afficher la réponse progressivement          |

Si votre UI affiche du texte généré, le streaming est souvent le meilleur défaut.

***

## Streaming, `isResponding` et `GenerationOptions`

Ces trois éléments vont souvent ensemble :

* `streamResponse(to:options:)` pour livrer la réponse progressivement
* `isResponding` pour protéger l'UI contre les doubles envois
* `GenerationOptions` pour borner la longueur et le niveau de variation

```swift theme={null}
import FoundationModels

let session = LanguageModelSession()

let options = GenerationOptions(
    sampling: .greedy,
    temperature: 0.2,
    maximumResponseTokens: 220
)

for try await partial in session.streamResponse(
    to: "Explique le rôle d'un transcript en 4 points.",
    options: options
) {
    print(partial.content)
}
```

Dans ce type de flow, `isResponding` devient votre garde-fou produit. Il évite qu'un utilisateur relance une seconde génération alors que la première est encore affichée en streaming.

***

## Exemple de streaming texte

```swift theme={null}
import FoundationModels
import Observation

@Observable
@MainActor
final class StreamingViewModel {
    var response = ""
    var errorMessage: String?
    var isResponding: Bool { session.isResponding }

    private let session = LanguageModelSession()

    func generate() async {
        response = ""
        errorMessage = nil

        do {
            for try await partial in session.streamResponse(
                to: "Explique MLX à un développeur Swift en 4 lignes."
            ) {
                response = partial.content
            }
        } catch {
            errorMessage = error.localizedDescription
        }
    }
}
```

À chaque itération, `partial.content` contient l'état courant complet de la réponse.

***

## Exemple de streaming structuré

Le streaming fonctionne aussi avec Guided Generation.

```swift theme={null}
let session = LanguageModelSession()

@Generable
struct Checklist {
    @Guide(description: "A short title")
    var title: String = ""

    @Guide(description: "Action items", .minimumCount(2))
    var items: [String] = []
}

let stream = session.streamResponse(
    to: "Prépare une checklist pour intégrer une feature IA dans une app iOS.",
    generating: Checklist.self
)

for try await partial in stream {
    print(partial.content)
}
```

Ce pattern est très utile quand vous remplissez progressivement une vue plus riche qu'un simple bloc de texte.

***

## UX : ce qu'il faut afficher

### Un indicateur d'activité

La propriété `isResponding` de votre session est faite pour ça.

```swift theme={null}
if viewModel.isResponding {
    ProgressView("Génération en cours...")
}
```

### Un bouton désactivé pendant la génération

```swift theme={null}
Button("Générer") {
    Task { await viewModel.generate() }
}
.disabled(viewModel.isResponding)
```

### Un affichage lisible

Le modèle génère souvent du Markdown. Si vous l'affichez tel quel, le résultat peut sembler brouillon.

```swift theme={null}
Text(.init(viewModel.response))
```

***

## Quand ne pas streamer

Le streaming n'est pas indispensable si :

* vous avez besoin d'un objet final unique
* la réponse est très courte
* le résultat sert uniquement à une logique interne

Dans ces cas-là, `respond(to:)` reste plus simple.

***

## Bonnes pratiques

* videz l'ancien contenu avant une nouvelle génération
* montrez clairement qu'une réponse est en cours
* gérez l'erreur dans la même zone d'UI
* bornez souvent `maximumResponseTokens` si l'écran attend une réponse courte
* ne forcez pas le streaming si l'écran a surtout besoin d'un résultat final

***

<CardGroup cols={2}>
  <Card title="Cycle de vie" icon="arrows-rotate" href="/mlx/foundation-models/session-lifecycle">
    Organiser vos sessions proprement.
  </Card>

  <Card title="Patterns d'architecture" icon="sitemap" href="/mlx/foundation-models/architecture-patterns">
    Choisir le bon pattern d'intégration.
  </Card>
</CardGroup>
