SPM (Swift Package Manager) هو مدير حزم مدمج في نظام Swift البيئي، طورته Apple لأتمتة ربط وبناء وتحديث المكتبات الخارجية. SPM جزء من مترجم Swift منذ الإصدار 3.0 (2016) ولا يتطلب تثبيتًا منفصلًا. على عكس CocoaPods و Carthage، يتكامل SPM مباشرة مع المترجم و Xcode، مما يجعله الأداة القياسية لإدارة التبعيات في مشاريع Swift الحديثة. في هذه المقالة سنشرح بنية Package.swift وأوامر SPM وإنشاء الحزم الخاصة بك والترحيل من المديرين البديلين.
النقاط الرئيسية
SPM (Swift Package Manager) هو مدير الحزم الرسمي للغة Swift، المدمج في مترجم swiftc وبيئة التطوير Xcode. يتيح للمطورين إضافة مكتبات خارجية وإدارة إصداراتها ونشر حزمهم الخاصة. ظهر SPM لأول مرة في Swift 3.0 (سبتمبر 2016) كأداة سطر أوامر، واعتبارًا من Xcode 11 (2019) حصل على تكامل كامل مع الواجهة الرسومية — الآن تُضاف التبعيات عبر قائمة File ← Add Packages.
SPM يقوم تلقائيًا بتنزيل الكود المصدري للتبعيات من مستودعات Git، ويبنيها بالتوازي مع المشروع الرئيسي ويخزن النتائج في ذاكرة تخزين مؤقت لتكون عمليات البناء اللاحقة أسرع. على عكس CocoaPods، لا ينشئ SPM مساحة عمل منفصلة (xcworkspace) — تصبح التبعيات جزءًا من مشروع Xcode الرئيسي. وفقًا لاستطلاع Swift.org Developer Survey (2024)، يستخدم 67% من مطوري iOS أداة SPM، مما يجعلها الأداة الأكثر شعبية لإدارة التبعيات في نظام Swift البيئي.
يدعم SPM ثلاث منصات: Apple (iOS و macOS و tvOS و watchOS و visionOS) و Linux (Ubuntu و CentOS و Amazon Linux) و Swift من جانب الخادم (Vapor و Kitura). على Linux، يعمل SPM بالكامل عبر سطر الأوامر بدون Xcode.
تم بناء SPM حول ثلاثة مفاهيم رئيسية: الحزم (packages) و المنتجات (products) و الأهداف (targets). الحزمة هي مستودع Git مع بيان Package.swift. المنتج هو نتيجة البناء (مكتبة أو ملف قابل للتنفيذ). الهدف هو وحدة داخل الحزمة تُترجم إلى وحدة بناء.
عندما يضيف المطور تبعية إلى Package.swift، يقوم SPM بتنفيذ الخطوات التالية:
~Library/Caches/org.swift.swiftpm/.يقوم ملف Package.resolved بتثبيت الإصدارات الدقيقة لجميع التبعيات ليعمل فريق التطوير مع مجموعة متطابقة من المكتبات. يجب إضافة هذا الملف إلى نظام التحكم في الإصدارات (git).
الميزة الرئيسية لـ SPM مقارنة بالبدائل هي غياب سجل مركزي. يمكن أن توجد الحزم في أي مستودع Git عام: GitHub أو GitLab أو Bitbucket، وكذلك على خوادم Git الخاصة بالشركة. منذ Swift 5.2، يدعم SPM التبعيات الثنائية (binary targets) — المكتبات المغلقة الموزعة كـ XCFramework دون توفير الكود المصدري.
Package.swift هو ملف Swift يصف بنية الحزمة وتبعياتها. الملف مكتوب بلغة Swift نفسها (ليس JSON أو YAML)، مما يتيح استخدام المنطق الشرطي والثوابت المحسوبة والدوال داخل البيان.
البنية الأساسية لـ Package.swift:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "MyLibrary",
platforms: [
.iOS(.v16),
.macOS(.v13)
],
products: [
.library(
name: "MyLibrary",
targets: ["MyLibrary"]
),
],
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git",
from: "5.9.0"),
.package(url: "https://github.com/onevcat/Kingfisher.git",
from: "7.12.0"),
],
targets: [
.target(
name: "MyLibrary",
dependencies: [
"Alamofire",
"Kingfisher"
]
),
.testTarget(
name: "MyLibraryTests",
dependencies: ["MyLibrary"]
),
]
)
دعنا نشرح العناصر الرئيسية:
// swift-tools-version: 5.9 — توجيه يحدد إصدار SPM؛ بناءً عليه يتوفر بناء جملة البيان.name — اسم الحزمة، يظهر في Xcode ويستخدم في روابط التبعيات.platforms — الحد الأدنى لإصدارات المنصات؛ لن يسمح SPM ببناء الحزمة على إصدار أقدم من نظام التشغيل.products — ما "يصدره" الحزمة: مكتبة (.library) أو ملف قابل للتنفيذ (.executable).dependencies — قائمة الحزم الخارجية مع عنوان URL والإصدار؛ يدعم from: و exact: و branch: و revision:.targets — أهداف البناء؛ كل هدف يحتوي على قائمة التبعيات والموارد وملفات swift من الدليل المقابل (Sources/TargetName/).مثال على تحديد إصدار دقيق وفرع و commit:
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git",
exact: "1.17.3"),
.package(url: "https://github.com/pointfreeco/swift-composable-architecture.git",
branch: "main"),
.package(url: "https://github.com/apple/swift-log.git",
revision: "e5c6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4"),
]
منذ Swift 5.9، أضاف Package.swift دعمًا لـ static framework و linkerSettings، مما يسمح بتكوين أكثر دقة للرابط للمكتبات الثابتة والديناميكية.
يوفر Swift Package Manager مجموعة من الأوامر للعمل عبر الطرفية. تُشغَّل الأوامر من الدليل الجذر للحزمة (حيث يوجد Package.swift).
# إنشاء حزمة جديدة مع مكتبة
swift package init --type library
# إنشاء حزمة قابلة للتنفيذ (تطبيق وحدة تحكم)
swift package init --type executable
# بناء المشروع
swift build
# بناء في تهيئة الإصدار
swift build -c release
# تشغيل الاختبارات
swift test
# تشغيل اختبار محدد
swift test --filter "MyLibraryTests/testExample"
# تنزيل وحل التبعيات
swift package resolve
# تحديث التبعيات إلى أحدث الإصدارات المتاحة
swift package update
# عرض رسم بياني للتبعيات
swift package show-dependencies
# تنظيف ذاكرة التخزين المؤقت للبناء
swift package clean
# إنشاء مشروع Xcode (قبل Xcode 11)
swift package generate-xcodeproj
عند العمل داخل Xcode، يتم تنفيذ معظم هذه الأوامر تلقائيًا: تُحل التبعيات عند فتح المشروع، ويبدأ البناء بـ ⌘B، والاختبارات بـ ⌘U. ومع ذلك، فإن معرفة أوامر الطرفية ضرورية لخطوط أنابيب CI/CD (GitHub Actions و GitLab CI و Jenkins) حيث لا يتوفر Xcode.
يقوم الأمر swift package resolve بإنشاء أو تحديث ملف Package.resolved. يثبت هذا الملف الإصدارات الدقيقة لجميع التبعيات بما في ذلك التبعيات المتعدية، ويجب إضافته إلى git. يُوصى بتشغيل swift package update قبل كل فرع ميزة جديد للعمل بأحدث إصدارات المكتبات.
إنشاء حزمة SPM الخاصة بك مفيد لتغليف منطق الأعمال في مشاريع متعددة الوحدات ولنشر المكتبات مفتوحة المصدر. دعنا نستعرض العملية خطوة بخطوة.
mkdir MyNetworkKit
cd MyNetworkKit
swift package init --type library
يقوم الأمر swift package init بإنشاء الهيكل التالي:
MyNetworkKit/
├── Package.swift
├── README.md
├── Sources/
│ └── MyNetworkKit/
│ └── MyNetworkKit.swift
└── Tests/
└── MyNetworkKitTests/
└── MyNetworkKitTests.swift
يقوم SPM بمسح الدليلين Sources/ و Tests/ تلقائيًا: كل دليل فرعي داخل Sources يتوافق مع هدف (target).
دعنا نضيف التبعيات ونضبط المنصات المستهدفة:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "MyNetworkKit",
platforms: [
.iOS(.v15),
.macOS(.v12)
],
products: [
.library(
name: "MyNetworkKit",
targets: ["MyNetworkKit"]
),
],
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git",
from: "5.9.0"),
],
targets: [
.target(
name: "MyNetworkKit",
dependencies: ["Alamofire"]
),
.testTarget(
name: "MyNetworkKitTests",
dependencies: ["MyNetworkKit"]
),
]
)
// Sources/MyNetworkKit/MyNetworkKit.swift
import Foundation
import Alamofire
public struct NetworkClient {
private let session: Session
public init() {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
self.session = Session(configuration: configuration)
}
public func fetchData(from url: String) async throws -> Data {
let response = try await session.request(url).serializingData().value
return response
}
}
ادفع الحزمة إلى مستودع Git وأنشئ علامة SemVer:
git init
git add .
git commit -m "Initial commit: MyNetworkKit"
git remote add origin https://github.com/username/MyNetworkKit.git
git push -u origin main
git tag 1.0.0
git push --tags
بعد ذلك، يمكن لأي مطور إضافة حزمتك باستخدام .package(url: "https://github.com/username/MyNetworkKit.git", from: "1.0.0").
Alamofire هو عميل HTTP الأكثر شعبية لـ Swift. دعنا نضيفه عبر SPM وننفذ طلب GET.
import Alamofire
func fetchUsers() {
AF.request("https://jsonplaceholder.typicode.com/users")
.validate()
.responseDecodable(of: [User].self) { response in
switch response.result {
case .success(let users):
print("تم استلام (users.count) مستخدم")
case .failure(let error):
print("خطأ: (error.localizedDescription)")
}
}
}
مكتبة Swinject توفر حاوية DI لـ Swift. تُضاف عبر .package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0").
import Swinject
let container = Container()
container.register(NetworkServiceProtocol.self) { _ in NetworkService() }
container.register(DataRepositoryProtocol.self) { r in
DataRepository(networkService: r.resolve(NetworkServiceProtocol.self)!)
}
let repository = container.resolve(DataRepositoryProtocol.self)
repository?.loadData()
حزمة swift-log من Apple توفر واجهة برمجة تسجيل موحدة تدعم عدة خلفيات (OSLog و وحدة التحكم والملفات).
import Logging
var logger = Logger(label: "com.myapp.network")
logger.logLevel = .debug
logger.info("بدأ طلب الشبكة", metadata: [
"url": "(requestURL)",
"method": "GET"
])
logger.warning("تجاوز وقت الاستجابة ثانيتين")
logger.error("خطأ في الاتصال: لا يوجد إنترنت")
تغطي هذه الأمثلة الثلاثة سيناريوهات الاستخدام النموذجية لـ SPM: عملاء HTTP وحاويات DI والبنية التحتية للنظام. اختيار المكتبات ليس عشوائيًا — Alamofire و Swinject و swift-log من بين أفضل 20 حزمة Swift من حيث النجوم على GitHub.
إذا كان مشروعك يستخدم CocoaPods أو Carthage، فإن الترحيل إلى SPM يتم في بضع خطوات. العملية آمنة: يمكن لتبعيات SPM أن تتعايش مع CocoaPods و Carthage في نفس المشروع، مما يسمح بالترحيل التدريجي.
.xcworkspace وافتح .xcodeproj ونفذ Clean Build Folder.rm -rf Carthage/ في الطرفية.اعتبارًا من 2025، يدعم SPM الغالبية العظمى من مكتبات Swift الشائعة. الاستثناءات هي بعض أطر ObjC بدون خرائط وحدات. إذا كانت المكتبة لا تدعم SPM بعد — تحقق من قسم Installation في ملف README الخاص بها؛ معظم المؤلفين أضافوا بالفعل دعم SPM في الإصدارات الأخيرة.
الأسئلة الشائعة
SPM مدمج في مترجم Swift و Xcode، ولا يتطلب تثبيتًا عبر gem أو Homebrew. يستخدم CocoaPods سجلاً مركزيًا Specs وينشئ مساحة عمل منفصلة. يعمل Carthage عبر أطر بدون تكامل مع المشروع. SPM هو المدير الوحيد المدمج على مستوى المترجم: يتم حل التبعيات وتخزينها مؤقتًا وبناؤها بالتوازي مع الكود الرئيسي.
نعم، يدعم SPM المشاريع المختلطة Swift + Objective-C. يتم تضمين ملفات ObjC داخل حزمة SPM تلقائيًا في Umbrella Header بشرط وجود modulemap صحيح. ومع ذلك، لا يدعم SPM مكتبات ObjC الثابتة التي لا تحتوي على خريطة وحدات. يُوصى بتوصيل مكتبات ObjC عبر SPM فقط إذا كانت توفر modulemap أو مكتوبة بلغة C نقية.
يستخدم SPM الإصدار الدلالي (SemVer). إذا كانت الحزمة A تتطلب Alamofire 5.8+ والحزمة B تتطلب Alamofire 5.9+، سيختار SPM الإصدار 5.9.x الذي يرضي كليهما. إذا كان التعارض غير قابل للحل (حزمة تتطلب 5.x وأخرى تتطلب 6.x)، سيبلغ SPM عن خطأ. في هذه الحالة، تحتاج إلى تحديث إحدى الحزم أو تحويل التبعية إلى إصدار متوافق مع كلا المتطلبين.
على macOS: ~Library/Caches/org.swift.swiftpm/ و ~/Library/Developer/Xcode/DerivedData/. على Linux: ~cache/swiftpm/. أثناء البناء، يخزن SPM الكود المصدري وملفات الكائنات المترجمة في ذاكرة تخزين مؤقت. لتنظيف ذاكرة التخزين المؤقت بالكامل، نفذ swift package reset — هذا الأمر يزيل ذاكرة التخزين المؤقت للتبعيات و DerivedData للمشروع الحالي.
نعم، منذ Swift 5.2 يدعم SPM الأهداف الثنائية (binary targets). يتم توزيع المكتبة المغلقة كـ XCFramework، ويُحدد المسار إلى .xcframework في Package.swift. لا يتم كشف الكود المصدري. يُحدد الهدف الثنائي عبر .binaryTarget(name: "PrivateSDK", path: "Sources/PrivateSDK.xcframework"). هذا يسمح بتوصيل SDK التجارية دون انتهاك اتفاقيات الترخيص.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.