組件化實(shí)踐之初體驗(yàn)

前因

  • 隨著公司業(yè)務(wù)的發(fā)展和細(xì)化拆分,各業(yè)務(wù)端需自主開發(fā),某些業(yè)務(wù)存在內(nèi)嵌邏輯
  • 公司當(dāng)前各端的項(xiàng)目存在重復(fù)的功能塊和邏輯塊
  • 業(yè)務(wù)發(fā)展的同時(shí)開發(fā)團(tuán)隊(duì)也在擴(kuò)展,那么如何處理團(tuán)隊(duì)和項(xiàng)目發(fā)展帶來的一些問題

一番調(diào)研之后,我們決定使用組件化來解決當(dāng)前遇到的這些問題

組件化

1.什么是組件/模塊化

簡單的來講可以包括下面這兩塊

  • 獨(dú)立的業(yè)務(wù)或功能塊(細(xì)分來講)
  • 多個(gè)功能和業(yè)務(wù)組成的模塊(可以稱之為大顆粒度的組件)

對應(yīng)到我們當(dāng)前公司目前的項(xiàng)目結(jié)構(gòu)可以大概分為這么幾塊

  • 業(yè)務(wù)組件: 登陸模塊、客服服務(wù)、支付模塊等等
  • 基礎(chǔ)UI組件:通用控件(和其他一些widget)、多媒體處理、日歷管理等等
  • 基礎(chǔ)功能組件:網(wǎng)絡(luò)管理、主題管理、定位管理等等

目標(biāo)

  • 模塊拆分,實(shí)現(xiàn)業(yè)務(wù)分離,可跨團(tuán)隊(duì)開發(fā)
  • 各端使用同一套基礎(chǔ)組件來進(jìn)行開發(fā),代碼整體可控
  • 模塊和組件可組裝和復(fù)用,提高開發(fā)效率
  • 業(yè)務(wù)隔離和代碼解耦

方案

1.對相關(guān)的功能模塊和業(yè)務(wù)組件進(jìn)行拆分,通過framework的形式發(fā)布成內(nèi)部私有庫
2.通過cocoapods實(shí)現(xiàn)對私有庫的引入和版本管理
3.通過fastlane進(jìn)行持續(xù)集成

技術(shù)棧

1.靜態(tài)庫和動(dòng)態(tài)庫:共享代碼的方式
2.CocoaPods:強(qiáng)大的iOS第三方庫管理工具
3.FastLane:自動(dòng)化持續(xù)集成工具集

動(dòng)態(tài)庫和靜態(tài)庫

1.靜態(tài)庫 Static Library

即.a文件,項(xiàng)目源碼所對應(yīng)的目標(biāo)文件(.o/.obj)的打包體;配合.h文件使用,暴露.a中的方法或成員

2.動(dòng)態(tài)庫 Dynamic Framework

即.framework文件,是一種資源打包方式,可以說是一個(gè)bundle文件夾(包含代碼文件、頭文件、資源文件等)

3. Library VS Framework

  • 靜態(tài)庫不能包含xib 、storyboard、圖片這樣的資源文件,其他開發(fā)者必須將它們復(fù)制到 app 的 main bundle 中才能使用,維護(hù)和更新非常困難

  • 而動(dòng)態(tài)庫 則可以將資源文件包含在自己的 bundle 中

  • 靜態(tài)庫只能隨應(yīng)用二進(jìn)制文件一起加載,鏈接時(shí)完整地拷貝至可執(zhí)行文件中,被多次使用就有多份拷貝

  • 而動(dòng)態(tài)庫鏈接時(shí)不復(fù)制,程序運(yùn)行時(shí)由系統(tǒng)動(dòng)態(tài)加載到內(nèi)存,供程序調(diào)用,系統(tǒng)只加載一次,多個(gè)程序共用,節(jié)省內(nèi)存。(iOS目前只允許使用系統(tǒng)動(dòng)態(tài)庫,如系統(tǒng)的UIKitFoundation等)

總結(jié)一下就是

