📑 Hébergement multi-onglets (UIKit et SwiftUI)
Vue d'ensemble de la navigation · Catalogue des destinations — chaque écran ouvrable avec sdk.navigate.
Lorsque vous intégrez Nutrition et Entraînement dans votre app — en général sur deux onglets — le SDK fournit un VC hôte léger par module et un coordinateur relié à votre UI d'onglets. Un seul FlutterViewController partagé alimente les deux onglets.
Pourquoi c'est important : le moteur Flutter sur iOS ne peut rendre que dans un
FlutterViewControllerà la fois. Une intégration naïve « deux FlutterVC par moteur » provoque des blocages au changement d'onglet. Le schéma SDK ci-dessous l'évite entièrement.
UIKit (UITabBarController)
import UIKit
import AzeooSDK
final class MainTabBarController: UITabBarController {
/// Holding the coordinator alive keeps the SDK's weak ref valid for the
/// lifetime of this tab bar controller.
private var tabCoordinator: AzeooUITabBarCoordinator?
override func viewDidLoad() {
super.viewDidLoad()
setupTabs()
}
private func setupTabs() {
guard let sdk = AzeooSDK.shared else { return }
let status = UINavigationController(rootViewController: StatusViewController())
status.tabBarItem = UITabBarItem(title: "Status",
image: UIImage(systemName: "info.circle"),
selectedImage: nil)
// Tab hosts — thin VCs that contain the shared Flutter surface.
let nutrition = sdk.tabHost(for: .nutrition)
nutrition.tabBarItem = UITabBarItem(title: "Nutrition",
image: UIImage(systemName: "leaf"),
selectedImage: nil)
let training = sdk.tabHost(for: .training)
training.tabBarItem = UITabBarItem(title: "Training",
image: UIImage(systemName: "dumbbell"),
selectedImage: nil)
viewControllers = [status, nutrition, training]
selectedViewController = status
// Install the coordinator once. From now on, any sdk.navigate(...)
// from anywhere automatically flips this tab bar.
let coordinator = AzeooUITabBarCoordinator(self, mapping: [
.nutrition: 1,
.training: 2,
])
sdk.setModuleContainer(coordinator)
tabCoordinator = coordinator
}
deinit {
AzeooSDK.shared?.setModuleContainer(nil)
}
}
C'est tout le code hôte nécessaire. Depuis n'importe quel view controller vous pouvez faire :
sdk.navigate(to: .nutrition(.plan(id: "abc-123")))
// Tab bar visually switches to Nutrition.
// Flutter switches to nutrition and pushes the plan detail.
SwiftUI (TabView)
import SwiftUI
import AzeooSDK
struct ContentView: View {
@EnvironmentObject var sdkManager: SDKManager
@State private var selectedTab = 0
@State private var tabCoordinator: AzeooSwiftUITabCoordinator<Int>? = nil
var body: some View {
TabView(selection: $selectedTab) {
StatusView()
.tabItem { Label("Status", systemImage: "info.circle.fill") }
.tag(0)
sdkManager.sdk!.modules.nutrition.getView()
.tabItem { Label("Nutrition", systemImage: "leaf.fill") }
.tag(1)
sdkManager.sdk!.modules.training.getView()
.tabItem { Label("Training", systemImage: "dumbbell.fill") }
.tag(2)
}
.onAppear {
guard let sdk = sdkManager.sdk, tabCoordinator == nil else { return }
let coordinator = AzeooSwiftUITabCoordinator<Int>(
selection: $selectedTab,
mapping: [.nutrition: 1, .training: 2]
)
sdk.setModuleContainer(coordinator)
tabCoordinator = coordinator
}
.onDisappear {
sdkManager.sdk?.setModuleContainer(nil)
tabCoordinator = nil
}
}
}
Fonctionnement
sdk.navigate(to: .training(.workouts))
│
▼
┌──────────────────────────────────────┐
│ AzeooSDK.navigate(to:) │
└─────────┬────────────────┬───────────┘
│ │
coordinator.azeoo_showModule(.training)
│ │
▼ ▼
┌──────────────────────┐ Pigeon to(
│ AzeooUITabBarCoord. │ "training",
│ tabBar.selectedIndex │ "workout-plans",
│ = 2 │ params: nil)
└──────────────────────┘ │
▼
Flutter switches its
internal tab + pushes
the workouts route
L'hôte écrit une ligne au démarrage ; toute navigation ensuite est automatique de bout en bout.
Pourquoi un seul FlutterViewController
Le SDK détient un seul FlutterViewController partagé. Chaque tabHost(for:) renvoie un view controller conteneur léger. Quand un onglet devient visible, le SDK :
- Reparente le FlutterVC partagé dans l'hôte d'onglet actif (un déplacement de vue UIKit, sans redémarrage du moteur)
- Envoie Pigeon
display(module)pour que Flutter affiche le bon contenu d'onglet
AzeooTabHost surcharge shouldAutomaticallyForwardAppearanceMethods à false, donc les changements d'onglet ne déclenchent pas viewWillDisappear → viewWillAppear sur le FlutterVC. Le moteur s'attache une fois et reste attaché — pas de flash, pas de blocage.
Autres conteneurs UIKit
Si vous utilisez UINavigationController plutôt qu'une barre d'onglets :
sdk.setModuleContainer(AzeooUINavigationCoordinator(
navController,
hostsByModule: [
.nutrition: nutritionVC,
.training: trainingVC,
],
))
Pour tout le reste (barre latérale, page view, tiroir, personnalisé) : implémentez AzeooModuleContainer directement — une seule méthode. Voir Conteneurs de modules.