Aller au contenu

Cycle de vie de l'application

Les applications de bureau ont un cycle de vie allant du démarrage à l’arrêt. Wails v3 fournit des services, des événements et des hooks pour gérer efficacement ce cycle de vie.

Diagram

Créez votre application avec application.New() :

app := application.New(application.Options{
Name: "My App",
Description: "An application built with Wails",
Services: []application.Service{
application.NewService(&MyService{}),
},
Assets: application.AssetOptions{
Handler: application.BundledAssetFileServer(assets),
},
})

Ce qui se passe :

  1. Les options sont analysées et validées
  2. Les services sont enregistrés (mais pas encore démarrés)
  3. Le serveur d’actifs est configuré
  4. Le runtime est mis en place

Appelez app.Run() pour démarrer l’application :

err := app.Run() // Blocks until quit
if err != nil {
log.Fatal(err)
}

Ce qui se passe :

  1. Les services sont démarrés dans l’ordre d’enregistrement
  2. Les écouteurs d’événements sont activés
  3. Les fenêtres peuvent être créées
  4. La boucle d’événements commence

L’application entre dans la boucle d’événements où elle passe la majeure partie de son temps :

  • Les événements du système d’exploitation sont traités (souris, clavier, événements de fenêtre)
  • Les messages Go vers JS sont gérés
  • Les appels JS vers Go sont exécutés
  • Les mises à jour de l’interface utilisateur sont rendues

Lorsque l’application se quitte :

  1. Le callback ShouldQuit est vérifié (s’il est défini)
  2. Les callbacks OnShutdown sont exécutés
  3. Les services sont arrêtés dans l’ordre inverse de leur enregistrement
  4. Les fenêtres sont fermées
  5. Les ressources sont libérées

Les services sont le moyen principal de gérer le cycle de vie dans Wails v3. Ils fournissent des hooks de démarrage et d’arrêt via des interfaces. Pour la documentation complète sur les services, consultez le guide des Services.

type MyService struct {
db *sql.DB
}
// ServiceStartup is called when the application starts
func (s *MyService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
var err error
s.db, err = sql.Open("sqlite3", "app.db")
if err != nil {
return err // Startup aborts if error returned
}
// Run migrations
if err := s.runMigrations(); err != nil {
return err
}
return nil
}
// ServiceShutdown is called when the application shuts down
func (s *MyService) ServiceShutdown() error {
if s.db != nil {
return s.db.Close()
}
return nil
}
app := application.New(application.Options{
Services: []application.Service{
application.NewService(&MyService{}),
application.NewService(&AnotherService{}),
},
})

Points clés :

  • Les services démarrent dans l’ordre d’enregistrement
  • Les services s’arrêtent dans l’ordre inverse d’enregistrement
  • Si le ServiceStartup d’un service renvoie une erreur, l’application s’arrête
  • Le ctx passé à ServiceStartup est annulé lorsque l’arrêt commence

Le contexte passé à ServiceStartup est valide pendant toute la durée de vie de l’application :

func (s *MyService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
// Start a background task that respects shutdown
go func() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ticker.C:
s.performBackgroundSync()
case <-ctx.Done():
// Application is shutting down
return
}
}
}()
return nil
}

Vous pouvez également accéder au contexte depuis l’instance de l’application :

app := application.Get()
ctx := app.Context()

Ce sont des callbacks pratiques dans application.Options qui vous permettent d’interagir avec le cycle de vie de l’application sans créer un service complet. Ils sont utiles pour les tâches de nettoyage simples, la confirmation de fermeture, ou lorsque vous devez exécuter du code à des moments spécifiques de la séquence d’arrêt.

Pour une gestion du cycle de vie plus complexe avec de la logique de démarrage, de l’injection de dépendances ou des ressources avec état, utilisez plutôt les Services.

Le callback ShouldQuit est appelé chaque fois qu’une demande de fermeture est effectuée — que ce soit par l’utilisateur qui ferme la dernière fenêtre, en appuyant sur Cmd+Q (macOS) / Alt+F4 (Windows), ou en appelant app.Quit() programmatiquement.

Valeur de retour :

  • Renvoyez true pour permettre la fermeture (l’application s’arrêtera)
  • Renvoyez false pour annuler la fermeture (l’application continue de s’exécuter)

C’est votre opportunité d’intercepter les demandes de fermeture et éventuellement de les empêcher, par exemple pour demander à l’utilisateur s’il souhaite quitter en cas de modifications non enregistrées :

app := application.New(application.Options{
ShouldQuit: func() bool {
if !hasUnsavedChanges() {
return true // No unsaved changes, allow quit
}
// Prompt the user
result, _ := application.QuestionDialog().
SetTitle("Unsaved Changes").
SetMessage("You have unsaved changes. Quit anyway?").
AddButton("Quit", "quit").
AddButton("Cancel", "cancel").
Show()
// Only quit if user clicked "Quit"
return result == "quit"
},
})

Si ShouldQuit n’est pas défini, l’application se fermera immédiatement lors de la demande.

Quand ShouldQuit est appelé :

  • L’utilisateur ferme la dernière fenêtre (sauf si DisableQuitOnLastWindowClosed est défini)
  • L’utilisateur appuie sur Cmd+Q sur macOS
  • L’utilisateur appuie sur Alt+F4 sur Windows (lorsqu’il est focalisé sur la dernière fenêtre)
  • Le code appelle app.Quit()

Quand ShouldQuit n’est PAS appelé :

  • Le processus est tué (SIGKILL, fermeture forcée via le Gestionnaire des tâches)
  • os.Exit() est appelé directement

Le callback OnShutdown est appelé lorsque l’application est confirmée comme étant en cours de fermeture (après que ShouldQuit a renvoyé true, s’il est défini). Utilisez-le pour des tâches de nettoyage comme la sauvegarde de l’état, la fermeture des connexions à la base de données ou la libération des ressources.

app := application.New(application.Options{
OnShutdown: func() {
// Save application state
saveState()
// Close connections----------|--------------------------------|
| macOS | L'application reste en cours d'exécution (la barre de menu reste) |
| Windows | L'application quitte |
| Linux | L'application quitte |
macOS suit les conventions natives de la plateforme les applications restent généralement actives dans la barre de menu même sans fenêtres. Windows et Linux quittent par défaut.
**Faire quitter toutes les plateformes lorsque la dernière fenêtre est fermée :**
```go
app := application.New(application.Options{
Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
})

Faire que toutes les plateformes restent en cours d’exécution lorsque la dernière fenêtre est fermée :

Ceci est utile pour les applications de barre d’état système ou les applications qui doivent rester en cours d’exécution en arrière-plan.

app := application.New(application.Options{
Windows: application.WindowsOptions{
DisableQuitOnLastWindowClosed: true,
},
Linux: application.LinuxOptions{
DisableQuitOnLastWindowClosed: true,
},
})
type DatabaseService struct {
db *sql.DB
}
func (s *DatabaseService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
var err error
s.db, err = sql.Open("sqlite3", "app.db")
if err != nil {
return fmt.Errorf("failed to open database: %w", err)
}
if err := s.db.PingContext(ctx); err != nil {
return fmt.Errorf("failed to connect to database: %w", err)
}
return nil
}**Des questions sur le cycle de vie ?** Posez-les sur [Discord](https://discord.gg/JDdSxwjhGf) ou consultez les [exemples](https://github.com/wailsapp/wails/tree/master/v3/examples).