<listing id="r7f1v"></listing>
<listing id="r7f1v"><var id="r7f1v"></var></listing><listing id="r7f1v"><cite id="r7f1v"><i id="r7f1v"></i></cite></listing>
<listing id="r7f1v"><cite id="r7f1v"></cite></listing>
<listing id="r7f1v"></listing>
<listing id="r7f1v"></listing>
<thead id="r7f1v"><cite id="r7f1v"></cite></thead>
<listing id="r7f1v"></listing>
<listing id="r7f1v"><cite id="r7f1v"></cite></listing>
<listing id="r7f1v"></listing>
<listing id="r7f1v"></listing>
<progress id="r7f1v"><var id="r7f1v"></var></progress>
<listing id="r7f1v"></listing>
APP開發平臺 > Blog > xconfig和script腳本在iOS開發中的使用

利用Xcode進行開發時需要進行很多build setting的設置以便能讓項目按照設置的進行編譯,同時有時候需要在編譯時利用script腳本進行一些設置。


Xcode編譯

在使用xconfig時有幾個關于Xcode的概念是需要理解的,這里我進行通俗簡單的說明,同時需要知道Xcode在編譯的過程中具體幫我們做了那幾件事情。


Xcode target

在實際開發中一個Xcode創建的項目是可以有多個taget的,比如我們創建一個widget時Xcode會自動新建一個target對應這個widget,也可以自己新建,同一個項目有多個target可以滿足不同的測試場景,比如在前期開發階段使用一個target,到UAT階段使用另外一個target。一個target對應一個product,也就是編譯后安裝到手機上的項目,target定義了生成的唯一 product, 它將構建該product 所需的文件和處理這些文件所需的指令集整合進 build system 中,這些指令以 build setting 和 build phases的形式存在,我們用xconfig文件來設置 build setting,同時將script腳本添加到build phases 中。

新建target

Xcode project

Xcode project 是一個倉庫,該倉庫包含了所有的文件,資源和用于生成一個或者多個software products 的信息,它包含一個或者多個targets,其中的每一個 target指明了如何生成 products。project為其擁有的所有 targets定義了默認的build settings,例如project中默認包含debug 和release 兩種build settings 當然,每一個 target能夠制定其自己的 build settings,且target 的build settings 會重寫project 的 build settings。


Xcode scheme

一個project可以有多個target,但是當前的target只能有一個,scheme就是用來確定當前的target的,并制定當前的target使用哪種configuration。


新建configuration

打開項目編輯欄選擇上面的progect同時選擇info欄,可以看到Xcode默認添加了二個Debug和Release的configuration,點擊做下角的+號按鈕選擇復制Debug或者Release其中一個configuration來新建并命名一個自己想取的名字,我這里命名為Mamba。


Configuration文件的使用

平時手動的在Xcode中進行項目的一些build setting設置還是比較麻煩的,一個是需要在Xcode中進行搜索,另外一個是不好管理,例如需要在debug或者release下進行不同的設置的話就比較麻煩。利用Configuration文件來代替手動設置則更加的方便,直接新建Configuration Setting file類型文件,如下圖所示:


利用Configuration設置不同的項目名

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進行導入依賴的Configuration文件,并利用$(inherited)來引用依賴的Configuration文件中的變量。


Configuration文件中的語法一般是SETTING_NAME = VALUE,具體等式二邊設置的值可見蘋果官網.


設置Configuration

點擊PROJECT導航欄并選擇Info會發現多了一個上文我們添加的名為Mamba的Configuration。


點擊左邊的小三角箭頭展開每個Configuration后可以設置項目的project級別的Configuration File和target級別的Configuration File,當然也可以默認不設置。分別設置三個Configuration下的project級別的Configuration File為Base,target級別的Configuration File則為對應的Configuration File,如下圖所示:


查看是否設置成功

點擊TARGETS導航欄,選擇Build Settings并選中All和Levels滑到最下面可看見APP_NAME的值設置如下:


這里需要解釋一下幾個設置的級別:

Resolved: 最后生效的值

Target: 顯示在Target級別生效的值,Target級別的優先級是高于Project的,并且默認繼承Project設置的值。

Project: 顯示在Project級別生效的值,往常在Xcode的General設置的值就是這一級別的。

iOS Default : 顯示iOS默認設置的值。

加上Configuration File后優先級順序從低到高如下:


Platform defaults

Project.xcconfig file

Project file build settings

Target .xcconfig file

Target build settings

設置Info.plist

最后為了通過Configuration File來控制APP運行時名字的顯示,需要在Info.plist中鏈接Bundle display name屬性(沒有的話需要新增)到我們上面設置的user-defined setting(APP_NAME) 上,為此修改Info.plist中Bundle display name的值為 $(APP_NAME)。


測試是否生效

在Scheme頁面分別選擇debug,release和mamba三中不同的Configuration環境運行APP成功的根據不同的Configtation設置不同的項目運行名字。


利用xconfig文件實現OC條件編譯

在開發中經常需要進行條件編譯,在OC中可以利用pch文件配合宏來實現,例如如下:


