深刻談一談iOS模塊獨立運行

背景

最近一直在團隊推動關於iOS模塊獨立運行相關的事項,想把最近的一些想法和實施狀況經過這篇文章作一個記錄。git

若是在一個項目中,某一塊代碼足夠獨立(功能、業務上),就會傾向於將他經過Cocoapods抽離爲一個pods文件。經過一個podspec文件描述這個pod的信息。api

最直接的方式就是把相關文件組織好遷移到一個目錄下,經過podspec對源碼位置,資源位置等信息的描述,完成一個最簡單直白的pod建立。bash

而且在Podfile中統統過網絡

pod 'XXX', :path => '~/dev/XXX'
複製代碼

這種方式,集成到主工程進行開發。開發自測完成後經過私有repo發佈,而且將Podfile中的指向版本ide

pod 'XXX', '1.0.0'
複製代碼

而後咱們就會說,抽離了一個庫,項目的代碼結構變得更合理更清晰了。工具

但這麼作,在我看來跟在項目裏直接用group把這些代碼區別開來沒什麼區別,反而要修改的時候還要從新git pull、pod install變得更加麻煩了。單元測試

由於這麼作被分離的代碼沒法獨立運行,並且因爲依賴不清晰,沒辦法共享給其餘項目使用,致使這種分離方式是一種僞解耦,該有的益處沒有展示出來,修改的時候反倒更加麻煩,這絕對不是咱們想要的效果。測試

由此引出對模塊獨立運行spa

對iOS模塊獨立運行的思考

Pod帶來的受益的預期

我的心目中完美的Pod應該在這四個維度上作到最好。命令行

  • 依賴足夠清晰 清楚的描述本身的依賴情況。說到這個不得不吐槽一個iOS項目與Cocoapods結合以後一個奇怪的現象,就是Pod若是在主工程中經過path引入的,那麼,在不聲明清楚自身以來的狀況下,可使用主工程內全部類,甚至是主工程的類,而且不會獲得任何提示,這種狀況很廣泛,而且過後想要理清依賴的成本極高,這個Pod算是廢了(沒有了抽離Pod的意義了)。

  • 方便共享給其餘項目使用 個人理解,抽離Pod的很大一個目的不就是爲了共享嗎?若是不是爲了這個目的,其實不必抽離Pod的,反而更加麻煩了,只跟一個項目綁死的Pod在我看來,徹底沒有必要作成Pod,項目裏放在Group就能夠了。而爲了能達到這個目的須要克服不少的困難。

  • 方便快速修改驗證 隨着主工程愈來愈大,編譯速度愈來愈慢,開發效率也在無形之中慢慢下降。Pod從主工程中脫離出來獨立運行,單獨編譯,隔絕對主工程的依賴,徹底自給自足,這樣代碼編譯量就會大大下降,達到開發效能提升的目的。

  • 自身質量保障 在第三點的基礎上,帶上徹底針對這個Pod的單元測試UI測試,完美顆粒化代碼的同時,還能很好的保障自身的質量,而且清晰易維護,這塊若是配合Xcode Server,會發揮強大的優點。

然而要作到這些維度上的最好,須要克服不少問題,接下來慢慢道來。

兩種類型的代碼

相似AFNetworking、SDWebImage這樣的功能型代碼,分離的一個只須要確保本身的依賴清晰,被依賴的時候使用方便就能夠了。而對於偏業務型的代碼就不那麼容易了,一般會有界面,還會有各類業務帶來的附加產物,例如打點,例如網絡庫各類規則。

由於功能型代碼自己對於以上四點門檻不高,我就不展開討論了,主要仍是展開說一下業務型代碼。

業務型代碼獨立運行的見解

推動業務型代碼獨立運行過程當中遇到的一些問題列舉:

