Trang chủ

Chuyển build settings của bạn vào các file xcconfig

Build settings chỉnh trong giao diện Xcode sống trong project.pbxproj — một file được sinh tự động, không đọc nổi, không review nổi, và đẻ ra những xung đột merge tệ nhất trong nghề iOS.

File .xcconfig là văn bản thuần, diff sạch sẽ, và review được. Chuyển sang chúng tốn một buổi chiều.

Những điều cơ bản

// Base.xcconfig
PRODUCT_BUNDLE_IDENTIFIER = com.example.app
MARKETING_VERSION = 2.4.0
SWIFT_VERSION = 6.0
IPHONEOS_DEPLOYMENT_TARGET = 17.0
DEVELOPMENT_TEAM = ABC123XYZ

Gán nó trong Project → Info → Configurations, theo từng cấu hình. Thiết lập trong file dự án vẫn ghi đè lên xcconfig, nên quy trình chuyển đổi là: chuyển một thiết lập vào file, rồi xóa nó khỏi giao diện. Chừng nào bạn chưa xóa thì file vẫn đang bị lờ đi.

Mẹo

Thiết lập sẽ hiện đậm trong bảng build settings của Xcode khi nó bị ghi đè ở tầng đó. Sau khi chuyển một thứ sang xcconfig, hãy kiểm tra rằng bảng đó hiện nó không đậm và ghi nguồn là từ file — đó là bằng chứng cuộc chuyển đổi thật sự có hiệu lực.

Phân tầng bằng #include

Cấu trúc chạy được:

Config/
  Base.xcconfig          — mọi thứ dùng chung
  Debug.xcconfig         — #include "Base.xcconfig"
  Release.xcconfig       — #include "Base.xcconfig"
  Staging.xcconfig       — #include "Release.xcconfig"
// Debug.xcconfig
#include "Base.xcconfig"

SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG
SWIFT_OPTIMIZATION_LEVEL = -Onone
GCC_OPTIMIZATION_LEVEL = 0
ONLY_ACTIVE_ARCH = YES
DEBUG_INFORMATION_FORMAT = dwarf
API_BASE_URL = https:/$()/api-dev.example.com
// Release.xcconfig
#include "Base.xcconfig"

SWIFT_OPTIMIZATION_LEVEL = -O
SWIFT_COMPILATION_MODE = wholemodule
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
VALIDATE_PRODUCT = YES
API_BASE_URL = https:/$()/api.example.com

Cái $() kia không phải lỗi gõ. // bắt đầu một dòng chú thích trong xcconfig, nên một URL sẽ bị cắt cụt ngay sau https:. $() là một phép thay thế biến rỗng, nó chẻ đôi cặp // mà không xuất hiện trong đầu ra. Đó là thứ gây rối trí nhất của định dạng này và nó lấy của ai cũng một tiếng đúng một lần.

Lấy giá trị từ trong code

Thêm vào Info.plist:

<key>APIBaseURL</key>
<string>$(API_BASE_URL)</string>

Rồi đọc nó:

enum Configuration {
    static let apiBaseURL: URL = {
        guard let string = Bundle.main.object(forInfoDictionaryKey: "APIBaseURL") as? String,
              let url = URL(string: string) else {
            fatalError("Thiếu APIBaseURL trong Info.plist")
        }
        return url
    }()
}

fatalError ở đây là đúng — một cấu hình build bị thiếu là một vấn đề lúc build, và sập lúc khởi động trong môi trường phát triển thì hơn một URL nil lặng lẽ vô hiệu hóa phần mạng trong môi trường thật.

Danh tính ứng dụng theo từng cấu hình

Đây là khoản lợi thực dụng mà người ta để ý đầu tiên — cài Debug, Staging và Release cạnh nhau trên cùng một máy:

// Debug.xcconfig
PRODUCT_BUNDLE_IDENTIFIER = com.example.app.debug
PRODUCT_NAME = App (Debug)
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Debug

Khác định danh gói, khác tên, khác icon. Người kiểm thử thôi phải hỏi họ đang cài bản nào.

Thứ xcconfig không làm được

Hai giới hạn thật đáng biết trước khi cam kết với cuộc chuyển đổi.

Không có câu điều kiện ngoài các bổ ngữ có sẵn. Bạn viết được SETTING[sdk=iphonesimulator*] = value và [arch=arm64], nhưng không có if. Bất cứ thứ gì thật sự có điều kiện đều cần một script trong build phase.

Nó không bao phủ mọi thứ trong file dự án. Build phase, phụ thuộc giữa các target, thiết lập scheme và các capability vẫn nằm trong project.pbxproj. xcconfig thu hẹp bề mặt xung đột; nó không xóa bỏ được nó.

Giữ bí mật ở ngoài

Một file xcconfig được commit, nên một khóa API không thuộc về nó. Khuôn mẫu là:

// Base.xcconfig
#include? "Secrets.xcconfig"

#include? — có dấu hỏi — không báo lỗi nếu file vắng mặt. Secrets.xcconfig nằm trong .gitignore, kèm một file Secrets.xcconfig.template được commit để cho biết cần những khóa nào. CI tự cung cấp bản của nó từ các biến môi trường.

Vì sao nó đáng một buổi chiều

Vấn đề review build settings là có thật. Một thay đổi trong project.pbxproj ở một pull request thì về cơ bản là không review nổi — cái diff là XML do máy sinh, và tôi đã hai lần duyệt những thay đổi hóa ra đã chỉnh một thiết lập ký hay một phiên bản hệ điều hành tối thiểu mà chẳng ai nhận ra.

Một diff của xcconfig thì đọc như một file văn bản, vì nó đúng là như vậy:

-IPHONEOS_DEPLOYMENT_TARGET = 17.0
+IPHONEOS_DEPLOYMENT_TARGET = 18.0

Đó là một thay đổi ai cũng thấy được và chất vấn được. Điều đó, hơn cả chuyện xung đột merge, mới là lập luận chính.