1F301289-415F-476F-B47E-F2E072479C1A.png

我們可以看到動(dòng)態(tài)庫相比較靜態(tài)庫而言是有很多方面的優(yōu)勢的,然而在iOS8之前蘋果是不允許我們使用動(dòng)態(tài)庫的,在iOS8之后蘋果推出了Embedded Framework的概念,即允許我們使用動(dòng)態(tài)庫,我們可以在工程模板內(nèi)看到Cocoa Touch Framework。蘋果之所以在iOS8上推出這個(gè)東西其原因是在iOS8時(shí)蘋果推出了APP Extension (程序擴(kuò)展,例如我們制作通知欄的擴(kuò)展程序以及允許我們使用第三方鍵盤等等),這種情況下需要和我們的主APP進(jìn)行部分代碼共享,還有一個(gè)原因就是在iOS8時(shí)推出了Swift,由于Swift的語言特性(運(yùn)行時(shí)特性尚未穩(wěn)定),所以目前只支持動(dòng)態(tài)庫。

簡單的來講Cocoa Touch Framework有以下兩個(gè)特點(diǎn)

  • 生命周期被限制在單個(gè)APP進(jìn)程中
  • 不同的APP使用到時(shí)需重復(fù)加載

所以說Cocoa Touch Framework其實(shí)是閹割版的動(dòng)態(tài)庫,不過這也是人之常情,畢竟蘋果如果開放了真正的動(dòng)態(tài)庫,那還不天下大亂。

我們簡單來看下使用組件化后預(yù)期的項(xiàng)目結(jié)構(gòu)

0FCDCF7C-E25E-4DD8-83E6-AA8BF3D953E1.png

首先我們目前沒有對更多的模塊做拆分(后續(xù)也會(huì)拆分出來),只是把商城這個(gè)模塊拆分出來(由另一個(gè)商城的團(tuán)隊(duì)進(jìn)行開發(fā)),然后以Framework的形式加入到我們主APP當(dāng)中。

如上圖所示我們可以看到我們的主APP需要引入我們的商城端,我們的商城端主APP同時(shí)又需要依賴于我們抽出的內(nèi)部組件;并且這三者都會(huì)依賴于一些第三方的Framework來進(jìn)行開發(fā)。我們最終的目標(biāo)就是把這些所有的東西融合在一個(gè)工程里面,也就是我們的主APP中。

最終的方案就是讓商城內(nèi)部組件、第三方Framework都以Framework的形式引入到我們的主APP

567E1911-C9F9-4827-98AA-A5AC9E2E407E.png

關(guān)于如何制作一個(gè)Framework網(wǎng)上已有很多相關(guān)的資料,可自行Google,這里就不做相應(yīng)的介紹

那么當(dāng)制作好我們的Framework后,如何讓另一個(gè)工程能夠使用到我們的Framework,我們最先想到的方法就是把我們的Framework直接導(dǎo)入到工程中,可能還需要添加相應(yīng)的依賴和其他的一系列配置。那么問題就來了,首先這種方案依賴和配置非常繁瑣,更重要的是當(dāng)我們的Framework更新時(shí)我們需要做的是刪除上一個(gè)版本然后添加新版本,然后可能還需要更新一些依賴配置等等,這種版本管理方式不僅效率低下而且還可能出現(xiàn)不可控的風(fēng)險(xiǎn)。幸運(yùn)的是在我們的iOS開發(fā)中另一種有更好的管理依賴的方式,這就是我們的Cocoapods

Cocoapods

1.什么是Cocoapods

簡單的來說Cocoapods是我們 iOS 應(yīng)用程序開發(fā)的一個(gè)第三方庫依賴的管理工具(使用Ruby開發(fā)),通過它我們可以高效率的導(dǎo)入、配置以及管理所用到的第三方,如果你是一個(gè)iOSCoder相信肯定對它不會(huì)陌生

2.特點(diǎn)/好處

