SPM (Swift Package Manager) là trình quản lý gói tích hợp trong hệ sinh thái Swift, được Apple phát triển để tự động hóa việc kết nối, xây dựng và cập nhật các thư viện bên thứ ba. SPM là một phần của trình biên dịch Swift từ phiên bản 3.0 (2016) và không yêu cầu cài đặt riêng. Không giống như CocoaPods và Carthage, SPM tích hợp trực tiếp với trình biên dịch và Xcode, trở thành công cụ quản lý phụ thuộc tiêu chuẩn trong các dự án Swift hiện đại. Trong bài viết này, chúng ta sẽ phân tích cấu trúc của Package.swift, các lệnh SPM, tạo gói tùy chỉnh và di chuyển từ các trình quản lý thay thế.
Những điểm chính
SPM (Swift Package Manager) là trình quản lý gói chính thức cho ngôn ngữ Swift, được tích hợp trong trình biên dịch swiftc và môi trường phát triển Xcode. Nó cho phép nhà phát triển thêm thư viện bên thứ ba, quản lý phiên bản và xuất bản gói riêng của họ. SPM xuất hiện lần đầu trong Swift 3.0 (tháng 9 năm 2016) như một công cụ dòng lệnh và từ Xcode 11 (2019) đã được tích hợp hoàn toàn với giao diện đồ họa — các phụ thuộc hiện được thêm qua menu File → Add Packages.
SPM tự động tải mã nguồn của phụ thuộc từ kho Git, xây dựng chúng song song với dự án chính và lưu trữ kết quả để các lần xây dựng sau nhanh hơn. Không giống CocoaPods, SPM không tạo không gian làm việc riêng (xcworkspace) — các phụ thuộc trở thành một phần của dự án Xcode chính. Theo khảo sát Swift.org Developer Survey (2024), 67% nhà phát triển iOS sử dụng SPM, biến nó thành công cụ quản lý phụ thuộc phổ biến nhất trong hệ sinh thái Swift.
SPM hỗ trợ ba nền tảng: Apple (iOS, macOS, tvOS, watchOS, visionOS), Linux (Ubuntu, CentOS, Amazon Linux) và Swift phía máy chủ (Vapor, Kitura). Trên Linux, SPM hoạt động hoàn toàn qua dòng lệnh mà không cần Xcode.
SPM được xây dựng xoay quanh ba khái niệm chính: gói (packages), sản phẩm (products) và mục tiêu (targets). Gói là một kho Git với tệp kê khai Package.swift. Sản phẩm là kết quả xây dựng (thư viện hoặc tệp thực thi). Mục tiêu là một mô-đun bên trong gói được biên dịch thành một đơn vị xây dựng.
Khi nhà phát triển thêm phụ thuộc vào Package.swift, SPM thực hiện các bước sau:
~Library/Caches/org.swift.swiftpm/.Tệp Package.resolved khóa các phiên bản chính xác của tất cả phụ thuộc để nhóm phát triển làm việc với cùng một bộ thư viện. Tệp này nên được thêm vào hệ thống kiểm soát phiên bản (git).
Một lợi thế chính của SPM so với các giải pháp thay thế là không có kho lưu trữ tập trung. Các gói có thể nằm trong bất kỳ kho Git công khai nào: GitHub, GitLab, Bitbucket, cũng như trên máy chủ Git riêng của công ty. Từ Swift 5.2, SPM hỗ trợ phụ thuộc nhị phân (binary targets) — thư viện mã nguồn đóng được phân phối dưới dạng XCFramework mà không cung cấp mã nguồn.
Package.swift là tệp Swift mô tả cấu trúc gói và các phụ thuộc của nó. Tệp được viết bằng chính Swift (không phải JSON hay YAML), cho phép sử dụng logic điều kiện, hằng số tính toán và hàm bên trong tệp kê khai.
Cấu trúc cơ bản của 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"]
),
]
)
Hãy phân tích các thành phần chính:
// swift-tools-version: 5.9 — chỉ thị xác định phiên bản SPM; cú pháp khả dụng của tệp kê khai phụ thuộc vào nó.name — tên gói, hiển thị trong Xcode và được sử dụng trong liên kết phụ thuộc.platforms — phiên bản nền tảng tối thiểu; SPM sẽ không cho phép xây dựng gói trên phiên bản OS cũ hơn.products — những gì gói "xuất khẩu": thư viện (.library) hoặc tệp thực thi (.executable).dependencies — danh sách gói bên ngoài với URL và phiên bản; hỗ trợ from:, exact:, branch:, revision:.targets — mục tiêu xây dựng; mỗi mục tiêu chứa danh sách phụ thuộc, tài nguyên và tệp swift từ thư mục tương ứng (Sources/TargetName/).Ví dụ chỉ định phiên bản chính xác, nhánh và 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"),
]
Từ Swift 5.9, Package.swift đã thêm hỗ trợ cho static framework và linkerSettings, cho phép cấu hình liên kết chính xác hơn cho thư viện tĩnh và động.
Swift Package Manager cung cấp một tập hợp lệnh để làm việc qua terminal. Các lệnh được chạy từ thư mục gốc của gói (nơi có Package.swift).
# Tạo gói mới với thư viện
swift package init --type library
# Tạo gói thực thi (ứng dụng console)
swift package init --type executable
# Xây dựng dự án
swift build
# Xây dựng với cấu hình release
swift build -c release
# Chạy kiểm thử
swift test
# Chạy kiểm thử cụ thể
swift test --filter "MyLibraryTests/testExample"
# Tải xuống và giải quyết phụ thuộc
swift package resolve
# Cập nhật phụ thuộc lên phiên bản mới nhất
swift package update
# Hiển thị đồ thị phụ thuộc
swift package show-dependencies
# Xóa bộ nhớ đệm xây dựng
swift package clean
# Tạo dự án Xcode (trước Xcode 11)
swift package generate-xcodeproj
Khi làm việc trong Xcode, hầu hết các lệnh này được thực thi tự động: phụ thuộc được giải quyết khi mở dự án, xây dựng bắt đầu bằng ⌘B, kiểm thử bằng ⌘U. Tuy nhiên, kiến thức về lệnh terminal là cần thiết cho các pipeline CI/CD (GitHub Actions, GitLab CI, Jenkins) nơi Xcode không có sẵn.
Lệnh swift package resolve tạo hoặc cập nhật tệp Package.resolved. Tệp này khóa phiên bản chính xác của tất cả phụ thuộc, bao gồm cả phụ thuộc bắc cầu và nên được thêm vào git. Nên chạy swift package update trước mỗi nhánh tính năng mới để làm việc với phiên bản thư viện mới nhất.
Tạo gói SPM riêng của bạn hữu ích để đóng gói logic kinh doanh trong các dự án đa mô-đun và xuất bản thư viện mã nguồn mở. Hãy xem quy trình từng bước.
mkdir MyNetworkKit
cd MyNetworkKit
swift package init --type library
Lệnh swift package init tạo cấu trúc sau:
MyNetworkKit/
├── Package.swift
├── README.md
├── Sources/
│ └── MyNetworkKit/
│ └── MyNetworkKit.swift
└── Tests/
└── MyNetworkKitTests/
└── MyNetworkKitTests.swift
SPM tự động quét các thư mục Sources/ và Tests/: mỗi thư mục con trong Sources tương ứng với một mục tiêu (target).
Hãy thêm phụ thuộc và cấu hình nền tảng mục tiêu:
// 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
}
}
Đẩy gói lên kho Git và tạo thẻ 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
Sau đó, bất kỳ nhà phát triển nào cũng có thể thêm gói của bạn bằng .package(url: "https://github.com/username/MyNetworkKit.git", from: "1.0.0").
Alamofire là HTTP client phổ biến nhất cho Swift. Hãy thêm nó qua SPM và thực hiện yêu cầu 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("Đã nhận (users.count) người dùng")
case .failure(let error):
print("Lỗi: (error.localizedDescription)")
}
}
}
Thư viện Swinject cung cấp vùng chứa DI cho Swift. Nó được thêm qua .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()
Gói swift-log của Apple cung cấp API ghi nhật ký thống nhất hỗ trợ nhiều backend (OSLog, console, tệp).
import Logging
var logger = Logger(label: "com.myapp.network")
logger.logLevel = .debug
logger.info("Yêu cầu mạng đã bắt đầu", metadata: [
"url": "(requestURL)",
"method": "GET"
])
logger.warning("Thời gian phản hồi vượt quá 2 giây")
logger.error("Lỗi kết nối: không có internet")
Ba ví dụ này bao gồm các kịch bản sử dụng SPM điển hình: HTTP client, vùng chứa DI và cơ sở hạ tầng hệ thống. Việc chọn thư viện không phải ngẫu nhiên — Alamofire, Swinject và swift-log nằm trong top 20 gói Swift có nhiều sao nhất trên GitHub.
Nếu dự án của bạn sử dụng CocoaPods hoặc Carthage, việc di chuyển sang SPM được thực hiện trong vài bước. Quy trình này an toàn: các phụ thuộc SPM có thể cùng tồn tại với CocoaPods và Carthage trong cùng một dự án, cho phép di chuyển dần dần.
.xcworkspace, mở .xcodeproj và thực hiện Clean Build Folder.rm -rf Carthage/ trong terminal.Tính đến năm 2025, SPM hỗ trợ đại đa số thư viện Swift phổ biến. Ngoại lệ là một số framework ObjC không có bản đồ mô-đun. Nếu thư viện chưa hỗ trợ SPM — hãy kiểm tra phần Installation trong README của nó; hầu hết tác giả đã thêm hỗ trợ SPM trong các phiên bản mới nhất.
Câu hỏi thường gặp
SPM được tích hợp trong trình biên dịch Swift và Xcode, không yêu cầu cài đặt qua gem hay Homebrew. CocoaPods sử dụng kho lưu trữ Specs tập trung và tạo không gian làm việc riêng. Carthage hoạt động qua framework mà không tích hợp với dự án. SPM là trình quản lý duy nhất được tích hợp ở cấp trình biên dịch: các phụ thuộc được giải quyết, lưu trữ và xây dựng song song với mã chính.
Có, SPM hỗ trợ dự án hỗn hợp Swift + Objective-C. Các tệp ObjC trong gói SPM tự động được đưa vào Umbrella Header nếu có modulemap chính xác. Tuy nhiên, SPM không hỗ trợ thư viện ObjC tĩnh không có bản đồ mô-đun. Nên kết nối thư viện ObjC qua SPM chỉ khi chúng cung cấp modulemap hoặc được viết bằng C thuần.
SPM sử dụng phiên bản ngữ nghĩa (SemVer). Nếu gói A yêu cầu Alamofire 5.8+ và gói B yêu cầu Alamofire 5.9+, SPM sẽ chọn phiên bản 5.9.x đáp ứng cả hai. Nếu xung đột không thể giải quyết (một gói yêu cầu 5.x, gói khác yêu cầu 6.x), SPM sẽ báo lỗi. Trong trường hợp đó, bạn cần cập nhật một trong các gói hoặc chuyển phụ thuộc sang phiên bản tương thích với cả hai yêu cầu.
Trên macOS: ~Library/Caches/org.swift.swiftpm/ và ~/Library/Developer/Xcode/DerivedData/. Trên Linux: ~cache/swiftpm/. Trong quá trình xây dựng, SPM lưu trữ mã nguồn và tệp đối tượng đã biên dịch. Để xóa hoàn toàn bộ nhớ đệm, chạy swift package reset — lệnh này xóa bộ nhớ đệm phụ thuộc và DerivedData cho dự án hiện tại.
Có, từ Swift 5.2 SPM hỗ trợ mục tiêu nhị phân (binary targets). Thư viện mã nguồn đóng được phân phối dưới dạng XCFramework và đường dẫn đến .xcframework được chỉ định trong Package.swift. Mã nguồn không được tiết lộ. Mục tiêu nhị phân được chỉ định qua .binaryTarget(name: "PrivateSDK", path: "Sources/PrivateSDK.xcframework"). Điều này cho phép kết nối SDK thương mại mà không vi phạm thỏa thuận cấp phép.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm