Swinject: ما هو، مبادئ Dependency Injection وكيف يعمل

المؤلف: IT Sectr نُشر: 2026-05-04 وقت القراءة: 8 دق

Swinject هي حاوية DI لـ Swift تطبق نمط Dependency Injection في تطبيقات iOS. يقوم الإطار بأتمتة إنشاء وحقن التبعيات، مما يلغي الحاجة إلى الإدارة اليدوية للكائنات والمصانع. وفقًا لـ Swinject على GitHub، تدعم المكتبة Constructor Injection و Property Injection و Method Injection مع نظام scopes مرن لإدارة دورة الحياة.

الملامح الرئيسية

  • Swinject — حاوية DI لـ Swift تقوم بأتمتة حقن التبعيات في مشاريع iOS.
  • Dependency Injection — نمط حيث يتلقى الكائن تبعياته من الخارج بدلاً من إنشائها داخليًا.
  • Container — المكون المركزي لـ Swinject الذي يخزن سجل الخدمات المسجلة ومصانعها.
  • Service — تجريد على شكل بروتوكول يخزن له الحاوية تنفيذًا ملموسًا.
  • ObjectScope — آلية تحدد دورة حياة المثيل: graph أو container أو transient.

ما هو Swinject و Dependency Injection

Swinject هي حاوية DI مفتوحة المصدر للغة Swift، مصممة لتبسيط حقن التبعيات في تطبيقات iOS و macOS و watchOS. يستخدم الإطار نهج Service Locator: يتم تسجيل الخدمات في حاوية مركزية، وتقوم الحاوية تلقائيًا بحل رسم بياني للتبعيات عند طلب مثيل.

Dependency Injection (DI) هو نمط تصميم حيث يتلقى الكائن تبعياته من الخارج بدلاً من إنشائها داخليًا. هذا يقلل من الاقتران بين المكونات، ويبسط الاختبارات الوحدوية ويسمح باستبدال التطبيقات دون تعديل كود المستهلك.

وفقًا لـ Martin Fowler (2004)، DI هي حالة محددة من Inversion of Control ويتم تنفيذها من خلال الحقن عبر المنشئ أو الخاصية أو الطريقة. تقوم Swinject بأتمتة هذه العملية، مما يلغي الحاجة إلى كتابة المصانع ومواقع الخدمات يدويًا.

استخدم Swinject في المشاريع التي تحتوي على ثلاثة خدمات أو أكثر ذات تبعيات متقاطعة، حيث يؤدي البناء اليدوي للكائنات إلى تضخم كود التهيئة وانخفاض قابلية الاختبار.

يتكامل Swinject بشكل وثيق مع نظام Apple البيئي ويدعم جميع إصدارات Swift بدءًا من 3.0. الإطار متوافق مع Objective-C من خلال الجسور، مما يسمح بإدخاله في المشاريع الحالية بلغات مختلطة دون ترحيل كامل للكود. هذا مهم بشكل خاص للتطبيقات الكبيرة التي يزيد عمرها عن خمس سنوات.

كيف يعمل حاوية Swinject

يتم تنفيذ حاوية Swinject بواسطة فئة Container، التي تخزن سجلًا للخدمات المسجلة. عند استدعاء طريقة resolve، تقوم الحاوية بإنشاء كائن، وحل جميع تبعياته بشكل متكرر عبر رسم بياني للتبعيات.

Container و Service

Container هو الكائن المركزي حيث يتم تسجيل التطابقات بين التجريدات وتطبيقاتها. Service هو بروتوكول يحدد عقدًا، بينما Component هو فئة تنفذ هذا البروتوكول. يتم التسجيل باستخدام طريقة register، التي تأخذ نوع الخدمة ومصنعًا.

swift
let container = Container()
container.register(Networking.self) { _ in
    NetworkService()
}
let service = container.resolve(Networking.self)

تقوم طريقة resolve بإرجاع مثيل من التطبيق الملموس المسجل للبروتوكول المحدد. إذا لم يتم تسجيل تبعية، يقوم الحاوية بإلقاء خطأ فادح للاكتشاف السريع للمشكلة أثناء التطوير.

التسجيل والخدمات المسماة

كل تسجيل ينشئ إدخالًا بوظيفة مصنع ونطاق محدد. يمكن أن يكون للخدمة الواحدة تسجيلات متعددة بأسماء مختلفة، مما يسمح باختيار تطبيق معين بالاسم — مفيد للبيئات المختلفة (التطوير، الاختبار، الإنتاج).

تعمل عملية حل التبعيات بشكل متكرر: عندما تقوم الحاوية بإنشاء مثيل Component، تقوم بتحليل المُهيئ الخاص به ولكل معامل تستدعي resolve من النوع المقابل. إذا كانت التبعية لها تبعياتها الخاصة أيضًا، تستمر العملية حتى يتم بناء الرسم البياني بأكمله بالكامل. عمق التداخل محدود فقط بالذاكرة المتاحة، ولكن في الممارسة نادرًا ما يتجاوز خمسة مستويات.