獨立運行問題

  1. 業務代碼須要用到 打點、網絡等基本能力,背後每一個能力均可能牽扯出一堆間接依賴,但這些依賴跟這個Pod自己沒任何屁關係,同時還會是否是的出現間接依賴不明確致使的編譯報錯問題。

  2. 做爲已是獨立可運行的Pod了,界面什麼的都本身hold了,那麼它必定還須要跟其餘自己以外的幾面進行交互,舉個例子,一個Product的Pod,須要跳轉到Order中的一個界面,或者Chat中的一個界面,而這個界面在代碼層面根本不存在,要如何處置。

  3. 以前也提到了,一個依賴清晰的獨立運行Pod若是被不當心path方式開發了一次,那麼這個Pod會慢慢變廢,下次運行可能就不能運行了,因此還要想辦法要怎麼不被path依賴。

  4. 還有一個比較頭疼的問題是,隨着業務迭代,某個冷門的獨立運行Pod並無跟上腳步,其直接依賴的功能庫在主工程都更新了,但它卻全然不知,難道還要一個一個校對嗎?

  5. 解決了基礎能力的間接依賴,各類必要的直接依賴的間接依賴也會出現不明確而出現的編譯失敗問題,須要解決,確保只有代碼api須要更新時纔會編譯不過,纔是最爽的開發流程。

思考實施解決之道

實施過程當中開發了兩套工具來解決。均未開源,外網勿搜。下面介紹詳細思路。

腳手架工具 - gearmaker

避免被path模式開發

好不容建了一個獨立運行Pod,要是不當心被不明真相的同窗用path開發了就糟了。如何避免被path模式開發呢?若是是源碼模式下,實際上是作不到的。因此咱們把每一個獨立運行的Pod的產物定位二進制庫,靜態動態均可,podspec層面就不容許指向源碼,想要修改源碼,只能經過獨立運行的工程進行修改。

具體操做這裏不展開了,若是podspec指向的是靜態庫,而沒有源碼指向則這個Pod理論上不可能不可被path模式開發。

而若是經過Cocoapods 官方建議的 pod lib create 方式建立,則Pod代碼會存在於 Development Pods 下,而必須在podspec中指定源碼路徑,所以我修改了 pod lib create 的腳手架模板,將Pod代碼直接放入項目的一個Group中,而Group對應產物是一個framework,podspec直接指向podspec,外加Universal打包腳本就能夠啦。

有同窗有疑問了,那源碼調試怎麼辦呢?這個不用擔憂,既然Pod已經能夠獨立運行,有什麼問題須要調試,是均可以在Pod工程中進行數據Mock來還原問題的,因此主工程只是用來集成,不須要考慮調試問題。

即使是特別特殊的狀況,只在主工程能還原,那臨時加一下podspec指向本地path作一下debug也是能夠的。

版本仲裁 & 保鮮

Pod獨立運行工程的一大詬病就是時間一長,工程就沒法編譯經過運行了,而且哪些依賴須要更新,須要詳細對比,成本很是高,不少遇到這樣狀況就會放棄Pod工程。

爲此gearmaker中集成了版本仲裁能力,經過hook pod命令,在pod install以前,計算出指定客戶端主工程最新的依賴全集,在pod install時,在這個全集中找到仲裁版原本使用。

這樣一來,pod install後的Pod工程全部依賴必然與主工程一致,只須要修改由於依賴更新帶來的相關api變動便可經過編譯正常運行,確保Pod工程不會腐敗。

依賴切斷 服務提供組件 - ServiceProvider

全部Pod只須要直接依賴ServiceProvider,由ServiceProvider來統一提供服務能力,包括Pod工程自己須要的任何能力。好比,路由、打點、網絡、從另外一個模塊獲取數據,獲取一個View對象等,均不須要依賴其餘庫,直接從ServiceProvider中經過內置的protocol來得到,並使用。

提供能力的一方對預先放置在ServiceProvider中的protocol進行功能實現,經過如下方式將自身的能力註冊入ServiceProvider,便可爲其餘提供能力,而不須要依賴。

註冊服務

[ServiceProvider registService:[XMUserTrack class] withProtocol:@protocol(UserTrack)];
複製代碼

獲取服務

id<UserTrack> ut = [ServiceProvider serviceWithProtocol:@protocol(UserTrack)];
複製代碼

使用體驗

對於新的Pod建立,直接使用如下命令建立:

gearmaker <PodName>
複製代碼

根據命令行提示進行建立便可。建立完成的腳手架直接提供了ServiceProvider,開發同窗直接從ServiceProvider中獲取服務進行Pod開發,開發完成後經過Universal腳本生成framework上傳到私有repo中定版本便可直接使用。

相關文章
相關標籤/搜索