XcodeGen 導入教學與心得

Hokila Jan
9 min readMay 16, 2021

--

事情是這樣開始的

因為公司的商業模式要大改,所以決定從 backend 到 frontend 都重寫。由於是重新開始,所有會寫 iOS 的同事都一起來開發,幾乎每次的 merge 都會加一堆新檔案,常常造成 project file conflict 。由於 project file 是 xml 格式的檔案,在 conflict 的時候內容非常難看,在解的時候就會比較辛苦,大家每天在解 conflict 就花一堆時間。

這時候找到的解法是 XcodeGen,在 sourcde code 裡面不存在 project file,XcodeGen 會依照設定檔(project.yml)跟目前的資料夾架構,去產生新的 project file,既然 project file 不存在,那就沒有衝突的問題,大家在開發的時候就可以把時間放在功能本身。

專案的需求如下:

  1. 使用 CocoaPods 管理第三方套件,同時也有手動安裝的 framework
  2. 切分四種 api 環境,每種環境再分成 develop 跟 release,共需要 8 個 configuration,每個 configuration 內容有些不同。
  3. 有 4 個 target,分別是 app 本身,一個 extension,兩個 test target
  4. 在 CI/CD 出版的時候使用 fastlane

看完 xocdegen 的文件,發現這些需求都可以滿足,甚至有些一開始沒想到的,看 XcodeGen 的文件才知道可以這樣拆分。我是覺得這個專案的複雜度蠻夠的,這個專案都可以,更簡單的專案一定沒問題。

那就開始囉~~

首先要安裝 XcodeGen,我是用 homebrew

brew install xcodegen

好了以後要依照目前的專案架構設定對應的設定檔,這是導入過程中最難的一步。但是也有好處,在寫的時候會重新學習 project file 裡面的欄位參數是在幹嘛,比較辛苦但是也比較扎實。對一個 iOS 開發為專業的工程師來說,這是本來就該知道但是很多人都靠 IDE 閃避掉的內容。

了解 project.yml

project.yml 是 XcodeGen 的設定檔,產生 project file 的來源,首先要學會怎麼寫。或者是說怎麼抄。

Spec
https://github.com/yonaskolb/XcodeGen/blob/master/Docs/ProjectSpec.md

別人的寫法,可以抄很重要
https://github.com/yonaskolb/XcodeGen/blob/master/Docs/Examples.md

推薦看這兩個
https://github.com/minvws/nl-covid19-notification-app-ios/blob/master/project.yml

https://github.com/atelier-socle/AppRepositoryTemplate/blob/master/xcodegen/project_ios.yml

看完這個 spec 你應該也知道要怎麼設檔案架構。或者可以看本文最後面,我有放 project.yml。

首先遇到的問題是專案的 configuration 太多,都寫在 project.yml 的話會太大,所以作法是把這些原本的設定都寫在 xcconfig 裡面,project.yml 只需要指定每個 configuration 要使用哪個 xcconfig 就好。

要改的東西太多了,能不能 export?

目標

原本的 project -> 產生一堆 xcconfig -> 用 xcodegen 做出新的 project

什麼是 xcconfig
https://nshipster.com/xcconfig/

每一個參數的解釋,因為這些參數實在是太多太複雜了,Matt 做了一個網站幫助解釋每個欄位。
https://xcodebuildsettings.com/

產生 xcconfig,原本找到的作法
https://stackoverflow.com/questions/11164876/is-there-a-way-export-xcode-build-settings-to-xcconfig-file

xcodebuild -scheme "schemeName" -configuration "Debug" -showBuildSettings >> mynew.xcconfig

但是否來發現有更簡單的方式。謝謝 James Dempsey,推薦用這個。
https://github.com/dempseyatgithub/BuildSettingExtractor

產出的 xcconfig 還有大量註解,使用上非常簡單,把 project file 拖進去就好了,會產生對應的 xcconfig。

離題一下

James Dempsey 是前 Apple 工程師,WWDC 傳奇人物,在古老的 session 最後面試會開放 QA 的,他在講 QA 環節都不開放 QA 而是唱一首歌,他有組一個樂團叫 breakpoints。歌在 Apple music 跟 Spotify 上面都找得到,歌名都是從工程師生活常見的名詞。

