حقن التبعية في Swift: المبادئ والأمثلة لتطبيقات iOS/macOS

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

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

ابدأ الدورة

مقدمة عن Dependency Injection في Swift



Dependency Injection (DI) هو أحد مبادئ التصميم الأساسية التي تساعد في إنشاء تطبيقات مرنة وقابلة للاختبار والصيانة باستخدام Swift. في سياق تطوير iOS وmacOS، يسمح DI بفصل إنشاء الكائنات عن استخدامها، مما يبسط بشكل كبير اختبار الكود وتعديله.



الفكرة الأساسية هي أن الفئة (class) لا ينبغي لها إنشاء تبعياتها بنفسها. بدلاً من ذلك، يتم "حقن" التبعيات من الخارج. هذا هو تطبيق مبدأ انعكاس التبعيات (Dependency Inversion Principle) من SOLID.



لماذا Dependency Injection مهم لتطوير iOS/macOS؟



في التطبيقات الأصلية بلغة Swift، غالباً ما نواجه مشكلة الاقتران الشديد (tight coupling). دعنا ننظر إلى مثال نموذجي:



class UserService {    let networkManager = NetworkManager()    let database = Database()        func fetchUser(id: Int) -> User {        // منطق    }}


هنا UserService مرتبط بشدة بتطبيقات محددة لـ NetworkManager و Database. هذا يخلق عدة مشاكل:



  • صعوبة الاختبار — من المستحيل استبدال الخدمات الحقيقية بـ mocks أو stubs
  • انخفاض قابلية إعادة الاستخدام — لا يمكن استخدام الفئة مع تطبيقات أخرى
  • انتهاك SRP — الفئة لا تقوم فقط بمنطق الأعمال، بل أيضاً بإنشاء التبعيات


Dependency Injection يحل هذه المشاكل، مما يسمح بحقن التبعيات عبر المُنشئ (constructor) أو الخصائص (properties) أو الطرق (methods).



طرق تنفيذ Dependency Injection في Swift



1. الحقن عبر المُنشئ (Constructor Injection)



الطريقة الأكثر شيوعاً والموصى بها. يتم تمرير التبعيات عبر المُهيئ (initializer):



protocol NetworkService {    func request(endpoint: String) -> Data}

protocol DatabaseService { func save(user: User)}

class UserService { private let networkService: NetworkService private let databaseService: DatabaseService init(networkService: NetworkService, databaseService: DatabaseService) { self.networkService = networkService self.databaseService = databaseService } func fetchUser(id: Int) -> User? { let data = networkService.request(endpoint: "users/\\(id)") // تحليل البيانات return User(id: id, name: "John") }}


المزايا: التبعيات واضحة، إلزامية لعمل الفئة، سهلة الاختبار.



2. الحقن عبر الخصائص (Property Injection)



يتم تعيين التبعيات بعد إنشاء الكائن:



class AuthManager {    var networkService: NetworkService?        func login(username: String, password: String) -> Bool {        guard let service = networkService else { return false }        // منطق المصادقة        return true    }}

// الاستخدامlet authManager = AuthManager()authManager.networkService = RealNetworkService()


العيب: قد لا يتم تعيين التبعيات، مما يؤدي إلى أنواع اختيارية واحتمال حدوث أعطال.



3. الحقن عبر الطريقة (Method Injection)



يتم تمرير التبعية مباشرة إلى الطريقة:



class DataProcessor {    func process(data: Data, using service: NetworkService) -> ProcessedData {        let result = service.request(endpoint: "process")        // معالجة        return ProcessedData()    }}


مناسب للحالات التي تكون فيها التبعية مطلوبة فقط لطريقة واحدة.



مثال عملي: تطبيق لإدارة المهام



لنقم بإنشاء تطبيق iOS بسيط يستخدم DI:



// بروتوكولات التبعياتprotocol TaskRepository {    func fetchTasks() ->                

المدونات

توصيات الكتب