簡單的來說可以有以下幾個(gè)方面的好處

  • 避免直接導(dǎo)入文件,方便后續(xù)版本管理
  • 簡化集成流程,避免不需要的配置
  • 自動(dòng)處理庫之間的依賴關(guān)系
  • 簡化開發(fā)者代碼的發(fā)布流程

3.原理

我們簡單的來討論下Cocoapods的功能實(shí)現(xiàn)原理,在講原理之前我們首先需要了解下我們Xcode的幾個(gè)大的項(xiàng)目工程結(jié)構(gòu)

Target:一個(gè)target對應(yīng)一個(gè)目標(biāo)文件,也就是一個(gè)APP;target可以相互獨(dú)立,也可以依賴于另一個(gè)target
Project:project是構(gòu)建一個(gè)或者多個(gè)APP所需的所有文件、資源和信息的存儲(chǔ)庫,可包含多個(gè)target,可以管理不同的target間的關(guān)系,我們默認(rèn)創(chuàng)建的工程類型就是Project

3E252D48-D0B0-4635-85B4-6AC6EE82FD1B.png

如上圖所示,我們的工程Project下對應(yīng)了三個(gè)target,分別是我們的App和兩個(gè)單元測試target

Workspace: workspace是最大的集合,可以包含多個(gè)Xcode Project,以及要包括的任何其他文件??梢怨芾矶鄠€(gè)Project間的關(guān)系(引用和依賴)

如上圖所示,在這種多target的情況下可能會(huì)存在兩種依賴關(guān)系
1.顯式依賴:某個(gè)target直接引入了另一個(gè)target
2.隱式依賴:某個(gè)target也需要依賴于另一target,但沒有直接引入

我們Cocoapods的方式就是創(chuàng)建一個(gè)WorkspacePods Project文件,通過Pods Project文件來管理所需要依賴的第三方的Framework,然后把我們的項(xiàng)目工程的Project和創(chuàng)建的Pods Project一起放入創(chuàng)建的Workspace中,最后通過這個(gè)Workspace來實(shí)現(xiàn)對所有工程的管理

如果你的項(xiàng)目使用了Cocoapods,你會(huì)發(fā)現(xiàn)有下面幾個(gè)特點(diǎn):

1.主工程沒有顯示依賴各個(gè)第三方庫,Pods 項(xiàng)目最終會(huì)編譯成一個(gè)名為 libPods.a (Pods_....framework)的文件,主項(xiàng)目只需要依賴這個(gè).a文件即可

5859815B-9F01-47BD-B473-EE1FAAF8BD1A.png
FE92E691-30A4-48A0-8503-80998F74169A.png

2.對于資源文件,CocoaPods 提供了一個(gè)名為 Pods-resources.sh 的 bash 腳本,該腳本在每次項(xiàng)目編譯的時(shí)候都會(huì)執(zhí)行,將 Pods 依賴庫的各種資源文件復(fù)制到目標(biāo)目錄中
3.CocoaPods 還通過一個(gè)名為 Pods.xcconfig 的文件來在編譯時(shí)設(shè)置所有的依賴和參數(shù)

2FA89168-DFEF-46F9-871A-16AF2DA54093.png

如果什么都不干,我們的主工程肯定是不能夠引用Workspace管理的第三庫的。如果打開工程的Build Settings,我們可以看到Header Search Paths、Library Search Paths、Framework Search Paths等這些字段,這些字段等于告訴了我們主工程所依賴的第三方庫在哪里也就是資源路徑。然而知道路徑只是第一步,因?yàn)槲覀冞€沒有確立他們之間的引用關(guān)系,所以我們得聲明一下,告訴編譯器他們之間存在著引用關(guān)系,這個(gè)字段就是Other linker flags,如下圖所示

CCCA5B64-B242-438D-9DC0-EA513E807423.png

4.如何使用

如果你新建一個(gè)工程,在引入Cocoapods進(jìn)行管理之后,你會(huì)發(fā)現(xiàn)工程目錄下會(huì)多出這么一些文件,如下圖所示

前.png
后.png

