在 iOS 開發(fā)過程中,你是不是會經(jīng)常遇到這些情況:
每次打開一個新項目,都需要手動搭建開發(fā)環(huán)境;有時候在安裝第三方工具時使用到 sudo 權(quán)限,導致以后安裝工具都需要手工輸入密碼而無法實施自動化。還有,每當啟動一臺新 CI 時,就需要手工登錄并配置一遍,更可怕的是,原先搭建好的 CI 會隨著 Xcode 版本更新需要重新配置。
為什么會這么麻煩呢?就是因為你在項目開始之初沒有做好統(tǒng)一配置。
所謂統(tǒng)一配置,就是所有的配置信息都以文本的格式存放在 Git 里面,我們可以隨時查看修改記錄,以此來幫助我們比較不同配置之間的差異性,然后在這個基礎(chǔ)上不斷更新迭代。
可以說,有了統(tǒng)一配置,任何工程師都可以搭建出一模一樣的開發(fā)環(huán)境,構(gòu)建出功能一致的 App;有了統(tǒng)一配置,還可以讓我們按需延展 CI 服務(wù),而不用任何手工操作。更重要的是,它還可以應(yīng)用到各個類似的 iOS 項目中,極大地減輕了項目前期的搭建成本。
既然統(tǒng)一的配置那么重要,那么我們怎樣搭建統(tǒng)一配置的開發(fā)環(huán)境呢?
Ruby 工具鏈
我們可以通過 Ruby 工具鏈為整個項目搭建一致的開發(fā)和構(gòu)建環(huán)境。為什么選擇 Ruby 而不是其他語言環(huán)境呢?因為在 iOS 開發(fā)方面,目前流行的第三方工具 CocoaPods 和 fastlane 都是使用 Ruby 來開發(fā)的。特別是 Ruby 有非常成熟的依賴庫管理工具 RubyGems 和 Bundler,其中 Bundler 可以幫我們有效地管理 CocoaPods 和 fastlane 的版本。
下面一起來看看怎樣搭建一個統(tǒng)一的開發(fā)環(huán)境吧。

