Aller au contenu principal

🚀 Guide: Système de Force Update

📋 Vue d'ensemble

Ce système permet de forcer les utilisateurs à mettre à jour l'application en cas de bug critique ou breaking change. L'utilisateur sera bloqué jusqu'à ce qu'il installe la nouvelle version.


🏗️ Architecture

main.dart
└── VersionCheckWrapper (vérifie au démarrage)
├── Si update requis → ForceUpdatePage (écran de blocage)
└── Si OK → MyApp (application normale)

Composants créés:

  1. core/models/app_version.dart - Modèle de données
  2. core/services/version_service.dart - Service de vérification
  3. core/presentation/pages/force_update_page.dart - Écran de blocage
  4. core/presentation/pages/version_check_wrapper.dart - Wrapper de check
  5. supabase/migrations/20250112_create_app_version_config.sql - Table BDD

🎯 Comment utiliser

1. Exécuter la migration Supabase

# Via Supabase CLI
supabase migration up

# Ou copier/coller le SQL dans le dashboard Supabase

2. Configurer les URLs des stores

Dans la table app_version_config, mettez à jour les URLs:

UPDATE app_version_config
SET android_url = 'https://play.google.com/store/apps/details?id=VOTRE_PACKAGE_ID',
ios_url = 'https://apps.apple.com/app/idVOTRE_APP_ID'
WHERE platform = 'android' OR platform = 'ios';

3. Forcer une mise à jour (exemple)

Scénario: Vous avez un bug critique en v1.0.0, vous sortez v1.0.1 en urgence.

-- 1. Désactiver l'ancienne config
UPDATE app_version_config
SET is_active = false
WHERE platform = 'android' AND version = '1.0.0';

-- 2. Créer nouvelle config qui force la mise à jour
INSERT INTO app_version_config (
platform,
version,
build_number,
min_required_version,
min_required_build_number,
force_update,
update_message,
android_url,
is_active
) VALUES (
'android',
'1.0.1', -- Version actuelle disponible
2, -- Build number actuel
'1.0.1', -- Version minimale requise
2, -- Build number minimal
true, -- ⚠️ FORCE UPDATE ACTIVÉ
'⚠️ Mise à jour critique : Un bug majeur a été corrigé. Veuillez mettre à jour immédiatement.',
'https://play.google.com/store/apps/details?id=VOTRE_PACKAGE_ID',
true
);

Résultat: Tous les utilisateurs en v1.0.0 seront bloqués et devront installer v1.0.1.


📊 Cas d'usage

✅ Cas 1: Mise à jour recommandée (non forcée)

UPDATE app_version_config
SET force_update = false,
update_message = 'Une nouvelle version est disponible avec de nouvelles fonctionnalités!'
WHERE platform = 'android';

Comportement: L'utilisateur peut continuer à utiliser l'app normalement.


✅ Cas 2: Mise à jour forcée (bug critique)

UPDATE app_version_config
SET force_update = true,
min_required_version = '1.0.2',
min_required_build_number = 3,
update_message = '🚨 Mise à jour de sécurité obligatoire'
WHERE platform = 'android';

Comportement: Utilisateurs en v < 1.0.2 sont bloqués jusqu'à mise à jour.


✅ Cas 3: Breaking change API

-- Exemple: Vous changez l'API backend et les anciennes versions ne sont plus compatibles
UPDATE app_version_config
SET force_update = true,
min_required_version = '2.0.0',
min_required_build_number = 10,
update_message = 'Mise à jour requise pour compatibilité avec nos nouveaux serveurs'
WHERE platform = 'android';

🔍 Comment ça marche

