iOS開發(fā)xconfig和script腳本使用詳解
引言
利用Xcode
進行開發(fā)時需要進行很多build setting
的設(shè)置以便能讓項目按照設(shè)置的進行編譯,同時有時候需要在編譯時利用script
腳本進行一些設(shè)置,本文主要介紹xconfig
文件和script
腳本在Xcode
開發(fā)中使用。
Xcode編譯
在使用xconfig
時有幾個關(guān)于Xcode
的概念是需要理解的,這里我進行通俗簡單的說明,同時需要知道Xcode
在編譯的過程中具體幫我們做了那幾件事情。
Xcode target
在實際開發(fā)中一個Xcode
創(chuàng)建的項目是可以有多個taget
的,比如我們創(chuàng)建一個widget
時Xcode
會自動新建一個target
對應(yīng)這個widget
,也可以自己新建,同一個項目有多個target
可以滿足不同的測試場景,比如在前期開發(fā)階段使用一個target
,到UAT
階段使用另外一個target
。一個target
對應(yīng)一個product
,也就是編譯后安裝到手機上的項目,target
定義了生成的唯一 product
, 它將構(gòu)建該product
所需的文件和處理這些文件所需的指令集整合進 build system
中,這些指令以 build setting
和 build phases
的形式存在,我們用xconfig
文件來設(shè)置 build setting
,同時將script
腳本添加到build phases
中。
新建target
Xcode project
Xcode project
是一個倉庫,該倉庫包含了所有的文件,資源和用于生成一個或者多個software products
的信息,它包含一個或者多個targets
,其中的每一個 target
指明了如何生成 products
。project
為其擁有的所有 targets
定義了默認的build settings
,例如project
中默認包含debug
和release
兩種build settings
當(dāng)然,每一個 target
能夠制定其自己的 build settings
,且target
的build settings
會重寫project
的 build settings
。
Xcode scheme
一個project
可以有多個target
,但是當(dāng)前的target
只能有一個,scheme
就是用來確定當(dāng)前的target
的,并制定當(dāng)前的target
使用哪種configuration
。
新建configuration
打開項目編輯欄選擇上面的progect
同時選擇info
欄,可以看到Xcode
默認添加了二個Debug
和Release
的configuration
,點擊做下角的+
號按鈕選擇復(fù)制Debug
或者Release
其中一個configuration
來新建并命名一個自己想取的名字,我這里命名為Mamba
。
Configuration文件的使用
平時手動的在Xcode
中進行項目的一些build setting
設(shè)置還是比較麻煩的,一個是需要在Xcode
中進行搜索,另外一個是不好管理,例如需要在debug
或者release
下進行不同的設(shè)置的話就比較麻煩。利用Configuration
文件來代替手動設(shè)置則更加的方便,直接新建Configuration Setting file
類型文件,如下圖所示:
利用Configuration設(shè)置不同的項目名
Configuration
文件是可以繼承的,一般先建立一個Common Configuration
文件用來作為父類,為此新建一個名為Common
的Configuration
文件,并加入如下代碼:
APP_NAME = TestDemo
然后分別新建名為debug
,Mamba
和release
的Configuration
文件,并加入如下代碼:
- debug
#include "Common.xcconfig" APP_NAME = $(inherited)Debug
- Mamba
#include "Common.xcconfig" APP_NAME = $(inherited)Mamba
- release
```Swift #include "Common.xcconfig" APP_NAME = $(inherited)Release
上面利用#include
進行導(dǎo)入依賴的Configuration
文件,并利用$(inherited)
來引用依賴的Configuration
文件中的變量。
Configuration
文件中的語法一般是SETTING_NAME = VALUE
,具體等式二邊設(shè)置的值可見蘋果官網(wǎng).
設(shè)置Configuration
點擊PROJECT
導(dǎo)航欄并選擇Info
會發(fā)現(xiàn)多了一個上文我們添加的名為Mamba
的Configuration
。
點擊左邊的小三角箭頭展開每個Configuration
后可以設(shè)置項目的project
級別的Configuration File
和target
級別的Configuration File
,當(dāng)然也可以默認不設(shè)置。分別設(shè)置三個Configuration
下的project
級別的Configuration File
為Base
,target
級別的Configuration File
則為對應(yīng)的Configuration File
,如下圖所示:
查看是否設(shè)置成功
點擊TARGETS
導(dǎo)航欄,選擇Build Settings
并選中All
和Levels
滑到最下面可看見APP_NAME
的值設(shè)置如下:
這里需要解釋一下幾個設(shè)置的級別:
Resolved
: 最后生效的值Target
: 顯示在Target
級別生效的值,Target
級別的優(yōu)先級是高于Project
的,并且默認繼承Project
設(shè)置的值。Project
: 顯示在Project
級別生效的值,往常在Xcode
的General
設(shè)置的值就是這一級別的。iOS Default
: 顯示iOS
默認設(shè)置的值。
加上Configuration File
后優(yōu)先級順序從低到高如下:
- Platform defaults
- Project.xcconfig file
- Project file build settings
- Target .xcconfig file
- Target build settings
設(shè)置Info.plist
最后為了通過Configuration File
來控制APP
運行時名字的顯示,需要在Info.plist
中鏈接Bundle display name
屬性(沒有的話需要新增)到我們上面設(shè)置的user-defined setting(APP_NAME)
上,為此修改Info.plist
中Bundle display name
的值為 $(APP_NAME)
。
測試是否生效
在Scheme
頁面分別選擇debug
,release
和mamba
三中不同的Configuration
環(huán)境運行APP
成功的根據(jù)不同的Configtation
設(shè)置不同的項目運行名字。
利用xconfig文件實現(xiàn)OC條件編譯
在開發(fā)中經(jīng)常需要進行條件編譯,在OC
中可以利用pch
文件配合宏來實現(xiàn),例如如下:
#ifdef DEBUG #define BaseURL @"192.168.1.1:8080/appname/api" #define PublicKEY @"QWE3R23WR09WURI220WR3TTY5ET3CR2X" #else #define BaseURL @"http://api.appname.com" #define PublicKEY @"32GDG4575UB5M97O7M2X32RFH53QWT43" #endif
通過在pch
文件中利用條件編譯定義不用的宏來實現(xiàn)項目的動態(tài)切換配置,上述宏定義一般定義在.pch
中,通常.pch
文件中定義的宏都比較雜亂,希望能單獨放在一個獨立的文件中,可以通過新建一個頭文件env.h
, 把上述宏定義放到env.h
中,在需要使用的時候?qū)腩^文件即可,把環(huán)境參數(shù)單獨放在一個獨立的頭文件中,更加簡潔,職能更加專一,也便于維護但是這種做法還不是最好的,因為還需要手動導(dǎo)入頭文件,而且生產(chǎn)環(huán)境參數(shù)和開發(fā)環(huán)境參數(shù)是放在同一個文件中而是不是獨立分開的,要想獨立分開并且使用時又不用導(dǎo)入頭文件可以通過Xcode
中的Configurations Setting Fil(.xcconfig)
來解決,這應(yīng)該是最優(yōu)的實現(xiàn)方式。
xconfig文件的設(shè)置
在上面的Debug.xconfig
和Mamba.xconfig
文件中分別加入如下代碼:
- Debug.xconfig
WEBSERVICE_URL = @"www.baidu.com"
- Mamba.xconfig
WEBSERVICE_URL = @"www.jd.com"
這樣只是自定義了一個Build Setting
變量,不能代碼里像使用宏那樣使用,Xcode
是支持利用GCC_PREPROCESSOR_DEFINITIONS
在定義宏的,在Common.xconfig
文件中加入如下代碼:
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) WEBSERVICE_URL='$(WEBSERVICE_URL)'
在TARGET
導(dǎo)航欄中Preprocessor Macros
即可看見我們定義的宏。
代碼使用
可以在代碼中直接使用定義的宏,當(dāng)切換Configuration
時則會根據(jù).xconfig
文件輸入不同的打印。
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"-----------%@-------------",WEBSERVICE_URL); }
使用#include
語法來包含其他配置文件,如#include "Common.xcconfig"
, 最好是放在文件的最后面,放在文件的開頭也可以。Common.xconfig
中第一個鍵的配置必須有:GCC_PREPROCESSOR_DEFINITIONS = $(inherited)
,沒有Xcode
會報錯,暴露自定義鍵時的語法:宏名='$(key)'
,在代碼或其他地方使用宏名來引用,'$(key)'
:通過key
來指定每個模式下的對應(yīng)的自定義鍵的名字,通常將宏的名字和key
的名字保持一致, 注意 等號前后一定不能有空,Common.xconfig
中第一個key
是GCC_PREPROCESSOR_DEFINITIONS = $(inherited)
后面跟自定義的key
,注意在第一個key
后面跟上自己定義的key
的時候一定不要回車換行,敲一個空格,然后在同一行后面追加就行了,換行會編譯錯誤, 不能換行,不能換行,不能換行!
Swift中條件編譯的實現(xiàn)
在Swift
中是不支持通過GCC_PREPROCESSOR_DEFINITIONS
來定義宏的,但是可以通過定義Custom Flags
進行定義,這里介紹另外一種方法,還是通過.xconfig文件
進行獲取我們需要的宏。前面我們通過info.plist
獲取到了.xconfig
文件中自定義的變量,再次我們同樣通過info.plist
來獲取自定義的變量的值來當(dāng)做宏使用,首先在info.plist
中新建一個WEBSERVICE_URL
變量,并設(shè)置值為'$(WEBSERVICE_URL)'
,由于需要解析info.plist
中的變量,再次封裝一個config.swift
的類用來解析:
import Foundation enum Config { static func stringValue(forKey key: String) -> String { guard let value = Bundle.main.object(forInfoDictionaryKey: key) as? String else { fatalError("Invalid value or undefined key") } return value } }
代碼使用
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() print(Config.stringValue(forKey:"WEBSERVICE_URL")) } }
相比較于OC
版本的是不能直接定義宏,需要通過在info.plist
定義后并通過方法取出值后才能使用,稍微麻煩了一點。
script的使用
上文我們已經(jīng)知道xconfig
文件的使用,其實在編譯之前不只是變量的自定義或者獲取項目的一些默認參數(shù),還可以在獲取這些參數(shù)的基礎(chǔ)上,將這些參數(shù)作為script
腳本的變量來做一些更有意義的事情,Xcode
是支持在編譯之前鏈接script
腳本的。
script的初步認識
腳本一般來說就是可執(zhí)行的二進制文件,下面先制作一個簡單的腳本加深認知(實例代碼采用Swift
),首先新建一個名為HelloXcode.swift
文件,加入如下代碼:
import Foundation @main enum MyScript { static func main() { print("Hello Xcode") } }
下面我們用終端來編譯上面的HelloXcode.swift
文件,cd
到文件所在的目錄執(zhí)行以下代碼:
xcrun --sdk macosx swiftc -parse-as-library HelloXcode.swift -o CompiledScript
利用macOS SDK
來編譯HelloXcode.swift
并輸出名為CompiledScript
的二進制腳本文件,此時可以直接在當(dāng)前目錄利用./CompiledScript
執(zhí)行該腳本文件,會直接輸出打印HelloXcode
。為了在Xcode
編譯階段就能運行腳本,我們需要將腳本插入到Xcode
的Build Phases
中,首先我們先新建一個Build Phases
如下所示:
Xcode
中的Build Phases
選項卡是Xcode build
項目的中心,Xcode
在編譯項目時其實幫我們做了如下幾件事情:
- 確定項目的一些依賴并編譯
- 編譯項目的代碼
- 鏈接上面編譯的依賴文件
- 復(fù)制資源文件例如圖片等到項目bundle中
這里我們是要在項目編譯開始之前就運行腳本,所以需要調(diào)整新增加的Build Phases
的順序,直接拖到Denpencies
下面,如下圖所示:
點擊剛剛新加的Build Phases
可以重命名,展開后加入如下代碼:
下面的Input Files
可以理解為腳本的變量,這里將HelloXcode.swift
相對工程文件所在的路$SCRIPT_INPUT_FILE_0
進行引用,$(SRCROOT)
代表工程文件所在的目錄,運行項目在build log
(不是打印臺)會看見如下輸出:
script的實際運用
利用script
來實現(xiàn)每當(dāng)build的時候改變 Info.plist
中Bundle version
或者Bundle version string (short)
的值,新建一個IncBuildNumber.swift
文件,加入如下代碼:
import Foundation @main enum IncBuildNumber { static func main() { guard let infoFile = ProcessInfo.processInfo .environment["INFOPLIST_FILE"] else { return } guard let projectDir = ProcessInfo.processInfo.environment["SRCROOT"] else { return } if var dict = NSDictionary(contentsOfFile: projectDir + "/" + infoFile) as? [String: Any] { guard let currentVersionString = dict["CFBundleShortVersionString"] as? String, let currentBuildNumberString = dict["CFBundleVersion"] as? String, let currentBuildNumber = Int(currentBuildNumberString) else { return } dict["CFBundleVersion"] = "\(currentBuildNumber + 1)" if ProcessInfo.processInfo.environment["CONFIGURATION"] == "Release" { var versionComponents = currentVersionString .components(separatedBy: ".") let lastComponent = (Int(versionComponents.last ?? "1") ?? 1) versionComponents[versionComponents.endIndex - 1] = "\(lastComponent + 1)" dict["CFBundleShortVersionString"] = versionComponents .joined(separator: ".") } (dict as NSDictionary).write( toFile: projectDir + "/" + infoFile, atomically: true) } } }
當(dāng)Xcode
在執(zhí)行run script phase
時會通過環(huán)境變量environment variables
來共享build settings
,可以將環(huán)境變量在這里理解為全局變量,這里通過環(huán)境變量拿到了info.plist
中的CFBundleShortVersionString
和CFBundleVersion
變量,并根據(jù)CONFIGURATION
配置的是Release
還是Debug
來修改對應(yīng)的BundleVersion
,至此每當(dāng)build時
都會改變相應(yīng)的BuildVersion
值。
總結(jié)
本文主要介紹了利用xconfig
文件如何進行項目的動態(tài)配置,并進行了實際的演示,同時介紹了script
在Xcode
中編譯的基本使用,并配合xconfig
文件能讓Xcode
在編譯前做更多有意義的事情,更多關(guān)于iOS使用xconfig script腳本的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
IOS LaunchScreen設(shè)置啟動圖片與啟動頁停留時間詳解
這篇文章主要介紹了IOS LaunchScreen設(shè)置啟動圖片與啟動頁停留時間詳解的相關(guān)資料,需要的朋友可以參考下2017-02-02iOS開發(fā)Masonry與Frame布局差異示例詳解
這篇文章主要為大家介紹了iOS開發(fā)Masonry與Frame布局差異示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11快速解決iOS10不能跳轉(zhuǎn)系統(tǒng)WiFi列表的問題
下面小編就為大家?guī)硪黄焖俳鉀QiOS10不能跳轉(zhuǎn)系統(tǒng)WiFi列表的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04iOS App中數(shù)據(jù)管理框架Core Data的基本數(shù)據(jù)操作教程
Core Data框架能夠為我們提供比操作SQL關(guān)系型數(shù)據(jù)庫更簡單的數(shù)據(jù)管理方式,而且內(nèi)置于Xcode中配合IDE操作十分方便,下面我們就來看一下iOS App中數(shù)據(jù)管理框架Core Data的基本數(shù)據(jù)操作教程2016-06-06