開發與維運

iOS開發體驗優化方案

滾動.gif

iOS開發體驗存在的問題

▐ 開發環境搭建難

開發環境依賴特定軟件版本,配置複雜

閒魚iOS工程不僅依賴XCode,還依賴了taobaoenv 1.2.0和cocoapods 1.2.0這兩個包管理工具。根據大家的經驗,這兩個工具在ruby2.3.x時,問題較少。特定軟件版本,系統自帶軟件版本衝突,環境變量設置等等一些列的操作步驟導致環境搭建複雜,需要求助iOS開發同學才能搞定。

維護難

Mac系統升級後,cocoapod容易出現問題,不得不重新搭建開發環境。具體原因也是多種多樣:系統環境變量變了,導致找不到特定版本ruby;ruby隨系統升級導致cocoapod不能用,需要重新安裝;Gem版本問題;Ruby源問題等等。這也導致許多開發同學不敢輕易的去升級系統,無法及時體驗到新系統的特性。

Pod依賴下載量大

由於cocoapod本身的工作原理,pod更新下載工程依賴時,會下載各個版本的文件信息,總量特別大。以閒魚iOS工程為例,總共需要下載近20G的緩存文件,而且大部分都是幾K的小文件,下載時間可能會持續十幾個小時,導致新環境搭建到初次體驗時間跨度非常久。

▐ 切分後APP打包慢

當開發同學在多個分支/版本開發的時候,時常需要切換分支開發調試和bugfix。但是切換分支之後,整個iOS工程打包時間在30-40分鐘左右。有時候為了修復一個版本的bug,不得不切換分支,然後重新打包調試。修復和驗證bug可能只需要五分鐘,打包卻用了30多分鐘,投入產出不成比例。

為了解決這些存在的問題,我們進行了一些列的探索,跟大家一起分享下,也歡迎有更好的解決方案出現。

iOS環境搭建

虛擬化技術的不斷髮展,為我們統一端側開發環境提供了新思路,我們設想如果iOS開發環境能夠跟Mac解耦,且可以移植,大家可以輕鬆複用,那麼第一二個問題就迎刃而解了。為此我們做了幾個嘗試:

▐ 虛擬機方案

在MacOS本地搭建虛擬機,內裝MacOS系統。在虛擬機內搭建iOS開發環境,然後通過虛擬機鏡像copy實現iOS開發環境移植,解決環境搭建難題。

image.png

這個方案存在以下幾個問題:

性能問題:iOS的編譯過程是一個IO密集型和CPU密集型操作,虛擬機通過虛擬HOST系統的磁盤和CPU,性能會大打折扣,導致編譯時間變長,影響開發體驗。

安全問題:在Mac工作機安裝虛擬機,需要通過公司安全審核。

黑蘋果問題:虛擬機內的Mac系統是沒有經過授權的,會帶來盜版侵權風險。

虛擬機是比較重的虛擬化技術,因此我們轉向了更加輕量化的docker技術。

▐ 完全Docker化

將iOS開發依賴的軟件和環境變量全部docker化。通過docker鏡像實現iOS開發環境的移植。對於cocoapod, taobaoenv等ruby類工具,鑑於ruby的跨平臺特性,可以很方便的遷移到docker內。但是對於強依賴MacOS的XCode,我們嘗試用Facebook出品的xcbuild替代。這是一款兼容xcodebuild的編譯工具,網上也確實有網友用這個軟件搭建iOS編譯環境。

image.png

這個方案存在以下幾個問題:

Xcbuild跟Xcodebuild的兼容性無法評估

xcode升級後,xcbuild跟進升級兼容的一段時間內,只能回退到原來的開發方案,來回切換開發環境,體驗差。

上面兩個方案都沒有很好的解決iOS開發環境移植和解耦的問題,但是在完全docker化的嘗試中,我們發現最複雜的cocoapod和ruby安裝配置部分是能夠docker化的,xcode安裝後並不需要特殊的配置,因此我們設計實現了一個折中方案:Host內開發(部分docker化)

▐ Host內開發(部分docker化)

本方案中:開發編譯調試工作仍然在MacOS本地,使用xcode; 而將cocoapod和taobaoenv相關的軟件和環境變量配置等docker化。這樣既遵循了開發同學一貫的開發體驗,又兼顧了開發環境的可移植問題。

image.png

為了能夠讓Docker內cocoapod拉取的依賴文件和生成的pod工程能被本地的XCode識別,我們將本地pod緩存目錄掛載到docker,這樣Pod拉取的依賴既能在docker內更新,也能在MacOS中被XCode訪問,具體如下圖(統一編程平面端+Faas軟件架構圖):