以下是他之前的表演影片,這首歌叫 Model View Controller,場地是 WWDC sesssion 後錄的,從畫質上看得出來年代久遠。

最新的影片,去年 2020 WWDC 改成 online,breakpoints 不會讓我們失望,還是每年推出新單曲,新的表演,看了才知道 breakpoints 樂團經過這些人的演變,人數直逼 AKB48 。

整合 CocoaPods

讓我們回到 XcodeGen,這個時候 project file 已經完成,檔案都加進來了,不同 scheme 也拆好了,接下來整合 cocoapods 就結束了吧。把 pod install 加到 post command 裡面吧。

pod install 以後出現 warning,實際上是 error,會 build 不過

[!] CocoaPods did not set the base configuration of your project because your project already has a custom config set. In order for CocoaPods integration to work at all, please either set the base configurations of the target `mazu` to `Target Support Files/Pods-mazu/Pods-mazu.prod.xcconfig` or include the `Target Support Files/Pods-mazu/Pods-mazu.prod.xcconfig` in your build configuration (`configs/mazu-PROD.xcconfig`).

解法:

在每個 xcconfig 裡面 include cocoapod 產生出來的 xcconfig。原本 cocoapod 的做法是直接做出一個新的 xcconfig 取代,但是這樣會跟 cocoapod 整合太深,而且沒有任何註解。現在這個方式未來需要把 cocoapod 移掉的時候只需要把這行移掉就好。

#include "../Pods/Target Support Files/Pods-mazu/Pods-mazu.prod.xcconfig"

到此為止,我們已經成功導入 XcodeGen,做出一個可以 build/archive 的 project file 了。接下來來講一些小問題。

實作時遇到的問題:

1. md files 消失

習慣會把 md 加到 project file 裡面,解釋這個 module 是在幹嘛的,或者是寫一些 sample code,原本如下:

  1. 加到 target 裡面,會 compile error,因為把 md 檔也拿去 compile
  2. exclude 裡面寫 **/*.md,就全部 md file 都消失了

解法

在 filegroup 裡面加進來整個 mazu folder,但是在 target 裡面設定 exclude

2. 騙人的 xcconfig

  1. BuildSettingExtractor export 出的 xcconfig file 很多
  2. 光看到產出數量就想放棄了

解法

雖然 generate 跑出 4*9 個 xcconfig,但是有用到的只有 project 跟 主 app target 的而已,仔細看 unit test 跟 UITest 的所有 xcconfig 內容都是一樣的,直覺就是讓所有 configuration 都使用同一個 xcconfig file,但是跑完 cocoapod 以後還是有 error,就乾脆都不寫 configFiles。想不到就過了。這樣一來 unit test 跟 UITest 的 xcconfig 都沒用到,可以全刪了。

之後要解決的問題:

  1. 新的 extension target
  2. build phase 的 shell script,有示範 R.swift 跟 swiftlint
  3. 3rd party framework 或者 carthage 或者 SPM,目前只用 cocoapods

[最重要] 要改 yaml 還是 xcconfig?

因為產生 project file 的時候,設定可以寫在 project.yml 或者 xcconfig,以下是在設定時的建議。

  1. 如果是整個 project 都會影響到的: 改 yaml,像是增加 configuration 名字,最低支援版本
  2. 如果是整個 target 都會影響的,改 yaml target 裡面,像是用了什麼 SPM,build phase 要執行什麼 shell script
  3. 如果是 info plist 裡面可以控制的,可以直接改 info plist,或者改 yaml,可以參考這份
  4. 如果是每個 config 有不一樣的設定,像是 optimal level,改 xcconfig ,像是使用不同 code sign

最後來上一個 sample,這是目前專案的 project.yml ,可以看出除了之前提到的需求外,還整合了 R.swift 跟 Swiftlint

後記

這篇文章其實是我在 2月到 Cocoaheads Taipei 分享內容,當時沒有錄影,而且當時剛導入 XcodeGen,還不確定效果怎樣,經過 3 個月同事們每天努力噴發一堆 code 發一推 merge request,沒有聽到有任何 project conflict 的怨言,全部都是檔案本身的 conflict,讓大家把時間都花在寫 code 本身,當時導入的目標的確有達成。可喜可賀。

--

--

No responses yet