Flow technique:

  1. Au démarrage de l'app:

    VersionCheckWrapper
    → VersionService.checkForUpdate()
    → Récupère version locale (package_info_plus)
    → Récupère config depuis Supabase (app_version_config)
    → Compare les versions
  2. Si version obsolète ET force_update = true:

    Affiche ForceUpdatePage (écran bloquant)
    → Utilisateur clique "Mettre à jour"
    → Ouvre Play Store / App Store
    → Installation nouvelle version
    → Redémarrage app
    → Check version OK ✅
  3. Si version OK:

    Continue normalement → MyApp()

🛠️ Administration

Consulter les versions actives:

SELECT
platform,
version,
build_number,
min_required_version,
min_required_build_number,
force_update,
is_active
FROM app_version_config
WHERE is_active = true;

Désactiver le force update:

UPDATE app_version_config
SET force_update = false
WHERE platform = 'android';

Changer le message:

UPDATE app_version_config
SET update_message = 'Nouveau message personnalisé'
WHERE platform = 'android';

📱 Tests

Tester en local:

  1. Modifier la version dans pubspec.yaml:

    version: 0.9.0+1  # Version inférieure à la minimale requise
  2. Dans Supabase, forcer la mise à jour:

    UPDATE app_version_config
    SET force_update = true,
    min_required_version = '1.0.0',
    min_required_build_number = 1
    WHERE platform = 'android';
  3. Lancer l'app:

    flutter run
  4. Résultat attendu: Écran de force update s'affiche 🚫


⚠️ Points d'attention

1. Gestion des erreurs

Si Supabase est down, le check version laisse passer pour ne pas bloquer l'app.

// Dans VersionService.checkForUpdate()
catch (e) {
debugPrint('❌ Erreur: $e');
return null; // Laisser passer
}

2. RLS Policies

La table app_version_config est publique en lecture pour que l'app puisse checker sans auth.

-- Lecture publique
CREATE POLICY "Lecture publique de la config version active"
ON app_version_config FOR SELECT
USING (is_active = true);

3. Une seule config active par plateforme

La contrainte UNIQUE(platform, is_active) empêche d'avoir plusieurs configs actives.

-- ❌ ERREUR si on essaie d'insérer une 2ème config active
INSERT INTO app_version_config (platform, is_active, ...)
VALUES ('android', true, ...); -- Violation de contrainte

🚦 Workflow recommandé

Lors d'une release normale:

  1. Publier nouvelle version sur stores
  2. NE PAS activer force_update
  3. Monitorer l'adoption naturelle

Lors d'un hotfix critique:

  1. Publier version fixée sur stores
  2. Activer force_update = true
  3. Mettre à jour min_required_version
  4. Personnaliser update_message
  5. Monitorer les mises à jour
  6. Une fois adoption > 95%, désactiver force_update

📈 Monitoring

Requête utile pour voir la répartition des versions:

-- À implémenter: Table de tracking des versions utilisateurs
CREATE TABLE user_app_versions (
user_id UUID REFERENCES auth.users(id),
platform TEXT,
version TEXT,
build_number INT,
last_seen TIMESTAMPTZ
);

-- Puis query:
SELECT
version,
COUNT(*) as nb_users
FROM user_app_versions
WHERE platform = 'android'
GROUP BY version
ORDER BY nb_users DESC;

🎨 Personnalisation

Changer le message de l'écran:

Éditez ForceUpdatePage:

// lib/core/presentation/pages/force_update_page.dart
AppText.h1(
'Votre titre personnalisé', // Modifiez ici
...
)

Changer l'icône:

Icon(
Icons.update, // Changez l'icône ici
size: 100,
...
)

🔒 Sécurité

  • ✅ Lecture publique de la config (pas de données sensibles)
  • ✅ Écriture admin seulement
  • ✅ Fallback gracieux en cas d'erreur
  • ✅ Pas de bypass possible (WillPopScope)

📞 Support

En cas de problème:

  1. Vérifier les logs: flutter logs
  2. Vérifier Supabase: supabase logs
  3. Désactiver temporairement: force_update = false

Auteur: Claude Code Date: 2025-01-12 Version: 1.0