SPM (Swift Package Manager) — مدیر بسته داخلی اکوسیستم Swift است که توسط Apple برای خودکارسازی اتصال، ساخت و بهروزرسانی کتابخانههای شخص ثالث توسعه یافته است. SPM از نسخه 3.0 (سال 2016) بخشی از کامپایلر Swift است و نیاز به نصب جداگانه ندارد. برخلاف 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 یک workspace جداگانه (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). در لینوکس، 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/) است.مثال تعیین نسخه دقیق، شاخه و کامیت:
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، پشتیبانی از static/framework و linkerSettings به Package.swift اضافه شده است که امکان پیکربندی دقیقتر لینکینگ برای کتابخانههای استاتیک و دینامیک را فراهم میکند.
Swift Package Manager مجموعهای از دستورات را برای کار از طریق ترمینال ارائه میدهد. دستورات از دایرکتوری ریشه بسته (جایی که Package.swift قرار دارد) اجرا میشوند.
# ایجاد یک بسته جدید با کتابخانه
swift package init --type library
# ایجاد بسته اجرایی (برنامه کنسول)
swift package init --type executable
# ساخت پروژه
swift build
# ساخت با پیکربندی release
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 اضافه شود. توصیه میشود قبل از هر شاخه feature جدید، swift package update را اجرا کنید تا با آخرین نسخههای کتابخانهها کار کنید.
ایجاد بسته SPM اختصاصی برای کپسولهسازی منطق کسبوکار در پروژههای چندماژوله و انتشار کتابخانههای open-source مفید است. فرآیند گامبهگام را بررسی میکنیم.
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 — یک API لاگگیری یکپارچه که از بکاندهای متعدد (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 در بین ۲۰ بسته محبوب Swift در GitHub قرار دارند.
اگر پروژه از CocoaPods یا Carthage استفاده میکند، مهاجرت به SPM در چند مرحله انجام میشود. فرآیند امن است: وابستگیهای SPM میتوانند با CocoaPods و Carthage در یک پروژه共存 داشته باشند که امکان مهاجرت تدریجی را فراهم میکند.
.xcworkspace را حذف کرده، .xcodeproj را باز کرده و Clean Build Folder را اجرا کنید.rm -rf Carthage/.تا سال 2025، SPM از اکثریت قریببهاتفاق کتابخانههای محبوب Swift پشتیبانی میکند. استثناها برخی فریمورکهای ObjC بدون نقشه ماژول (modulemap) هستند. اگر کتابخانهای هنوز از SPM پشتیبانی نمیکند — بخش Installation را در README آن بررسی کنید؛ بیشتر نویسندگان در آخرین نسخهها پشتیبانی SPM را اضافه کردهاند.
سوالات متداول
SPM در کامپایلر Swift و Xcode تعبیه شده است و نیازی به نصب از طریق gem یا Homebrew ندارد. CocoaPods از رجیستری متمرکز Specs استفاده میکند و یک workspace جداگانه تولید میکند. Carthage از طریق فریمورکها بدون ادغام با پروژه کار میکند. SPM تنها مدیری است که در سطح کامپایلر ادغام شده است: وابستگیها حل، کش و به موازات کد اصلی ساخته میشوند.
بله، SPM از پروژههای ترکیبی Swift + Objective-C پشتیبانی میکند. فایلهای ObjC درون بسته SPM به شرط وجود modulemap صحیح به صورت خودکار در Umbrella Header قرار میگیرند. با این حال، 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 ارائه میشود و در Package.swift مسیر .xcframework مشخص میشود. کد منبع فاش نمیشود. Binary target از طریق .binaryTarget(name: "PrivateSDK", path: "Sources/PrivateSDK.xcframework") مشخص میشود. این امکان اتصال SDKهای تجاری را بدون نقض توافقنامههای مجوز فراهم میکند.
جمعبندی
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.