Scheme trong Xcode là một cấu hình xác định cách xây dựng, kiểm thử, phân tích hiệu năng và lưu trữ một ứng dụng cho iOS, macOS, watchOS hoặc tvOS. Mỗi Scheme chứa một tập hợp các hành động (Build, Run, Test, Profile, Analyze, Archive) với các tham số, đối số và biến môi trường riêng. Theo Apple Developer Documentation, 2025, Scheme là công cụ chính để quản lý cấu hình build trong Xcode, thay thế cho việc chuyển đổi tham số thủ công. Xcode tự động tạo một scheme cho mỗi target khi mở dự án lần đầu.
Điểm chính
Scheme trong Xcode là một tệp XML (với phần mở rộng .xcscheme) mô tả chuỗi các hành động và tham số của chúng để build và phân tích ứng dụng. Mỗi Scheme gắn với một hoặc nhiều target và xác định với cấu hình nào (Debug, Release, AdHoc) để thực hiện mỗi hành động. Scheme tương đương với Build Variant trong Android, nhưng có cấu trúc linh hoạt hơn: một scheme có thể chứa các target khác nhau cho các hành động khác nhau.
Xcode tự động tạo một scheme cho mỗi target khi mở dự án lần đầu. Tên scheme mặc định trùng với tên target. Nếu dự án có target test, Xcode tự động thêm nó vào hành động Test của scheme target chính. Đối với dự án có nhiều target (ứng dụng chính + watchOS + extension), Xcode tạo một scheme riêng cho từng target, nhưng cũng có thể tạo một scheme duy nhất build tất cả các target cùng lúc.
Scheme được lưu trong thư mục xcshareddata/xcschemes/ (đối với shared) hoặc xcuserdata/<user>/xcschemes/ (đối với private). Scheme shared được đưa vào Git và cả nhóm sử dụng. Scheme private được lưu cục bộ và không được đồng bộ. Tệp .xcscheme có định dạng XML với phần tử gốc <Scheme>. Bên trong là các khối cho mỗi hành động: BuildAction, TestAction, LaunchAction, ProfileAction, AnalyzeAction, ArchiveAction.
.xcscheme là một tệp XML có thể chỉnh sửa thủ công hoặc qua Xcode. Các phần tử chính: <BuildAction> (danh sách target cần build), <TestAction> (liên kết tới target test), <LaunchAction> (cấu hình khởi chạy), <ProfileAction>, <AnalyzeAction>, <ArchiveAction>. Mỗi khối chứa thuộc tính buildConfiguration, xác định dùng cấu hình nào (Debug/Release) cho hành động đó.
Scheme gồm sáu hành động, mỗi hành động có thể cấu hình độc lập. Hành động Build xác định target nào được build và theo thứ tự nào. Hành động Run xác định cách ứng dụng khởi chạy: với đối số, biến môi trường và cấu hình nào. Hành động Test xác định bài test nào được chạy và tùy chọn code coverage nào được bật. Hành động Profile khởi chạy với công cụ Instruments để phân tích hiệu năng. Hành động Analyze thực hiện phân tích mã tĩnh với Clang Static Analyzer. Hành động Archive build để phát hành trên App Store hoặc phân phối AdHoc.
Với mỗi hành động có thể đặt một build configuration riêng. Thông thường dùng Debug cho Run và Test, Release cho Archive. Build configuration xác định tập hợp các cờ trình biên dịch, tối ưu hóa và thông tin gỡ lỗi. Xcode cung cấp hai cấu hình chuẩn: Debug (không tối ưu hóa, có ký hiệu gỡ lỗi) và Release (có tối ưu hóa, không có thông tin gỡ lỗi). Nhà phát triển có thể thêm cấu hình tùy chỉnh qua project.xcconfig.
Hành động Archive đặc biệt quan trọng — nó tạo ra một tệp .xcarchive, sau đó được xuất thành .ipa cho App Store hoặc AdHoc. Hành động Archive mặc định dùng cấu hình Release, nhưng có thể chuyển sang AdHoc hoặc Distribution. Trong hành động Archive cũng có cờ revealArchiveInOrganizer — sau khi lưu trữ hoàn tất, Xcode mở Organizer để tiếp tục xử lý tệp lưu trữ.
<!-- Ví dụ .xcscheme cho ứng dụng iOS -->
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ABCD1234"
BuildableName = "MyApp.app"
BlueprintName = "MyApp"
ReferencedContainer = "container:MyApp.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
enableAddressSanitizer = "YES">
</LaunchAction>
</Scheme>
Việc tạo scheme mới được thực hiện qua menu Xcode: Product → Scheme → New Scheme hoặc bằng nút "+" trong bảng Scheme (cạnh nút Run). Khi tạo, bạn chọn target để tạo scheme. Xcode tự động sao chép cài đặt từ scheme hiện có nếu nó được chọn là "duplicate". Scheme mới mặc định được lưu dưới dạng private — để chia sẻ cho nhóm, bạn cần bật Shared trong Manage Schemes.
Cửa sổ Edit Scheme (Product → Scheme → Edit Scheme) chứa sáu tab tương ứng với số hành động. Trên mỗi tab có thể thay đổi build configuration, đối số khởi chạy, biến môi trường và cờ chẩn đoán. Trên tab Run có các tùy chọn: executable (chạy tệp nhị phân nào), wait for executable to be launched (để gỡ lỗi quá trình được khởi chạy), debugger (LLDB hoặc None), launch arguments, environment variables và các tùy chọn mở rộng (Address Sanitizer, Thread Sanitizer, Main Thread Checker, Memory Management).
Để chẩn đoán, Address Sanitizer (ASan) phát hiện truy cập vượt giới hạn, use-after-free và các lỗi bộ nhớ khác trong mã C/C++/ObjC. Thread Sanitizer (TSan) phát hiện điều kiện cạnh tranh dữ liệu (data races) trong mã đa luồng. Undefined Behavior Sanitizer (UBSan) phát hiện hành vi không xác định, như tràn số int có dấu. Các tùy chọn này có trong Edit Scheme → Run → Diagnostics và chỉ hoạt động với bản build Debug. Bật tất cả các sanitizer có thể làm khởi động chậm gấp 2-3 lần, vì vậy nên bật chúng một cách chọn lọc.
Thông lệ phổ biến là tạo các scheme riêng cho từng môi trường: Dev, Staging, Production. Mỗi scheme dùng cùng một Build Configuration (Debug cho Dev, Release cho Production), nhưng đối số khởi chạy khác nhau: -FIRAnalyticsDebugEnabled, -com.apple.CoreData.SQLDebug 1 cho Dev và không có các đối số này cho Production. Đối số khởi chạy được truyền vào UserDefaults (ProcessInfo.processInfo.arguments) và có thể đọc được khi ứng dụng khởi động. Điều này cho phép chuyển đổi URL máy chủ, mức ghi log và tính năng mà không cần thay đổi mã.
Scheme shared được lưu trong <project>.xcworkspace/xcshareddata/xcschemes/ hoặc <project>.xcodeproj/xcshareddata/xcschemes/ và được đưa vào kho Git. Tất cả nhà phát triển trong nhóm đều thấy các scheme này trong Xcode. Scheme shared là cách duy nhất để phân phối scheme trong nhóm. Nếu một nhà phát triển tạo scheme quan trọng (ví dụ "Staging Archive") nhưng không đánh dấu là Shared, phần còn lại của nhóm sẽ không thấy nó, gây ra sự nhầm lẫn: mỗi người sẽ tự tạo scheme của mình với cài đặt riêng.
Scheme private được lưu trong xcuserdata/<user>/xcschemes/ và không được đưa vào Git. Chúng hữu ích cho các cấu hình cá nhân: ví dụ, một scheme có bật tất cả sanitizer cho một nhà phát triển cụ thể. Scheme private không được chứa các cài đặt quan trọng mà việc build dự án phụ thuộc vào — nếu nhà phát triển rời dự án, các scheme private của họ sẽ biến mất. Khuyến nghị: tất cả scheme được dùng trong CI/CD và ít nhất hai nhà phát triển sử dụng nên được đặt là Shared.
Quản lý scheme được thực hiện qua Manage Schemes (Product → Scheme → Manage Schemes). Cửa sổ hiển thị tất cả scheme của dự án, trạng thái của chúng (Shared/Private) và các nút +/− để thêm/xóa. Hộp kiểm Shared chuyển đổi mức hiển thị của scheme cho nhóm. Trong trường hợp xung đột Git (thay đổi .xcscheme bởi hai nhà phát triển), cần giải quyết merge một cách cẩn thận — các tệp XML có thể chứa định danh target khác nhau. Nên thêm .xcscheme vào các tệp bị khóa khi merge (git lfs hoặc .gitattributes).
Arguments (đối số) trong Scheme là các chuỗi được truyền cho ứng dụng khi khởi chạy (ProcessInfo.processInfo.arguments) và các biến môi trường (ProcessInfo.processInfo.environment). Đối số được dùng cho các cờ: -AppleLanguages (ru), -AppleLocale ru_RU để mô phỏng vùng ngôn ngữ Nga, hoặc -FIRDebugEnabled để bật gỡ lỗi Firebase. Biến môi trường được dùng cho cấu hình: API_BASE_URL=http://localhost:3000, LOG_LEVEL=debug.
Để quản lý tính năng (feature flags) trong các môi trường khác nhau, người ta dùng tổ hợp Arguments + Build Configuration. Trong scheme Dev, đối số -FeatureFlagNewOnboarding YES được đặt, còn trong Production — -FeatureFlagNewOnboarding NO (hoặc không có đối số). Trong mã, việc kiểm tra là: UserDefaults.standard.bool(forKey: "FeatureFlagNewOnboarding"). Cách tiếp cận này cho phép bật dần tính năng trên staging mà không cần thay đổi mã và không cần commit giá trị production.
Quan trọng: đối số và biến môi trường của Scheme ghi đè giá trị từ Info.plist. Nếu API_URL được chỉ định trong Info.plist, còn trong Scheme — API_URL=http://localhost cho hành động Run, thì khi khởi chạy từ Xcode sẽ dùng giá trị từ Scheme. Khi khởi chạy trên thiết bị (không phải từ Xcode) — giá trị từ Info.plist. Điều này thuận tiện cho phát triển cục bộ, nhưng cần nhớ rằng biến của Scheme không đi vào bản build — chúng chỉ hoạt động khi khởi chạy qua Xcode.
import Foundation
struct AppEnvironment {
var apiBaseURL: String {
ProcessInfo.processInfo.environment["API_BASE_URL"]
?? Bundle.main.object(forInfoDictionaryKey: "API_BASE_URL") as? String
?? "https://api.production.com"
}
var isDebugMode: Bool {
ProcessInfo.processInfo.arguments.contains("-DebugModeEnabled")
}
var isNewOnboardingEnabled: Bool {
UserDefaults.standard.bool(forKey: "FeatureFlagNewOnboarding")
}
}
// Sử dụng khi khởi động
let env = AppEnvironment()
NetworkConfig.shared.configure(baseURL: env.apiBaseURL)
Trong CI/CD (GitHub Actions, Jenkins, GitLab CI), Scheme được dùng làm đối số chính của lệnh xcodebuild. Ví dụ: xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphoneos archive. Cờ -scheme xác định scheme nào sẽ được dùng. xcodebuild đọc mọi cài đặt từ tệp .xcscheme, bao gồm build configuration, target và thứ tự build. Điều này đảm bảo CI/CD build ứng dụng với các tham số giống như IDE cục bộ.
Đối với CI/CD, scheme shared rất quan trọng. Nếu scheme không phải Shared, xcodebuild sẽ không tìm thấy nó trong kho và bản build sẽ thất bại với lỗi "Scheme not found". Quy tắc: trước khi cấu hình CI/CD, hãy đảm bảo tất cả scheme được dùng đều được đánh dấu Shared. Quy tắc thứ hai: trong CI/CD, không dùng scheme mặc định (Xcode tự động chọn scheme đầu tiên) — luôn truyền tên scheme một cách tường minh qua cờ -scheme.
Để build song song nhiều scheme (ví dụ, ứng dụng và extension watchOS), có thể chạy xcodebuild tuần tự hoặc song song. Các hệ thống CI hiện đại cho phép song song hóa việc build các scheme khác nhau qua ma trận: một job build ứng dụng iOS, job thứ hai build extension watchOS. Điều này giảm tổng thời gian build từ 15 xuống 8 phút với hai agent song song. Cuối cùng, các artifact được gộp thành một tệp .xcarchive duy nhất bằng xcodebuild -exportArchive.
#!/bin/bash — xây dựng CI/CD với xcodebuild
# 1. Dọn dẹp và build
xcodebuild clean archive \
-workspace "MyApp.xcworkspace" \
-scheme "MyApp Production" \
-configuration Release \
-sdk iphoneos \
-archivePath "build/MyApp.xcarchive" \
CODE_SIGN_STYLE="Manual" \
PROVISIONING_PROFILE_SPECIFIER="match AppStore"
# 2. Xuất sang IPA
xcodebuild -exportArchive \
-archivePath "build/MyApp.xcarchive" \
-exportPath "build/ipa" \
-exportOptionsPlist "ExportOptions.plist"
Câu hỏi thường gặp
Thông thường 2-3 scheme là đủ: Development (Debug), Staging (có đối số cho máy chủ test) và Production (Release). Đối với thư viện dạng module — một scheme với cài đặt kiểm thử. Đừng tạo quá nhiều scheme — mỗi scheme mới cần bảo trì.
Build Configuration (Debug/Release) là tập hợp các cờ trình biên dịch được định nghĩa trong .xcconfig. Scheme là tập hợp các hành động, mỗi hành động tham chiếu đến một Build Configuration. Scheme nói "khi khởi chạy hãy dùng Debug", cấu hình xác định "Debug nghĩa là không tối ưu hóa, có ký hiệu".
Đối số đi vào ProcessInfo.processInfo.arguments và UserDefaults (nếu đối số bắt đầu bằng dấu gạch ngang). Biến môi trường đi vào ProcessInfo.processInfo.environment. Trong mã: UserDefaults.standard.bool(forKey: "FeatureFlag") cho các đối số dạng -FeatureFlag YES.
Có, trong Build Action có thể thêm nhiều target. Ví dụ, scheme "App + Watch + Widget" sẽ build cả ba target tuần tự (nếu parallelizeBuildables=NO) hoặc song song (YES). Để lưu trữ ứng dụng, chỉ cần target chính — các target còn lại được build như phụ thuộc.
Swift Package Manager không thay thế scheme — scheme vẫn xác định với cấu hình nào để build các phụ thuộc SPM, chạy những test nào và cách lưu trữ. Các gói SPM có thể có scheme riêng, được tự động nhập vào dự án khi thêm gói.
Tổng kế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