Swinject — حاوية حقن التبعية لـ Swift (iOS/macOS)
\n\nحقن التبعية (Dependency Injection) هو أحد المبادئ الأساسية لـ SOLID، والذي يسمح بإنشاء كود ضعيف الاقتران، قابل للاختبار وقابل للتوسع. في عالم Swift لتطوير iOS و macOS، واحدة من أشهر المكتبات التي تطبق DI هي Swinject. إنها حاوية حقن تبعية خفيفة الوزن ولكنها قوية، تبسط إدارة الكائنات ودورة حياتها.
\n\nفي هذه المقالة، سنشرح بالتفصيل كيفية عمل Swinject، وكيفية تثبيته واستخدامه في المشاريع الحقيقية.
\n\nما هو حقن التبعية ولماذا نحتاج إلى حاوية؟
\nحقن التبعية هو أسلوب يحصل فيه الكائن على تبعياته من الخارج، بدلاً من إنشائها بنفسه. هذا يزيد من المرونة ويبسط الاختبار. حاوية DI (مثل Swinject) تقوم بأتمتة عملية إنشاء وربط الكائنات، مما يوفر على المطور كتابة كود boilerplate.
\n\nتثبيت Swinject
\nيمكن تثبيت Swinject باستخدام Swift Package Manager أو CocoaPods أو Carthage. دعنا ننظر إلى الطريقة الأكثر حداثة — SPM:
\n\n// في Xcode: File → Swift Packages → Add Package Dependency\n// الرابط: https://github.com/Swinject/Swinject.git\n// الإصدار: من 2.8.0 فما فوق\n\nأو أضف في Package.swift:
dependencies: [\n .package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0")\n]\n\nالمكونات الأساسية لـ Swinject
\n- \n
- Container — الكائن الرئيسي الذي يتم فيه تسجيل وحل التبعيات. \n
- Service — النوع (بروتوكول أو كلاس) الذي نريد حقنه. \n
- Registration — عملية ربط التجريد (البروتوكول) بالتنفيذ الملموس. \n
- Scope — يدير دورة حياة الكائن (مؤقت، مفرد، إلخ). \n
مثال أساسي للاستخدام
\nلنفترض أن لدينا بروتوكول NetworkServiceProtocol وتنفيذه NetworkService:
import Swinject\n\nprotocol NetworkServiceProtocol {\n func fetchData() -> String\n}\n\nclass NetworkService: NetworkServiceProtocol {\n func fetchData() -> String {\n return "تم الحصول على البيانات"\n }\n}\n\n// إنشاء الحاوية\nlet container = Container()\n\n// تسجيل التبعية\ncontainer.register(NetworkServiceProtocol.self) { _ in NetworkService() }\n\n// حل (الحصول على) المثيل\nlet service = container.resolve(NetworkServiceProtocol.self)!\nprint(service.fetchData()) // "تم الحصول على البيانات"\n\nحقن التبعيات في ViewController (iOS)
\nغالبًا ما يُستخدم Swinject لحقن الخدمات في المتحكمات. دعنا ننظر إلى مثال مع نموذج العرض:
\n\nimport Swinject\n\n// بروتوكول وتنفيذ نموذج العرض\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 // استخدام networkService للتحميل\n return ["Alice", "Bob"]\n }\n}\n\n// التسجيل في الحاوية\ncontainer.register(UserViewModelProtocol.self) { resolver in\n let network = resolver.resolve(NetworkServiceProtocol.self)!\n return UserViewModel(networkService: network)\n}\n\n// الاستخدام في المتحكم\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\nدورات حياة الكائنات (Scopes)
\nيدعم Swinject عدة نطاقات:
\n- \n
- .transient \n