مقدمة: لماذا المستودع الأحادي في Swift؟
غالبًا ما يواجه التطوير لنظامي iOS وmacOS معضلة: كيف ننظم الكود بحيث يظل قابلاً للتوسع وإعادة الاستخدام وسهل الصيانة؟ يؤدي النهج التقليدي مع العديد من المستودعات المنفصلة (multi-repo) إلى التجزئة، وتكرار التبعيات، وصعوبات في تنسيق التغييرات. الحل هو المستودع الأحادي (monorepo).
المستودع الأحادي هو مستودع واحد يحتوي على جميع مكونات المشروع: التطبيقات، المكتبات، أدوات البناء، والتكوينات. بالنسبة لنظام Swift البيئي، هذا النهج مهم بشكل خاص بفضل Swift Package Manager (SPM)، الذي يدعم بشكل ممتاز الحزم المحلية والهندسة المعمارية المعيارية منذ الإصدار 5.3+.
في هذه المقالة، سنشرح كيفية تنظيم مستودع أحادي لمشاريع Swift، وسننظر في الهيكل، والأدوات (Tuist, XcodeGen)، وأفضل الممارسات مع أمثلة الكود.
1. مزايا المستودع الأحادي لـ Swift
قبل الانتقال إلى التنفيذ، دعنا نلقي نظرة على المزايا الرئيسية:
- إصدار موحد للتبعيات — تستخدم جميع الوحدات نفس الإصدار من المكتبات، مما يستبعد التعارضات.
- الالتزامات الذرية — التغيير في API في حزمة واحدة ينعكس فورًا على جميع الوحدات التابعة، دون الحاجة إلى مزامنة بين المستودعات.
- تبسيط CI/CD — خط أنابيب واحد، تكوين واحد، تخزين مؤقت للبناءات.
- إعادة استخدام الكود — الأدوات المساعدة المشتركة، النماذج، طبقات الشبكة متاحة بسهولة لجميع الأهداف (iOS, macOS, watchOS).
- مراجعة الكود — التغييرات في أجزاء مختلفة من المشروع مرئية في PR واحد.
ومع ذلك، هناك أيضًا عيوب: حجم المستودع، وقت الاستنساخ، الحاجة إلى هيكل مدروس. ولكن بالنسبة للفرق متوسطة الحجم وما فوق، غالبًا ما يكون المستودع الأحادي هو الخيار الأمثل.
2. هيكل المستودع الأحادي في Swift
لنلقِ نظرة على هيكل نموذجي لمشروع يتضمن تطبيق iOS وتطبيق macOS مع مكتبة مشتركة:
MyProject/├── Apps/│ ├── iOSApp/│ │ ├── Sources/│ │ │ └── AppDelegate.swift│ │ ├── Resources/│ │ └── Package.swift│ └── macOSApp/│ ├── Sources/│ │ └── AppDelegate.swift│ ├── Resources/│ └── Package.swift├── Packages/│ ├── Core/│ │ ├── Sources/│ │ │ ├── Core.swift│ │ │ └── Extensions/│ │ ├── Tests/│ │ └── Package.swift│ ├── Networking/│ │ ├── Sources/│ │ │ └── NetworkManager.swift│ │ ├── Tests/│ │ └── Package.swift│ └── UIComponents/│ ├── Sources/│ │ └── CustomButton.swift│ ├── Tests/│ └── Package.swift├── Tools/│ └── Scripts/│ └── build.sh├── .github/│ └── workflows/│ └── ci.yml├── Package.swift (جذر)├── Tuist.swift (إذا كنا نستخدم Tuist)└── README.mdشرح الهيكل:
Apps/— دليل التطبيقات النهائية. كل تطبيق هو حزمة Swift منفصلة تعتمد على الحزم منPackages/.Packages/— جميع المكتبات والوحدات الداخلية. كل حزمة لهاPackage.swiftالخاص بها ويمكن اختبارها بشكل مستقل.Tools/— نصوص البناء، أدوات التحليل، توليد الكود.Package.swiftالجذر (أو تكوين Tuist/XcodeGen) — يصف مساحة العمل والروابط بين الحزم.
2.1 مثال على Package.swift الجذر (SPM)
// swift-tools-version:5.9import PackageDescription
let package = Package( name: "MyProject", platforms: [ .iOS(.v16), .macOS(.v13) ], products: [ .library(name: "Core", targets: ["Core"]), .library(name: "Networking", targets: ["Networking"]), .library(name: "UICompon