SPM: nó là gì, Swift Package Manager và Package.swift

Tác giả: IT Sectr Đã đăng: 2026-02-13 Thời gian đọc: 11 phút

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 được tích hợp trong trình biên dịch Swift, không yêu cầu cài đặt riêng; hoạt động trên iOS, macOS, Linux và các nền tảng máy chủ.
  • Package.swift là tệp kê khai mô tả tên gói, nền tảng, phụ thuộc và mô-đun mục tiêu (targets) ở định dạng khai báo.
  • SPM giải quyết phụ thuộc bằng phiên bản ngữ nghĩa (SemVer), lưu trữ mã nguồn và xây dựng gói song song để tăng tốc độ.
  • Lệnh: swift package init (tạo gói), swift package update (cập nhật phụ thuộc), swift build (xây dựng), swift test (chạy kiểm thử).
  • Di chuyển từ CocoaPods/Carthage sang SPM được thực hiện qua Xcode: File → Add Package Dependency, sau đó xóa podfile và Cartfile.

SPM là gì?

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.

Swift Package Manager hoạt động như thế nào

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:

  1. Sao chép — SPM tải kho Git của phụ thuộc từ URL được chỉ định.
  2. Giải quyết phiên bản — nó phân tích thẻ SemVer (ví dụ 2.1.3) và chọn phiên bản phù hợp trong phạm vi được chỉ định.
  3. Giải quyết bắc cầu — nó kiểm tra phụ thuộc của phụ thuộc và xây dựng đồ thị phiên bản không xung đột.
  4. Lưu trữ — nó lưu mã nguồn đã tải trong ~Library/Caches/org.swift.swiftpm/.
  5. Biên dịch — nó xây dựng tất cả mục tiêu của gói với các cờ của dự án chính.

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 — tệp kê khai dự á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
// 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:

swift
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 frameworklinkerSettings, 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.

Các lệnh SPM cơ bản

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).

bash
# 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 riêng của bạn

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.

Bước 1: Khởi tạo

bash
mkdir MyNetworkKit
cd MyNetworkKit
swift package init --type library

Bước 2: Cấu trúc thư mục

Lệnh swift package init tạo cấu trúc sau:

text
MyNetworkKit/
├── Package.swift
├── README.md
├── Sources/
│   └── MyNetworkKit/
│       └── MyNetworkKit.swift
└── Tests/
    └── MyNetworkKitTests/
        └── MyNetworkKitTests.swift

SPM tự động quét các thư mục Sources/Tests/: mỗi thư mục con trong Sources tương ứng với một mục tiêu (target).

Bước 3: Chỉnh sửa Package.swift

Hãy thêm phụ thuộc và cấu hình nền tảng mục tiêu:

swift
// 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"]
        ),
    ]
)

Bước 4: Viết mã

swift
// 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
    }
}

Bước 5: Xuất bản

Đẩy gói lên kho Git và tạo thẻ SemVer:

bash
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").

Ví dụ sử dụng SPM

Ví dụ 1: Thêm Alamofire cho yêu cầu mạng

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.

swift
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)")
            }
        }
}

Ví dụ 2: Swinject — tiêm phụ thuộc

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").

swift
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()

Ví dụ 3: Swift-log cho ghi nhật ký có cấu trúc

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).

swift
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.

Di chuyển từ CocoaPods và Carthage

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.

CocoaPods → SPM

  1. Trong Xcode: File → Add Package Dependency, nhập URL gói.
  2. Chọn phiên bản và thêm gói vào các mục tiêu cần thiết.
  3. Sau khi thêm tất cả phụ thuộc qua SPM, xóa các dòng khỏi Podfile.
  4. Xóa .xcworkspace, mở .xcodeproj và thực hiện Clean Build Folder.

Carthage → SPM

  1. Thêm gói qua Xcode File → Add Package Dependency.
  2. Xóa phụ thuộc khỏi Cartfile.
  3. Xóa tập lệnh xây dựng Carthage khỏi Build Phases.
  4. Xóa bộ nhớ đệm: 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 khác CocoaPods và Carthage như thế nào?

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ó thể sử dụng SPM cho dự án Objective-C không?

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 giải quyết xung đột phiên bản như thế nào?

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.

Các gói SPM đã tải được lưu ở đâu?

Trên macOS: ~Library/Caches/org.swift.swiftpm/~/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.

SPM có hỗ trợ thư viện mã nguồn đóng (độc quyền) không?

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

  • SPM (Swift Package Manager) là trình quản lý gói tích hợp trong Swift, không yêu cầu cài đặt riêng và được tích hợp với Xcode và trình biên dịch.
  • Package.swift là tệp kê khai khai báo được viết bằng Swift mô tả tên gói, nền tảng, phụ thuộc, sản phẩm và mục tiêu xây dựng.
  • SPM sử dụng kho Git làm nguồn gói và giải quyết phiên bản qua SemVer, lưu trữ mã nguồn để xây dựng sau nhanh hơn.
  • Lệnh chính: swift package init (tạo gói), swift build (xây dựng), swift test (kiểm thử), swift package update (cập nhật phụ thuộc).
  • Gói tùy chỉnh được tạo qua swift package init, xuất bản lên Git và có sẵn cho dự án khác qua URL với thẻ SemVer.
  • Di chuyển từ CocoaPods/Carthage sang SPM an toàn: phụ thuộc có thể cùng tồn tại, di chuyển qua File → Add Package Dependency trong Xcode.
  • SPM là công cụ quản lý phụ thuộc tiêu chuẩn trong hệ sinh thái Swift, được 67% nhà phát triển iOS sử dụng (Swift.org Developer Survey, 2024).

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.

Thảo luận dự án

Đọc thêm