通常,統(tǒng)一的開發(fā)環(huán)境應(yīng)該從操作系統(tǒng)開始。對于 iOS 開發(fā)來說,MacOS 是目前 iOS 開發(fā)唯一支持的操作系統(tǒng)。在公司,MacOS 的版本一般由 IT 部門統(tǒng)一管理和更新。要注意,當公司統(tǒng)一更新了我們開發(fā)環(huán)境的 MacOS 版本以后,需要同時更新 CI 上 MacOS 的版本,以保持一致。
Xcode
位于 MacOS 上層的是 Xcode 和 rbenv。其中,Xcode 是 iOS 開發(fā)和構(gòu)建工具,在同一個項目里,最好使用同一個版本的 Xcode 進行開發(fā)和構(gòu)建,我們可以在項目的 README.md 文件標注 Xcode 的版本。
那我們怎樣才能保證每個人都安裝同一個版本號的 Xcode 呢?技巧就是我們不要到有自動更新功能的 Mac App Store 中下載 Xcode,而是到蘋果的開發(fā)者網(wǎng)站搜索并下載。
有時候我們會同時開發(fā)多個項目,這樣有可能要安裝多個不同版本的 Xcode。如果你的機器有多于一個版本的 Xcode,此時需要特別注意,為了保證所使用的編譯器版本一致,在每次執(zhí)行自動化命令之前(如執(zhí)行bundle exec fastlane test),要先使用xcode-select -s來選擇該項目所對應(yīng)版本的 Xcode。
比如說我的電腦上有多個 Xcode 版本,在開發(fā) Moments App 時,每次執(zhí)行自動化命令之前都會執(zhí)行這樣一條命令xcode-select -s /Applications/Xcode12.2.app/Contents/Developer來選擇 Moments App 項目所使用的 Xcode。這里的Xcode12.2.app就是我安裝的 Xcode 12.2 版所在的位置。
rbenv
有了版本一致的 Xcode 以后,因為后期我們會用到 CocoaPods 等第三方 Ruby 工具,為了自動化安裝和管理這些工具,整個項目團隊所使用的 Ruby 版本也必須保持一致。為此,我們就需要用到 Ruby 環(huán)境管理工具。
目前流行的 Ruby 環(huán)境管理工具有 RVM 和 rbenv。我推薦使用的是 rbenv,因為它使用 shims 文件夾來分離各個 Ruby 版本,相對于 RVM 更加輕裝而方便使用。千萬注意,團隊內(nèi)部不要同時使用不同的 Ruby 環(huán)境管理工具,否則項目編譯會出錯。
rbenv 是 Ruby 環(huán)境管理工具,能夠安裝、管理、隔離以及在多個 Ruby 版本之間切換。要使用 rbenv,我們可以通過 Homebrew 來安裝它,下面是安裝 Homebrew 和 rbenv 的腳本。
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
brew install rbenv ruby-build rbenv-vars
一旦安裝 rbenv 完畢,我們需要把以下的設(shè)置信息放到你的 Shell 配置文件里面,例如 ~/.bash_profile 或者 ~/.zshrc 等文件,這樣能保證每次打開終端的時候都會初始化 rbenv。
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
接著我們就可以安裝和設(shè)置項目的 Ruby 環(huán)境了。
$ cd $(PROJECT_DIR)
$ rbenv install 2.7.1
$ rbenv local 2.7.1
此處是把項目的 Ruby 環(huán)境配置為 2.7.1 版本。rbenv 會幫我們建立 一個叫作.ruby-version 的文件,該文件里面只保存一個版本號(例如2.7.1)的字符串。這個包含了版本號的文件可以用 Git 進行管理。如果要更新版本,可以通過rbenv local命令進行,每次更新也由 Git 統(tǒng)一管理,這樣就能讓其他開發(fā)者使用同一版本的 Ruby 開發(fā)環(huán)境了。
RubyGems 和 Bundler
RubyGems 和 Bundler 主要是用來安裝和管理 CocoaPods 和 fastlane 等第三方工具。
具體來說,RubyGems?是 Ruby 依賴包管理工具。在 Ruby 的世界,包叫作 Gem,我們可以通過gem install命令來安裝。但是 RubyGems 在管理 Gem 版本的時候有些缺陷,就有人開發(fā)了 Bundler,用它來檢查和安裝 Gem 的特定版本,以此為 Ruby 項目提供一致性的環(huán)境。
要安裝 Bundler,我們可執(zhí)行g(shù)em install bundler命令進行,之后,再執(zhí)行bundle init就可以生成一個 Gemfile 文件,像 CocoaPods 和 fastlane 等依賴包,我們就可以添加到這個文件里面。
具體代碼如下:
source "https://rubygems.org"
gem "cocoapods", "1.10.0"
gem "fastlane", "2.166.0"
注意我們在gem命令里面都指定了依賴包的特定版本號。例如,在我們的 Moment App 就使用了1.10.0版的 CocoaPods,然后執(zhí)行bundle install來安裝各個 Gem。 Bundler 會自動生成一個 Gemfile.lock 文件來鎖定所安裝的 Gem 的版本,例如:
DEPENDENCIES
? cocoapods (= 1.10.0)
? fastlane (= 2.166.0)
為了保證團隊其他成員都可以使用版本號一致的 Gem,我們需要把 Gemfile 和 Gemfile.lock 一同保存到 Git 里面統(tǒng)一管理起來。
到此為止,我們已經(jīng)知道怎樣使用 Ruby 工具鏈配置一個統(tǒng)一的開發(fā)環(huán)境。但在真實的開發(fā)環(huán)境中,搭建環(huán)境只需要一個人來完成即可,其他成員執(zhí)行以下腳本就能完成整套開發(fā)環(huán)境的搭建。
$ ./scripts/setup.sh
我們一起看看這個腳本做了些什么?
# Install ruby using rbenv
ruby_version=`cat .ruby-version`
if [[ ! -d "$HOME/.rbenv/versions/$ruby_version" ]]; then
? rbenv install $ruby_version;
fi
# Install bunlder
gem install bundler
# Install all gems
bundle install
# Install all pods
bundle exec pod install
該腳本主要做了四件事情,第一步是在 rbenv 下安裝特定版本的 Ruby 開發(fā)環(huán)境,然后通過 RubyGems 安裝 Bunlder,接著使用 Bundler 安裝 CocoaPods 和 fastlane 等依賴包,最后安裝各個 Pod。這樣,一個統(tǒng)一的項目環(huán)境就搭建完成了,接下來開發(fā)者就可以打開 Moments.xcworkspace進行開發(fā)了。
說完 Ruby 環(huán)境搭建以后,最后我們一起聊聊保證項目文件一致性的 .gitignore 文件。
.gitignore 文件
.gitignore 文件是一個配置文件,用來指定讓 Git 需要忽略的文件或者目錄。如果沒有 .gitignore 文件,項目成員可能會不小心把一些自動生成等無關(guān)重要的文件或者具有個人信息(例如 xcuserdata)的文件保存到 Git 里面。這就大大增加了查看 Git 修改歷史的難度。因此,在項目初期就配置一個合適的 .gitignore 文件,能減輕后續(xù)的管理工作。