podfile:說明文件,說明pod需要導(dǎo)入和管理那些依賴庫
podfile.lock:用來保存已安裝的pod的依賴庫的版本
pods文件夾:Pod和依賴庫的工程文件夾

我們簡單的來看下導(dǎo)入的過程,如下圖所示

導(dǎo)入過程

首先會(huì)根據(jù)我們的podfile中的地址找到對應(yīng)的第三庫所在的git倉庫,如果有指定tag的話會(huì)去定位到對應(yīng)的tag的提交(沒有取最近的一次),然后會(huì)去檢索工程目錄下的podspec文件,通過podspec文件來做一系列的驗(yàn)證,比如工程名,版本號(hào)等等,驗(yàn)證合法之后會(huì)根據(jù)podspec中的source_file字段找到需要下載的代碼文件,然后配合對應(yīng)的一些資源文件和配置文件一起下載下來,共同組成我們所需要的第三方庫文件

5.制作podspec

如上文所敘,要想讓我們的工程能夠支持cocoapods,我們需要制作工程對應(yīng)的podspec,并將其發(fā)布到cocoapods上。
如何制作一個(gè)合適的podspec這里就不做敘述,可以通過下面的一些文章來進(jìn)行了解

1.CocoaPods建立自己的Podspec
2.發(fā)布CocoaPods組件碰到的坑與心得體會(huì)

Fastlane

Fastlane是用Ruby語言編寫的一套自動(dòng)化工具集和框架,它可以非??焖俸唵蔚拇罱ㄒ粋€(gè)自動(dòng)化發(fā)布服務(wù),并且支持Android,iOS,MacOS。比如在我們的iOS開發(fā)過程中,經(jīng)常會(huì)經(jīng)歷從 編譯->打包上傳->填寫應(yīng)用更新數(shù)據(jù)->等待iTunesConnect編譯->選擇版本發(fā)布等這一系列過程,而Fastlane則可以幫我們自動(dòng)化處理這些事情。
具體的可參考:小團(tuán)隊(duì)的自動(dòng)化發(fā)布-Fastlane帶來的全自動(dòng)化發(fā)布
以及 Fastlane實(shí)戰(zhàn)(一):移動(dòng)開發(fā)自動(dòng)化之道

踩到的坑

由于Swift不支持.a靜態(tài)庫的原因,當(dāng)我們的第三方庫內(nèi)包含.a靜態(tài)庫,組件庫內(nèi)引用了這個(gè)第三庫,然后我們在引入組件庫時(shí)就會(huì)pod install報(bào)錯(cuò),如下

 [!] The 'Pods-LJA_Example' target has transitive dependencies that include static binaries: 
(/Users/nero/Desktop/Static_Dynamic/Componment/Example/Pods/libWeChatSDK/libWeChatSDK.a)

具體的解決方案參考這篇文章組件化-動(dòng)態(tài)庫實(shí)戰(zhàn)

未來

  • 更細(xì)分的功能組件和模塊
    我們當(dāng)前只是對部分模塊和功能進(jìn)行了拆分,如只是把商場單獨(dú)作為一個(gè)模塊分離出去了,后續(xù)可能需要對相應(yīng)模塊和功能進(jìn)行更細(xì)分的拆分

  • 模塊間的通信,采用何種方式?
    在我們的模塊和組件進(jìn)行拆分過后,那么一個(gè)需要思考的問題就是這些模塊和組件間的通信該如何解決,當(dāng)前對這方面的討論也比較多,具體可參考這些文章
    iOS組件化思路-大神博客研讀和思考
    iOS組件化方案
    iOS APP組件化開發(fā)實(shí)踐
    我們這一塊的方案還未確定,后續(xù)確定了再做相應(yīng)的討論

  • 使用jazzy為內(nèi)部庫添文檔說明
    jazzy是Realm開源的一個(gè)使用ruby編寫的插件,配合xcode7以后支持的markdown注釋功能,可生成類似于蘋果官方文檔樣式的代碼文檔 Jazzy

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容