Swinject — Conteneur d'injection de dépendances pour Swift (iOS/macOS)
\n\nL'injection de dépendances (DI) est l'un des principes clés du SOLID, permettant de créer un code faiblement couplé, testable et évolutif. Dans le monde Swift pour le développement iOS et macOS, l'une des bibliothèques les plus populaires implémentant la DI est Swinject. C'est un conteneur d'injection de dépendances léger mais puissant, qui simplifie la gestion des objets et de leur cycle de vie.
\n\nDans cet article, nous allons détailler comment fonctionne Swinject, comment l'installer et l'utiliser dans des projets réels.
\n\nQu'est-ce que l'injection de dépendances et pourquoi un conteneur ?
\nL'injection de dépendances est une technique par laquelle un objet reçoit ses dépendances de l'extérieur, plutôt que de les créer lui-même. Cela améliore la flexibilité et simplifie les tests. Un conteneur DI (comme Swinject) automatise le processus de création et de liaison des objets, évitant au développeur d'écrire du code boilerplate.
\n\nInstallation de Swinject
\nSwinject peut être installé via Swift Package Manager, CocoaPods ou Carthage. Examinons la méthode la plus moderne — SPM :
\n\n// Dans Xcode : File → Swift Packages → Add Package Dependency\n// URL : https://github.com/Swinject/Swinject.git\n// Version : à partir de 2.8.0\n\nOu ajoutez dans Package.swift :
dependencies: [\n .package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0")\n]\n\nComposants principaux de Swinject
\n- \n
- Container — l'objet principal dans lequel les dépendances sont enregistrées et résolues. \n
- Service — le type (protocole ou classe) que nous souhaitons injecter. \n
- Registration — le processus de liaison d'une abstraction (protocole) avec une implémentation concrète. \n
- Scope — gère le cycle de vie de l'objet (transient, singleton, etc.). \n
Exemple d'utilisation de base
\nSupposons que nous ayons un protocole NetworkServiceProtocol et son implémentation NetworkService :
import Swinject\n\nprotocol NetworkServiceProtocol {\n func fetchData() -> String\n}\n\nclass NetworkService: NetworkServiceProtocol {\n func fetchData() -> String {\n return "Données reçues"\n }\n}\n\n// Création du conteneur\nlet container = Container()\n\n// Enregistrement de la dépendance\ncontainer.register(NetworkServiceProtocol.self) { _ in NetworkService() }\n\n// Résolution (obtention) de l'instance\nlet service = container.resolve(NetworkServiceProtocol.self)!\nprint(service.fetchData()) // "Données reçues"\n\nInjection de dépendances dans un ViewController (iOS)
\nLe plus souvent, Swinject est utilisé pour injecter des services dans les contrôleurs. Voici un exemple avec un view-model :
\n\nimport Swinject\n\n// Protocole et implémentation du view-model\nprotocol UserViewModelProtocol {\n func getUsers() -> [String]\n}\n\nclass UserViewModel: UserViewModelProtocol {\n private let networkService: NetworkServiceProtocol\n \n init(networkService: NetworkServiceProtocol) {\n self.networkService = networkService\n }\n \n func getUsers() -> [String] {\n // Utilisation de networkService pour le chargement\n return ["Alice", "Bob"]\n }\n}\n\n// Enregistrement dans le conteneur\ncontainer.register(UserViewModelProtocol.self) { resolver in\n let network = resolver.resolve(NetworkServiceProtocol.self)!\n return UserViewModel(networkService: network)\n}\n\n// Utilisation dans le contrôleur\nclass UsersViewController: UIViewController {\n var viewModel: UserViewModelProtocol?\n \n override func viewDidLoad() {\n super.viewDidLoad()\n viewModel = container.resolve(UserViewModelProtocol.self)\n print(viewModel?.getUsers() ?? [])\n }\n}\n\nCycles de vie des objets (Scopes)
\nSwinject prend en charge plusieurs scopes :
\n- \n
- .transient \n
Aussi dans la bibliothèque
Bull (File d'attente de tâches pour Node.js)
kotlinx-datetime Kotlin : guide complet avec exemples pratiques
ts-node-dev TypeScript : guide complet avec exemples