#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文件中利用條件編譯定義不用的宏來實現項目的動態切換配置,上述宏定義一般定義在.pch中,通常.pch文件中定義的宏都比較雜亂,希望能單獨放在一個獨立的文件中,可以通過新建一個頭文件env.h, 把上述宏定義放到env.h中,在需要使用的時候導入頭文件即可,把環境參數單獨放在一個獨立的頭文件中,更加簡潔,職能更加專一,也便于維護但是這種做法還不是最好的,因為還需要手動導入頭文件,而且生產環境參數和開發環境參數是放在同一個文件中而是不是獨立分開的,要想獨立分開并且使用時又不用導入頭文件可以通過Xcode中的Configurations Setting Fil(.xcconfig)來解決,這應該是最優的實現方式。


xconfig文件的設置

在上面的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導航欄中Preprocessor Macros即可看見我們定義的宏。


代碼使用

可以在代碼中直接使用定義的宏,當切換Configuration時則會根據.xconfig文件輸入不同的打印。


- (void)viewDidLoad {

    [super viewDidLoad];

    NSLog(@"-----------%@-------------",WEBSERVICE_URL);

}

使用#include語法來包含其他配置文件,如#include "Common.xcconfig", 最好是放在文件的最后面,放在文件的開頭也可以。Common.xconfig中第一個鍵的配置必須有:GCC_PREPROCESSOR_DEFINITIONS = $(inherited),沒有Xcode會報錯,暴露自定義鍵時的語法:宏名='$(key)',在代碼或其他地方使用宏名來引用,'$(key)':通過key來指定每個模式下的對應的自定義鍵的名字,通常將宏的名字和key的名字保持一致, 注意 等號前后一定不能有空,Common.xconfig中第一個key是GCC_PREPROCESSOR_DEFINITIONS = $(inherited) 后面跟自定義的key,注意在第一個key后面跟上自己定義的key的時候一定不要回車換行,敲一個空格,然后在同一行后面追加就行了,換行會編譯錯誤, 不能換行,不能換行,不能換行!


Swift中條件編譯的實現

在Swift中是不支持通過GCC_PREPROCESSOR_DEFINITIONS來定義宏的,但是可以通過定義Custom Flags進行定義,這里介紹另外一種方法,還是通過.xconfig文件進行獲取我們需要的宏。前面我們通過info.plist獲取到了.xconfig文件中自定義的變量,再次我們同樣通過info.plist來獲取自定義的變量的值來當做宏使用,首先在info.plist中新建一個WEBSERVICE_URL變量,并設置值為'$(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的使用

上文我們已經知道xconfig文件的使用,其實在編譯之前不只是變量的自定義或者獲取項目的一些默認參數,還可以在獲取這些參數的基礎上,將這些參數作為script腳本的變量來做一些更有意義的事情,Xcode是支持在編譯之前鏈接script腳本的。


script的初步認識

腳本一般來說就是可執行的二進制文件,下面先制作一個簡單的腳本加深認知(實例代碼采用Swift),首先新建一個名為HelloXcode.swift文件,加入如下代碼:


import Foundation

@main

enum MyScript {

  static func main() {

    print("Hello Xcode")

  }

}

下面我們用終端來編譯上面的HelloXcode.swift文件,cd到文件所在的目錄執行以下代碼:


xcrun --sdk macosx swiftc -parse-as-library HelloXcode.swift -o CompiledScript

利用macOS SDK來編譯HelloXcode.swift并輸出名為CompiledScript的二進制腳本文件,此時可以直接在當前目錄利用./CompiledScript執行該腳本文件,會直接輸出打印HelloXcode。為了在Xcode編譯階段就能運行腳本,我們需要將腳本插入到Xcode的Build Phases中,首先我們先新建一個Build Phases如下所示:


Xcode中的Build Phases選項卡是Xcode build項目的中心,Xcode在編譯項目時其實幫我們做了如下幾件事情:


確定項目的一些依賴并編譯

編譯項目的代碼

鏈接上面編譯的依賴文件

復制資源文件例如圖片等到項目bundle中

這里我們是要在項目編譯開始之前就運行腳本,所以需要調整新增加的Build Phases的順序,直接拖到Denpencies下面,如下圖所示:


點擊剛剛新加的Build Phases可以重命名,展開后加入如下代碼:


下面的Input Files可以理解為腳本的變量,這里將HelloXcode.swift相對工程文件所在的路$SCRIPT_INPUT_FILE_0進行引用,$(SRCROOT)代表工程文件所在的目錄,運行項目在build log(不是打印臺)會看見如下輸出:


script的實際運用

利用script來實現每當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)

    }

  }

}

當Xcode在執行run script phase時會通過環境變量environment variables來共享build settings,可以將環境變量在這里理解為全局變量,這里通過環境變量拿到了info.plist中的CFBundleShortVersionString和CFBundleVersion變量,并根據CONFIGURATION配置的是Release還是Debug來修改對應的BundleVersion,至此每當build時都會改變相應的BuildVersion值。


總結

本文主要介紹了利用xconfig文件如何進行項目的動態配置,并進行了實際的演示,同時介紹了script在Xcode中編譯的基本使用,并配合xconfig文件能讓Xcode在編譯前做更多有意義的事情。


2022-07-28 來源:APICloud

xconfig script腳本 iOS開發

高效的App定制平臺,標準化、便宜、快!

提交APP定制開發需求
欧美激情一区二区,国产精品区免费视频,欧美激情视频在线播放,久久久亚洲综合久久98,久久国产精品99精品国产