Swift Package Manager 2026: การสร้าง การเผยแพร่ และคำถามสัมภาษณ์งาน
เชี่ยวชาญ Swift Package Manager ด้วยบทเรียนที่ครอบคลุมนี้ เรียนรู้การสร้าง package จัดการ dependency เผยแพร่ library และเตรียมตัวสำหรับการสัมภาษณ์ iOS

Swift Package Manager (SPM) จัดการ dependency และการแจกจ่าย package สำหรับโปรเจกต์ Swift ต่างจาก CocoaPods หรือ Carthage ตรงที่ SPM รวมเข้ากับ Xcode และ Swift toolchain โดยตรง ไม่ต้องติดตั้งเครื่องมือภายนอก Swift 6.2 และ 6.3 ได้นำเสนอการตั้งค่าความปลอดภัยหน่วยความจำแบบเข้มงวด การแยก actor เริ่มต้น และระบบ Swift Build ใหม่ ทำให้ SPM เป็นตัวเลือกหลักสำหรับการพัฒนา iOS สมัยใหม่
Swift Package Manager แก้ไข dependency ดาวน์โหลดซอร์สโค้ด คอมไพล์โมดูล และลิงก์เข้ากับ binary สุดท้าย ไฟล์ manifest Package.swift เพียงไฟล์เดียวกำหนดทุกอย่าง: dependency, target, product และการตั้งค่า build
โครงสร้างและไวยากรณ์ของ Manifest Package.swift
ทุก Swift package เริ่มต้นด้วยไฟล์ Package.swift ที่ root ของ repository manifest นี้ใช้โค้ด Swift เพื่อประกาศโครงสร้าง package, dependency และการกำหนดค่า build
import PackageDescription
let package = Package(
name: "NetworkKit",
platforms: [
.iOS(.v15),
.macOS(.v12)
],
products: [
.library(
name: "NetworkKit",
targets: ["NetworkKit"]
)
],
dependencies: [
.package(
url: "https://github.com/Alamofire/Alamofire.git",
from: "5.9.0"
)
],
targets: [
.target(
name: "NetworkKit",
dependencies: ["Alamofire"]
),
.testTarget(
name: "NetworkKitTests",
dependencies: ["NetworkKit"]
)
]
)array platforms ระบุเป้าหมายการ deploy ขั้นต่ำ ส่วน products กำหนดสิ่งที่ package อื่นสามารถ import ได้ ส่วน targets แสดงรายการหน่วยคอมไพล์พร้อม dependency ที่เกี่ยวข้อง
การสร้าง Swift Package จาก Command Line
คำสั่ง swift package init สร้างโครงร่าง package ใหม่พร้อมโครงสร้างไดเรกทอรีมาตรฐาน flag --type กำหนดว่า package จะสร้าง library หรือ executable
# สร้าง library package
mkdir NetworkKit && cd NetworkKit
swift package init --type=library
# โครงสร้างที่สร้างขึ้น:
# NetworkKit/
# ├── Package.swift
# ├── Sources/
# │ └── NetworkKit/
# │ └── NetworkKit.swift
# └── Tests/
# └── NetworkKitTests/
# └── NetworkKitTests.swiftรัน swift build เพื่อคอมไพล์ package รัน swift test เพื่อรัน test suite ทั้งสองคำสั่งใช้การกำหนดค่าจาก Package.swift โดยไม่ต้องตั้งค่าเพิ่มเติม
ข้อกำหนดเวอร์ชัน Dependency และการแก้ไข
SPM รองรับกลยุทธ์การระบุเวอร์ชันสามแบบ: เวอร์ชันที่แน่นอน ช่วงเวอร์ชัน และการอ้างอิง branch หรือ commit การเลือกนี้ส่งผลต่อความสามารถในการทำซ้ำและความยืดหยุ่น
dependencies: [
// Semantic versioning: 5.9.0 ถึง major ถัดไป
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.9.0"),
// เวอร์ชันที่แน่นอน: ล็อกที่ 5.9.1
.package(url: "https://github.com/Alamofire/Alamofire.git", exact: "5.9.1"),
// ช่วงเวอร์ชัน: 5.8.0 ถึง 5.9.9
.package(url: "https://github.com/Alamofire/Alamofire.git", "5.8.0".."5.9.9"),
// การอ้างอิง branch: สำหรับการพัฒนา
.package(url: "https://github.com/Alamofire/Alamofire.git", branch: "main"),
// การอ้างอิง commit: ปักหมุดที่ commit เฉพาะ
.package(url: "https://github.com/Alamofire/Alamofire.git", revision: "abc123")
]ไฟล์ Package.resolved บันทึกเวอร์ชันที่แน่นอนที่แก้ไขได้ในระหว่างการแก้ไข dependency ไฟล์นี้ควร commit ไปยัง version control เพื่อให้ build สามารถทำซ้ำได้ในทุกสมาชิกทีมและระบบ CI
การอ้างอิง branch และ commit ข้าม semantic versioning การใช้ branch: "main" ในแอปโปรดักชันหมายความว่า push ใด ๆ ไปยัง main สามารถทำให้ build เสียได้ สงวนการอ้างอิง branch ไว้สำหรับการพัฒนาที่กำลังดำเนินการกับฟีเจอร์ที่ยังไม่ปล่อย
การเพิ่ม Package ในโปรเจกต์ Xcode
Xcode รวม SPM ผ่านเมนู File นำทางไปที่ File จากนั้น Add Package Dependencies ใส่ URL repository เลือกกฎเวอร์ชัน และเลือก target ที่จะลิงก์ dependency
import UIKit
import Alamofire // พร้อมใช้งานหลังจากเพิ่มผ่าน Xcode
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
AF.request("https://api.example.com/health").response { response in
print(response.result)
}
return true
}
}Xcode เก็บการอ้างอิง package ในไฟล์ .xcodeproj และเวอร์ชันที่แก้ไขแล้วใน Package.resolved ที่ root โปรเจกต์ โฟลเดอร์ Derived Data แคช package ที่ดาวน์โหลด
การพัฒนา Package ในเครื่องด้วย Path Dependency
ในระหว่างการพัฒนา การชี้ไปที่ path ในเครื่องแทน URL ระยะไกลช่วยให้สามารถทำซ้ำได้อย่างรวดเร็วโดยไม่ต้องเผยแพร่เวอร์ชันกลาง เทคนิคนี้เหมาะกับการตั้งค่า monorepo และการพัฒนาฟีเจอร์
dependencies: [
// Path ในเครื่องสำหรับการพัฒนา
.package(path: "../NetworkKit"),
// URL ระยะไกลสำหรับ release
// .package(url: "https://github.com/company/NetworkKit.git", from: "1.0.0")
]สลับระหว่าง dependency ในเครื่องและระยะไกลโดย comment บรรทัดที่ไม่ใช้งาน Xcode และ Swift CLI แก้ไข path dependency เทียบกับ consuming package
พร้อมที่จะพิชิตการสัมภาษณ์ iOS แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
การเผยแพร่ Swift Package ไปยัง GitHub
การเผยแพร่ต้องมี Git repository พร้อม tag เวอร์ชันความหมาย SPM ถือว่า Git tag เป็นเวอร์ชัน release Swift Package Index จัดทำดัชนี package สาธารณะโดยอัตโนมัติ
# เริ่มต้น repository และ push
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/username/NetworkKit.git
git push -u origin main
# สร้าง tag เวอร์ชัน
git tag 1.0.0
git push origin 1.0.0
# Package อื่นสามารถขึ้นอยู่กับ:
# .package(url: "https://github.com/username/NetworkKit.git", from: "1.0.0")ปฏิบัติตามข้อตกลง semantic versioning: เพิ่ม major สำหรับ breaking changes, minor สำหรับฟีเจอร์ใหม่ และ patch สำหรับแก้ไขบั๊ก SPM สันนิษฐานว่าปฏิบัติตามกฎเหล่านี้เมื่อแก้ไขข้อกำหนดเวอร์ชัน from:
การตั้งค่า Package Swift 6.2 และ 6.3
Swift 6.2 แนะนำการกำหนดค่าโหมดภาษาต่อ target และการตั้งค่าความปลอดภัยหน่วยความจำแบบเข้มงวด Swift 6.3 เพิ่มการรวม Swift Build เป็นระบบ build แบบ opt-in
import PackageDescription
let package = Package(
name: "SafeNetworkKit",
platforms: [.iOS(.v17)],
products: [
.library(name: "SafeNetworkKit", targets: ["SafeNetworkKit"])
],
targets: [
.target(
name: "SafeNetworkKit",
swiftSettings: [
// เปิดใช้โหมดภาษา Swift 6 สำหรับ target นี้เท่านั้น
.swiftLanguageMode(.v6),
// เปิดใช้การตรวจสอบความปลอดภัยหน่วยความจำแบบเข้มงวด (SE-0458)
.enableExperimentalFeature("StrictMemorySafety"),
// ตั้งค่าการแยก actor เริ่มต้น (SE-0466)
.enableExperimentalFeature("GlobalActorIsolation")
]
)
]
)การตั้งค่า swiftLanguageMode อนุญาตให้นำฟีเจอร์ Swift 6 มาใช้ทีละน้อยในทั้ง codebase Target ต่าง ๆ สามารถใช้โหมดภาษาที่แตกต่างกันภายใน package เดียวกัน
การสร้าง SBOM สำหรับการปฏิบัติตามความปลอดภัย
SE-0509 เพิ่มการสร้าง Software Bill of Materials แบบ native ใน Swift 6.3 เอกสาร SBOM แสดงรายการ dependency ทั้งหมดและเวอร์ชันสำหรับการตรวจสอบความปลอดภัยและการปฏิบัติตามกฎระเบียบ
# สร้าง SBOM ในรูปแบบ CycloneDX
swift build --sbom-spec cyclonedx
# สร้าง SBOM โดยใช้ subcommand เฉพาะ
swift package generate-sbom --format spdx
# ผลลัพธ์รวม:
# - ชื่อและเวอร์ชัน package
# - dependency แบบ transitive ทั้งหมด
# - ข้อมูลลิขสิทธิ์
# - URL repository ต้นทางสภาพแวดล้อมองค์กรและสัญญาภาครัฐต้องการเอกสาร SBOM มากขึ้น รูปแบบ CycloneDX และ SPDX รวมกับเครื่องมือสแกนช่องโหว่มาตรฐาน
Binary Target และการแจกจ่าย XCFramework
Binary target อนุญาตให้แจกจ่าย framework ที่คอมไพล์แล้วแทนซอร์สโค้ด XCFramework รวม binary สำหรับหลายแพลตฟอร์มและสถาปัตยกรรม
import PackageDescription
let package = Package(
name: "AnalyticsSDK",
platforms: [.iOS(.v14)],
products: [
.library(name: "AnalyticsSDK", targets: ["AnalyticsSDK"])
],
targets: [
.binaryTarget(
name: "AnalyticsSDK",
url: "https://releases.example.com/AnalyticsSDK-2.0.0.xcframework.zip",
checksum: "abc123def456..."
)
]
)
// สร้าง checksum:
// swift package compute-checksum AnalyticsSDK-2.0.0.xcframework.zipBinary target ลดเวลา build สำหรับผู้ใช้และปกป้องการใช้งานที่เป็นกรรมสิทธิ์ Checksum รับรองความสมบูรณ์ของการดาวน์โหลด
คำถามสัมภาษณ์ทั่วไปเกี่ยวกับ Swift Package Manager
การสัมภาษณ์ทางเทคนิคสำหรับตำแหน่ง iOS มักรวมคำถามเกี่ยวกับ SPM คำถามเหล่านี้มีตั้งแต่การใช้งานพื้นฐานไปจนถึงการตัดสินใจด้านสถาปัตยกรรม
Q: SPM แตกต่างจาก CocoaPods และ Carthage อย่างไร?
SPM รวมเข้ากับ Xcode และ Swift toolchain โดยไม่ต้องใช้เครื่องมือภายนอก CocoaPods ใช้ repository spec ส่วนกลางและแก้ไขโครงสร้าง workspace ของ Xcode Carthage build framework ในขั้นตอนแยกต่างหากโดยไม่มีการรวม Xcode SPM แก้ไข dependency และ build ในกระบวนการรวมเดียว เอกสาร Apple เกี่ยวกับ Swift packages ครอบคลุมเครื่องมืออย่างเป็นทางการ
Q: เกิดอะไรขึ้นในระหว่างการแก้ไข dependency?
SPM อ่าน manifest Package.swift แบบ recursive สร้างกราฟ dependency จากนั้นใช้ข้อจำกัดเวอร์ชันเพื่อหาเวอร์ชันที่เข้ากันได้สำหรับทุก package Resolver เขียนเวอร์ชันที่แน่นอนไปยัง Package.resolved ความขัดแย้งเกิดขึ้นเมื่อ package สองตัวต้องการเวอร์ชันที่เข้ากันไม่ได้ของ dependency ที่ใช้ร่วมกัน
Q: จัดการกับความขัดแย้ง diamond dependency อย่างไร?
เมื่อ package A และ B ทั้งคู่ขึ้นอยู่กับ package C ที่มีข้อกำหนดเวอร์ชันที่เข้ากันไม่ได้ การแก้ไข SPM ล้มเหลว วิธีแก้ปัญหารวมถึง: อัปเดต consumer หนึ่งตัวให้รองรับช่วงเวอร์ชันที่กว้างขึ้น fork package ที่จำกัด หรือใช้ module aliasing หากความขัดแย้งระหว่าง package ต่างกันที่มีชื่อโมดูลเหมือนกัน
// Module aliasing สำหรับความขัดแย้งของชื่อ
.target(
name: "MyApp",
dependencies: [
.product(name: "Logging", package: "swift-log", moduleAliases: ["Logging": "SwiftLogging"])
]
)Q: เมื่อใดควรใช้ binary target แทนการแจกจ่ายซอร์ส?
Binary target เหมาะกับ SDK ที่เป็นกรรมสิทธิ์ที่การเปิดเผยซอร์สไม่เป็นที่ยอมรับ dependency ขนาดใหญ่ที่เวลาคอมไพล์ส่งผลต่อประสิทธิภาพของนักพัฒนา และ library จากผู้ขายที่คอมไพล์ล่วงหน้า การแจกจ่ายซอร์สยังคงเป็นที่นิยมสำหรับโปรเจกต์ open source และ library ภายในที่การ debug เข้าไปใน dependency เพิ่มคุณค่า
การย้ายจาก CocoaPods ไปยัง Swift Package Manager
การย้ายต้องแทนที่รายการ Podfile ด้วยการอ้างอิง SPM package ไม่ใช่ทุก CocoaPods จะมี SPM เทียบเท่า ดังนั้นควรตรวจสอบความพร้อมใช้งานที่ Swift Package Index ก่อนเริ่ม
# ก่อน: Podfile
pod 'Alamofire', '~> 5.9'
pod 'SwiftyJSON', '~> 5.0'
pod 'Kingfisher', '~> 7.0'// หลัง: Package dependency ใน Xcode
// File > Add Package Dependencies สำหรับแต่ละตัว:
// https://github.com/Alamofire/Alamofire.git from 5.9.0
// https://github.com/SwiftyJSON/SwiftyJSON.git from 5.0.0
// https://github.com/onevcat/Kingfisher.git from 7.0.0ลบ Podfile, Podfile.lock และไดเรกทอรี Pods หลังการย้าย รัน pod deintegrate เพื่อลบการแก้ไข workspace ของ CocoaPods คำสั่ง import ในไฟล์ Swift ยังคงไม่เปลี่ยนแปลง
แนวปฏิบัติที่ดีที่สุดของ SPM สำหรับแอปโปรดักชัน
- ปักหมุดเวอร์ชัน major ด้วย
from:เพื่อความเสถียรในขณะที่รับอัปเดต minor และ patch - Commit
Package.resolvedเพื่อรับรองว่า build สามารถทำซ้ำได้ในทีม - ใช้ package ในเครื่องในระหว่างการพัฒนาที่กำลังดำเนินการ เปลี่ยนเป็น URL ระยะไกลก่อน merge
- แยก dependency เฉพาะ test โดยใช้
testTargetเพื่อหลีกเลี่ยงการรวมใน release build - บันทึกเวอร์ชัน Swift tools ขั้นต่ำที่ด้านบนของ manifest:
// swift-tools-version: 5.10 - ตรวจสอบ build ของ package บน CI ก่อนสร้าง tag release
comment // swift-tools-version: ต้องปรากฏที่บรรทัดแรกของ Package.swift ซึ่งกำหนดว่าเวอร์ชัน API ของ PackageDescription ใดที่ manifest ใช้ Swift 6.0 เพิ่มความยืดหยุ่นในการอนุญาตให้ comment นี้อยู่ในบรรทัดถัดไปสำหรับ header ลิขสิทธิ์
ประเด็นสำคัญสำหรับนักพัฒนา iOS ที่ใช้ SPM
- SPM รวมเข้ากับ Xcode แบบ native ขจัดค่าใช้จ่ายในการตั้งค่า CocoaPods และ Carthage
- manifest
Package.swiftใช้โค้ด Swift ช่วยให้การประกาศ dependency ถูกตรวจสอบประเภท - ข้อกำหนดเวอร์ชันรองรับช่วง semantic versioning เวอร์ชันที่แน่นอน และการอ้างอิง branch
Package.resolvedล็อกเวอร์ชัน dependency สำหรับ build ที่ทำซ้ำได้- Swift 6.2 เพิ่มโหมดภาษาต่อ target และการตั้งค่าความปลอดภัยหน่วยความจำแบบเข้มงวด
- Swift 6.3 แนะนำการสร้าง SBOM สำหรับเอกสารการปฏิบัติตาม
- Binary target แจกจ่าย XCFramework ที่คอมไพล์ล่วงหน้าเมื่อการแจกจ่ายซอร์สไม่เหมาะสม
- คำถามสัมภาษณ์มุ่งเน้นที่ความขัดแย้งในการแก้ไข กลยุทธ์การย้าย และการแลกเปลี่ยนด้านสถาปัตยกรรมระหว่าง SPM และทางเลือกอื่น
สำหรับการเตรียมสัมภาษณ์ iOS อย่างครอบคลุม ให้ทบทวนโมดูล การจัดการ state SwiftUI และ การเขียนโปรแกรมเชิง protocol บน SharpSkill
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
คุณหาบั๊กใน iOS เจอไหม
โค้ดจริงหนึ่งชิ้น บั๊กที่ซ่อนอยู่หนึ่งจุด วันละหนึ่งครั้ง ลองได้โดยไม่ต้องมีบัญชี

