Créer un chatbot SwiftUI avec ExyteChat et OpenAI (MacPaw)
Guide pas à pas pour créer une interface de chatbot iOS moderne et la connecter à OpenAI avec Swift Package Manager.

Introduction
Ce tutoriel montre comment construire une interface de chatbot SwiftUI propre et la connecter à OpenAI en utilisant le SDK MacPaw, en s’appuyant sur le projet de référence : GitHub – GuillaumeBlanchet/chatbot (laissez une étoile si ça vous plaît, ce sera très apprécié).
J’ai rédigé ce tutoriel parce que les autres que j’ai trouvés n’étaient pas à jour ou n’exploitaient pas les bibliothèques populaires existantes comme ExyteChat et MacPaw OpenAI.
Voici un court clip de démonstration du projet finalisé:
Ce que vous allez faire :
- Créer une app iOS dans Xcode
- Ajouter des dépendances via Swift Package Manager
- Brancher un
ChatViewModel
simple pour appeler OpenAI - Afficher les messages avec l’UI de chat d’Exyte
Prérequis : Xcode 14+, iOS 15+ et une clé d’API OpenAI.
1) Créer le projet Xcode
Ouvrez Xcode et créez un nouveau projet :
Choisissez iOS > App :
Saisissez un nom de produit (p. ex. « chatbot ») et assurez‑vous que l’interface est SwiftUI :
2) Ajouter les dépendances (Swift Package Manager)
Ajoutez les paquets suivants :
- Exyte Chat UI (ExyteChat)
- OpenAI (MacPaw)
Dans Xcode : Project navigator > sélectionnez votre projet > Package Dependencies > bouton « + ».
Recherchez ou collez les URLs des paquets et choisissez des versions adaptées (le projet de référence utilise ExyteChat v2.6.5 et MacPaw OpenAI v0.4.6).
Enfin, ajoutez les paquets à la cible de votre app :
3) Configurer le client OpenAI et le view model
Créez un nouveau fichier Swift ChatViewModel.swift
et initialisez le client OpenAI de MacPaw avec votre clé d’API.
@MainActor
class ChatViewModel: ObservableObject {
private let openAI: OpenAI
@Published var messages: [Message] = []
init() {
// TODO: stocker cette clé API dans le trousseau (Keychain)
self.openAI = OpenAI(apiToken: "YOUR_OPENAI_API_KEY_HERE")
let welcomeMessage = createMessage(userId: "bot", text: "Salut ! Quoi de neuf ?")
messages.append(welcomeMessage)
}
func send(draft: DraftMessage) {
let userMessage = createMessage(userId: "user", text: draft.text, createdAt: draft.createdAt)
messages.append(userMessage)
// Créer un message initial du bot pour le streaming
let botMessageId = UUID().uuidString
let botMessage = createMessage(messageId: botMessageId, userId: "bot", text: "en train d’écrire...", status: .sending)
messages.append(botMessage)
// Démarrer la réponse OpenAI
Task {
await getOpenAIResponse(userText: draft.text, botMessageId: botMessageId)
}
}
private func getOpenAIResponse(userText: String, botMessageId: String) async {
// Créer le contexte de conversation avec tous les messages
var chatMessages: [ChatQuery.ChatCompletionMessageParam] = [
.system(.init(content: .textContent("Tu es un chatbot enjoué qui apporte de la joie et de l’humour à chaque conversation. Tu réponds de façon très courte et concise.")))
]
// Ajouter l’historique récent (10 derniers messages pour garder un contexte gérable)
let recentMessages = messages.suffix(10)
for message in recentMessages {
if message.user.isCurrentUser {
chatMessages.append(.user(.init(content: .string(message.text))))
} else if message.id != botMessageId { // Ne pas inclure le message en cours de génération
chatMessages.append(.assistant(.init(content: .textContent(message.text))))
}
}
let query = ChatQuery(
messages: chatMessages,
model: .gpt5_nano,
stream: false
)
// Récupérer la réponse complète auprès d’OpenAI
// TODO: ajouter la gestion d’erreurs (voir le projet GitHub)
let result = try! await openAI.chats(query: query)
if let content = result.choices.first?.message.content {
// Mettre à jour le message du bot avec la réponse complète
if let messageIndex = messages.firstIndex(where: { $0.id == botMessageId }) {
var updatedMessage = messages[messageIndex]
updatedMessage.text = content
updatedMessage.status = .sent
messages[messageIndex] = updatedMessage
}
}
}
}
Astuce sécurité : en production, stockez la clé d’API dans le Trousseau (Keychain) ou via une couche de configuration — ne la codez pas en dur.
4) Construire l’UI de chat avec ExyteChat
Créez une vue SwiftUI simple qui affiche vos messages et en envoie de nouveaux :
struct ChatGoalView: View {
@StateObject private var chatViewModel = ChatViewModel()
var body: some View {
ChatView(messages: chatViewModel.messages) { draft in
chatViewModel.send(draft: draft)
}
// Si vous supprimez ceci, l’interface permettra d’envoyer des images, de l’audio, etc.
// Vous pouvez décider de supporter ces entrées, mais il faudra gérer le message
// dans le view model et envoyer le média au bon modèle/endpoint pour
// l’analyser avec votre chatbot.
.setAvailableInputs([AvailableInputType.text])
.navigationTitle("Ton chatbot amusant")
.navigationBarTitleDisplayMode(.inline)
}
}
Intégrez ChatGoalView
dans le point d’entrée de votre app (p. ex. ContentView
).
5) Lancer l’app
Sélectionnez un simulateur et appuyez sur Cmd+R. Vous devriez pouvoir taper un message et recevoir une réponse.
Si vous souhaitez comparer avec un exemple fonctionnel, consultez le projet de référence : GitHub – GuillaumeBlanchet/chatbot.
Diffuser la réponse du chatbot (streaming)
Si vous voulez diffuser la réponse du chatbot en streaming, utilisez la méthode chatsStream
du wrapper OpenAI fourni par la bibliothèque MacPaw avec le paramètre stream
défini à true
dans l’objet ChatQuery
:
let query = ChatQuery(
// Le client OpenAI de MacPaw ne supporte pas encore gpt5_nano pour le streaming, on utilise donc gpt4_1_nano
messages: chatMessages, model: .gpt4_1_nano,
temperature: 0.7,
stream: true
)
var streamText = ""
for try await result in openAI.chatsStream(query: query) {
// etc.
Consultez la version streaming du chatbot dans le projet de référence : GitHub – branche « streaming ».
À noter : la version streaming n’est pas encore prise en charge pour les modèles GPT‑5 dans la bibliothèque MacPaw. Voir l’issue correspondante : MacPaw/OpenAI #378 pour plus de détails et mises à jour.
Prochaines étapes
- Personnaliser les prompts système pour modifier la personnalité de l’assistant ;
- Ajouter des états d’erreur et des relances pour une UX plus robuste ;
- Mettre en place un système RAG (Retrieval‑Augmented Generation) pour améliorer les réponses du chatbot avec la documentation de votre entreprise ;
- Ajouter d’autres types d’entrée au chatbot (images, audio, etc.) ;
- etc.