حاوية حقن التبعية لـ Swift (iOS/macOS): Swinject

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

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:

\n
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\n

مثال أساسي للاستخدام

\n

لنفترض أن لدينا بروتوكول NetworkServiceProtocol وتنفيذه NetworkService:

\n\n
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\n
import 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

توصيات