طرق حقن التبعيات في Swinject

Swinject تدعم ثلاث طرق رئيسية لحقن التبعيات، كل منها قابل للتطبيق حسب السياق المعماري.

Constructor Injection

Constructor Injection يقوم بحقن التبعيات من خلال معاملات المُهيئ. هذه هي الطريقة المفضلة، التي تضمن أن يكون الكائن دائمًا في حالة صالحة منذ لحظة الإنشاء. تقوم Swinject تلقائيًا بحل جميع التبعيات التي تم تمريرها إلى المنشئ.

swift
class LoginViewModel {
    private let authService: AuthProtocol

    init(authService: AuthProtocol) {
        self.authService = authService
    }
}

container.register(AuthProtocol.self) { _ in
    AuthService()
}
container.register(LoginViewModel.self) { r in
    LoginViewModel(authService: r.resolve(AuthProtocol.self)!)
}

Property Injection

Property Injection يقوم بحقن التبعيات عن طريق تعيين خصائص الكائن بعد التهيئة. يُستخدم عندما تكون التبعية اختيارية أو لا يمكن تمريرها عبر المنشئ، على سبيل المثال، عند العمل مع Storyboard، حيث يتم إنشاء view controller تلقائيًا. تدعم Swinject التعليق @Inject للحقن التلقائي للخصائص في وقت التشغيل دون استدعاء صريح لـ resolve.

عند استخدام Property Injection، من المهم التأكد من تعيين التبعية قبل الوصول الأول إلى الكائن. وإلا، ستبقى الخاصية nil، مما يؤدي إلى تعطل غير متوقع. تحل Swinject هذه المشكلة من خلال آلية Implicitly Unwrapped Optional والتحقق الصارم في مرحلة حل الرسم البياني للتبعيات.

Method Injection

Method Injection يقوم بحقن التبعيات من خلال معاملات الطريقة. يُستخدم للخدمات المطلوبة فقط لتنفيذ عملية واحدة ولا يجب تخزينها كحالة دائمة للكائن. هذه هي الطريقة الأقل شيوعًا ولكنها مفيدة لـ الاستدعاءات.

النطاقات في Swinject والغرض منها

ObjectScope هي آلية تحدد دورة حياة المثيل الذي تم إنشاؤه داخل حاوية Swinject. يوفر الإطار ثلاثة نطاقات مدمجة مع إمكانية إنشاء نطاقات مخصصة عبر ObjectScopeProtocol.

ObjectScope.graph

نطاق graph هو القيمة الافتراضية. كل استدعاء لـ resolve ينشئ مثيلًا جديدًا يعيش فقط طوال مدة حل الرسم البياني للتبعيات. هذا اختيار آمن للخدمات عديمة الحالة لأنه يزيل تسرب الذاكرة بسبب التخزين المؤقت.

ObjectScope.container

نطاق container هو singleton داخل الحاوية. يتم إنشاء المثيل مرة واحدة عند أول resolve ويتم إرجاعه في جميع الطلبات اللاحقة. مناسب للخدمات ذات الحالة المشتركة: تخزين البيانات المؤقت، المسجل، إعدادات التطبيق.

ObjectScope.transient

نطاق transient ينشئ مثيلًا جديدًا في كل استدعاء لـ resolve دون تخزين مؤقت. يُستخدم للكائنات الخفيفة التي لا تحتاج إلى إعادة استخدامها — على سبيل المثال، الوحدات التي تعالج طلب HTTP محدد.

النطاقدورة الحياةالاستخدام الموصى به
graphطوال مدة حل الرسم البيانيخدمات عديمة الحالة افتراضيًا
containerطوال عمر الحاويةSingletons: تخزين مؤقت، مسجل، عميل شبكة
transientبدون تخزين مؤقتكائنات خفيفة للاستخدام لمرة واحدة

Swinject في مشاريع iOS

يبدأ دمج Swinject في مشروع iOS حقيقي بتهيئة الحاوية عند بدء التطبيق — في AppDelegate أو المشهد. يُوصى بتنظيم التسجيلات باستخدام Assembly: فئة أو هيكل منفصل يجمع الخدمات ذات الصلة.

وفقًا لاستطلاع Swift Developer Community (2025)، يستخدم 43% من مطوري iOS حاويات DI في المشاريع التجارية لإدارة تبعيات طبقة الشبكة والمستودعات ومنسقي التنقل. تظل Swinject الحل الأكثر شعبية نظرًا لـ بناء الجملة البسيط والتوافق مع Objective-C.

Storyboard Injection هي ميزة فريدة لـ Swinject: تقوم الحاوية تلقائيًا بحقن التبعيات في وحدات التحكم في العرض المنشأة من Storyboard دون كود إضافي في AppDelegate. يستخدم هذا محللاً خاصًا يتم تمريره إلى UIStoryboard عبر طريقة init(container:)، التي تعترض إنشاء view controller وتحقق التبعيات المسجلة.