เขียนโดย
Anthony Fillion-Mailletผู้ก่อตั้ง SharpSkill
เป็นนักพัฒนาฟูลสแตกมากว่า 10 ปี ดูแล SharpSkill และรับผิดชอบทุกสิ่งที่เผยแพร่ที่นี่
อัปเดตเมื่อ 20 กันยายน 2569
แชร์
บทความที่เกี่ยวข้อง

CloudKit ร่วมกับ SwiftUI ในปี 2026: รูปแบบการซิงค์ข้อมูลระหว่างอุปกรณ์
คู่มือฉบับสมบูรณ์สำหรับการนำการซิงค์ CloudKit ร่วมกับ SwiftUI ไปใช้: CKSyncEngine การผสาน SwiftData การแก้ไขความขัดแย้ง และแนวปฏิบัติที่ดีที่สุดสำหรับ iOS 2026

Combine vs async/await ใน Swift: รูปแบบการย้ายระบบแบบค่อยเป็นค่อยไป
คู่มือฉบับสมบูรณ์สำหรับการย้ายจาก Combine ไปยัง async/await ใน Swift: กลยุทธ์แบบค่อยเป็นค่อยไป รูปแบบการเชื่อมโยง และการอยู่ร่วมกันของกระบวนทัศน์ในโค้ดเบส iOS

คำถามสัมภาษณ์การเข้าถึง iOS ในปี 2026: VoiceOver และ Dynamic Type
เตรียมตัวสัมภาษณ์ iOS ด้วยคำถามสำคัญเรื่องการเข้าถึง: VoiceOver, Dynamic Type, traits เชิงความหมาย และการตรวจสอบ.