image.png

這樣既做到了簡化開發環境搭建的複雜度,方便了想嘗試iOS開發的同學快速搭建環境,還能給開發同學無差別體驗。而且通過這個方案,我們的iOS開發環境可以方便的在各個同學的開發環境中遷移,而且也可以統一進行升級改造。

本方案將Pod相關的依賴遷移到了Docker中,與MacOS解耦,因此iOS開發同學可以自由升級Mac系統,不用擔心開發環境被破壞,解決了維護難的問題。

為了解決新搭建的環境需要大量拉取pod依賴的問題,我們將pod的本地中間文件上傳到OSS雲盤(上圖藍色OSS雲盤),開發同學只需要一次性下載壓縮包並解壓到本地,然後增量更新就可以了。

切分支後APP打包速度問題

客戶端開發同學經常需要在多個分支(版本)上面開發業務,且時常需要來回切換進行業務開發和問題定位。這帶來的一個問題是:當開發同學從A分支切換到B分支的時候,需要重新打包APP,整個過程大概需要30-40分鐘左右。

在分析了閒魚iOS工程打包過程後,我們將耗時鎖定在兩個階段:Pod操作和XCode編譯。打包速度優化也將分為兩個階段進行:

▐ Pod操作加速

Pod install/update主要的工作是讀取Podfile,進行依賴版本控制和衝突解決,並生成Pod工程。生成的相關文件存儲在Pods目錄和Pods.xcodeproj中。當切換回之前分支時,Podfile經常是不會發生變化的,因此重新生成pod工程實屬浪費。

經過測試,如果我們將這些中間文件保存起來,多次切換分支後,這些中間文件仍然能夠還原之前的Pod工程,從而避免切分支後重新生成Pod工程的步驟,省去10分鐘左右的開銷。

▐ XCode編譯速度優化

對於XCode編譯速度優化,網上有很多方案,大致可以分為三類:

Cocopods依賴編譯加速:

比如cocoapods-packager,它可以將pod依賴打包成static library,IOS工程以靜態庫的形式引入pod依賴,省去重複編譯的時間。

但是這個方案也存在一些問題;私有庫和第三方庫更新很麻煩,每次都需要重新打包靜態庫,並上傳到代碼倉庫;且很難調試源碼

分佈式編譯:比如distcc

分佈式編譯的原理是將需要編譯的文件分散到編譯集群的其他機器上編譯,然後將編譯好的二進制文件傳回。本地編譯器再將這些二進制文件鏈接在一起。分佈式編譯對於大工程提速明顯,但是對於小工程,反而會拖累編譯速度。

緩存編譯的中間結果:CCache,BUCK

更為廣泛的加速方案是緩存編譯的中間結果,比如CCache,Buck等,這些方案,網上有詳細的資料,不再一一贅述。但是引入這些方案,都需要對目前的iOS工程進行改造,甚至需要改變用戶的開發習慣,因此不符合我們的要求。但是緩存中間編譯結果的方案給我們提供了一些啟發:

我們知道XCode是具有增量編譯能力的,這其實也是利用了上一次編譯的中間產物,本地再次編譯的時候,如果發現文件沒有變化,則忽略這個文件,如果源碼文件時間戳更新了,那麼就重新編譯這個文件,因為每次變化的源碼都是少量的,這樣就可以達到加快編譯速度的目的。

對於閒魚iOS工程,如果我們在切分支之前保存當前iOS分支編譯的中間產物,然後在切換回當前分支的時候,恢復之前保存的中間產物,那麼是不是就可以觸發XCode增量編譯了呢?事實確實如此。

image.png

具體方案:

1、在切分之前緩存當前分支的Pods Project, Flutter Project以及編譯的中間產物,Podfile.lock, linkmap等等相關文件。

2、切換分支

3、恢復新分支之前緩存的中間產物

4、重新打包iOS APP。
通過這兩步優化,我們將閒魚iOS工程切分支後的打包時間由原來的30-40分鐘降低到五分鐘以內,效率提升近六倍。

總結

iOS環境搭建中複雜和耗時的步驟,通過docker鏡像和緩存優化後,搭建的難度大大降低,iOS新手也基本可以在三小時內搞定。

image.png

同時,通過緩存和複用打包過程產生的中間產物,切換分支後的打包耗時控制在五分鐘內,降低為原來的六分之一,提升了開發效率。

關注「淘系技術」微信公眾號,一個有溫度有內容的技術社區~
image.png

Leave a Reply

Your email address will not be published. Required fields are marked *