في المشاريع الكبيرة، يمكن دمج Swinject مع منسقي التنقل: يتلقى المنسق الحاوية وينشئ الشاشات عن طريق حل تبعياتها عبر resolve، مما يحافظ على نقطة تكوين واحدة للمشهد بأكمله.

هندسة Assembly هي النمط الموصى به لتنظيم التسجيلات. كل Assembly يجمع الخدمات ذات الصلة (مثل NetworkingAssembly و DatabaseAssembly) ويمكن أن يعتمد على Assemblies أخرى. عند تهيئة الحاوية، يتم تحميل جميع Assemblies وتسجيل خدماتها، مما يوفر فصلًا واضحًا للمسؤوليات ويبسط التنقل عبر تكوين DI في المشاريع الكبيرة ذات العشرات من الخدمات.

لتصحيح أخطاء رسم DI البياني، يوفر Swinject امتداد SwinjectPropertyLoader الذي يقوم بتحميل التكوين من ملف plist، و SwinjectStoryboard — التكامل مع القصص المصورة عبر إصدار خاص من UIStoryboard. هذه الأدوات مفيدة بشكل خاص عند ترحيل مشروع موجود من البناء اليدوي للكائنات إلى DI: يمكن للمطور تسجيل الخدمات تدريجيًا، والتحقق من الرسم البياني للتبعيات من خلال الاختبارات وتسجيل أخطاء الحل دون إيقاف تطوير الميزات الرئيسية.

يوفر Swinject أيضًا التكامل مع RxSwift و Combine من خلال امتداد SwinjectAutoregistration للحل التلقائي للتبعيات بناءً على أنواع معاملات المُهيئ دون تسجيل مصانع صريح. هذا يقلل من كمية كود التسجيل للخدمات البسيطة: ما عليك سوى استدعاء container.register(ServiceProtocol.self) دون تحديد مصنع، وسيقوم Swinject تلقائيًا ببناء المصنع بناءً على انعكاس Signal المقدم من وقت تشغيل Swift. يُوصى بهذا النهج للخدمات التي تقبل منشئاتها أنواعًا أساسية فقط ولا تتطلب منطق إنشاء معقد.

الأسئلة الشائعة

كيف يختلف Swinject عن أطر DI الأخرى لـ Swift؟

Swinject مكتوب بلغة Swift نقية دون توليد كود أو انعكاس. على عكس Needle، لا يتطلب توليد مصادر، وبالمقارنة مع Dip، يوفر دعمًا مدمجًا لـ Storyboard Injection، مما يبسط التكامل في مشاريع UIKit الحالية.

كيفية تثبيت Swinject عبر Swift Package Manager؟

أضف الحزمة عبر URL github.com/Swinject/Swinject من خلال Xcode في قائمة File — Add Packages. التثبيت عبر CocoaPods و Carthage متاح أيضًا. بعد التثبيت، قم باستيراد وحدة Swinject وأنشئ مثيل Container.

هل يمكن استخدام Swinject في مشاريع SwiftUI؟

نعم، Swinject متوافقة تمامًا مع SwiftUI. يتم حقن التبعيات عبر مُهيئات View أو عبر Environment، حيث يتم تمرير الحاوية كـ EnvironmentObject. Swinject لا تعتمد على UIKit وتعمل بشكل متساوٍ مع كلا الإطارين.

كيفية استخدام Swinject للاختبارات الوحدوية؟

أنشئ حاوية منفصلة للاختبارات، مع استبدال الخدمات الحقيقية بـ mocks. تسمح Swinject بتجاوز التسجيلات دون تغيير كود المستهلكين. يحصل كل اختبار على حاوية معزولة بمجموعة أدنى من التبعيات.

أي نطاق تختار لخدمة التحليلات؟

لـ التحليلات، استخدم نطاق container بحيث ترسل جميع الشاشات الأحداث عبر مثيل واحد. هذا يضمن قائمة إرسال موحدة وتشغيل صحيح لتجميع الدفعات دون ازدواجية البيانات بين المستهلكين المختلفين.

الخلاصة

  • Swinject — حاوية DI لـ Swift تؤتمت حقن التبعيات عبر Container و ObjectScope.
  • Dependency Injection تقلل من اقتران الكود، وتبسط الاختبارات وتسمح باستبدال التطبيقات دون تغيير المستهلكين.
  • Container — سجل خدمات يدعم register للتسجيل و resolve لاسترجاع المثيل.
  • Constructor Injection هي طريقة الحقن المفضلة، التي تضمن حالة صالحة للكائن.
  • ObjectScope يدير دورة الحياة: graph (افتراضي)، container (singleton) و transient (بدون تخزين مؤقت).
  • Storyboard Injection يحقن تلقائيًا التبعيات في مشاهد UIKit دون إعداد يدوي.
  • للاختبارات الوحدوية، استخدم حاوية منفصلة مع تطبيقات mock للخدمات.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا