iOS的組件化思路分享

Cocoapods

iOS的組件化,一直都是一個理念,很多大公司一直在強調卻沒有具體可行的或簡單可行的方案。所以分享下大概思路。

框架思路

框架說明:

  1. 繼續集成:一個主工程(殼工程),包含所有的內容(整個項目),用于發包或打包測試。
  2. 基礎組件: 不依賴其他任何組件,獨立完成功能。主要有:*與業務無法的功能(如string或data的加密,category的封裝) *對第三方庫的封裝(如AFNetworking,SDWebImage的封裝)
  3. 業務公用組件:依賴基礎組件或UIKit等系統組件,創建業務共同使用的功能(如分享,支付,網絡訪問)
  4. 中間組件:連接業務公用組件和業務組件,及業務組件之間的互相調用。(如Mediator的組件)
  5. 業務組件:單獨的業務功能,不依賴其他業務組件。

大致的框架思路,其中會碰到一個顆粒度的問題,將顆粒度設定過大,則組件之間的耦合性過大,顆粒度設定過小,則將產生很多的組件和中間組件,組件的管理成本過大。(具體問題需要具體分析)

一、 Cocoapods的組件管理

1. pod spec文件創建

spec文件說明

Pod::Spec.new do |spec| 
  spec.name = 'Reachability' spec.version = '3.1.0'
  spec.license = { :type => 'BSD' } 
  spec.homepage = 'https://github.com/tonymillion/Reachability' 
  spec.authors = { 'Tony Million' => 'tonymillion@gmail.com' } 
  spec.summary = 'ARC and GCD Compatible Reachability Class for iOS and OS X.' 
  spec.source = { :git => 'https://github.com/tonymillion/Reachability.git', :tag => 'v3.1.0' } 
  spec.source_files = 'Reachability.{h,m}' 
  spec.framework = 'SystemConfiguration'
end

根據內容編寫項目的信息,小的細節可以學習Git上支持Cocoapods的項目,查看項目中*.spec文件。
如:
(1). 添加資源文件使用 s.resource_bundles
(2). 需要添加多個文件,使用數組s.source_files = ['ThirdSdk/Classes/**/*.{h,m}', 'ThirdSdk/Classes/**/**/*.{h,m}']

2. LICENSE和README.md文件

編寫許可證和說明的文件。最好參考官方的寫法,不然檢測不過。

3.創建一個Example工程

關鍵一個Example的工程,在Podfile中采用直接引用Module文件進行組件的開發。方便對單個組件進行調試
pod 'ThirdSdk', :path => '../'

組件直接引用

可以直接對Module進行修改,添加,刪除等。

4.本地資料庫(cocoapods鏡像)創建

新建一個Git庫,取名Specs用來保存本地的所有私有庫。
使用pod repo add [name] [Git地址] 添加一個私有Cocoapods Specs鏡像。
使用pod repo push REPO [NAME.podspec] 將私有組件推送到私有第三方庫鏡像。

5.依賴第三方庫

在Podfile文件中可以添加依賴的第三方庫,包括Cocoapods和私有本地的庫。

platform :ios, '7.0'

source 'git@code.*.com:ios/Specs.git'

source 'https://github.com/CocoaPods/Specs.git'

target 'ThirdSdk_Example' do
  pod 'ThirdSdk', :path => '../'
  
  #cocoapods specs
  pod 'AliyunOSSiOS'
  
  #local specs
  pod 'WebService'
end

其中
(1)source 'git@code.*.com:ios/Specs.git'是申請引用本地指定的資料地址。
(2)source 'https://github.com/CocoaPods/Specs.git'是cocoapods官方的第三方庫資料地址。
(3)pod 'AliyunOSSiOS'引用的是Cocoapods的第三方庫。
(4)pod 'WebService'引用的是私有的第三方庫。

二、組件之間的依賴

(1)對于基礎組件的依賴,采用Pod 直接導致,在代碼中進行import頭文件。這個方式會導致基礎組件的耦合性比較高。可以將組件的顆粒度設定的小一些,盡量抽取出不依賴其他組件的獨立組件。再將這些獨立的組件打包成一個較大的組件,方便管理。如Podfile中

#local specs
  pod 'Account'
  pod 'AppPod'
  pod 'WebService'

其中的AppPod就是集合,包含很多獨立的基礎組件。

(2)如果是業務組件,在不同的業務之間,一定要避免組件間耦合,采用Mediator組件作為中間件,降低耦合。 具體可以參考iOS應用架構談 開篇 提供的Demo編寫。

三、主工程

主工程(殼工程)引用所有的組件,并實現應用的工程。

iOS應用框架及Cocoapods內容的參考:
http://casatwy.com/iosying-yong-jia-gou-tan-kai-pian.html
http://www.cocoachina.com/ios/20160108/14916.html
http://gracelancy.com/blog/2016/01/06/ape-ios-arch-design/
http://blog.csdn.net/xyxjn/article/details/42527341
http://pingguohe.net/2015/11/24/Navigator-and-Rewrite.html
http://studentdeng.github.io/blog/2013/09/13/cocoapods-tutorial/
http://ishalou.com/blog/2012/10/16/how-to-create-a-cocoapods-spec-file/
http://stackoverflow.com/questions/25470415/multiple-private-repo-cocoapods
http://www.cnblogs.com/Mr-ios/p/5310666.html

補充

1. 使用pod install 更新失敗的處理

在使用Cocoapods管理代碼的時候,如果在GitHut上更新了代碼,希望在project中執行pod install 來刷新代碼,那么需要
刪除Pods和Podfile.lock文件
然后在執行pod install 來刷新代碼

使用pod update進行repo的更新

多次安裝pod install 會出現Build Phase里多個Check Pods Manifest.lock的選項,這些選項可以刪除。


刪除這個目錄下的緩存文件,進行第三方庫文件的重新獲取
/Users/*/Library/Caches/CocoaPods/Pods/*

因為有可能因為版本號,沒有增加,在Pods目錄下的VERSION保存了這個版本的第三方庫,那么將直接返回已經緩存的文件。所以當私有組件有代碼修改后,則可以清空這個文件夾下的目錄,來進行私有組件的更新。??????(研究了很久才發現這個終極解決方法)


2. Pod中生成文件夾形式或創建子組件

在*.podspec文件中,根據功能創建不同的子組件,如AFN中的

s.subspec 'Security' do |ss|

ss.source_files = 'AFNetworking/AFSecurityPolicy.{h,m}'

ss.public_header_files = 'AFNetworking/AFSecurityPolicy.h'

ss.frameworks = 'Security'

end

生成的效果


文件夾

這種方式也是子組件的方法,在pod中可以直接引用AFSecurityPolicy的組件,如pod 'AFNetworking/Security' 此時僅僅引用的是Security組件,而不是整個AFN組件

單個Security組件

在開發是看到的組件,文件夾會比較亂,當其他組件pod后,看到的文件夾就如AFNetworking一樣的效果。


3. *.podspec的dependency使用

組件可以依賴其他的第三方庫,如s.dependency 'JTObjectMapping'AFNetworking等。
同時也可以依賴私有的第三方庫
(1)使用pod repo add [name] [Git地址] 添加一個私有Cocoapods Specs鏡像。
(2)使用pod repo push REPO [NAME.podspec] 將私有組件推送到私有第三方庫鏡像。
(3)在組件中就可以將已經上傳的組件可以用來依賴了。

4. ThirdSdk組件

在ThirdSdk組件中,不能包含業務或項目相關的類,不然會導致互相依賴。僅僅包含第三方庫的代碼,可以降低耦合性。
如果能用Cocoapods管理的第三方庫,盡量使用Cocoapods吧。

5. 加載靜態庫和framework
ss.vendored_frameworks = 'ThirdSdk/AlipaySDK/AlipaySDK.framework'
ss.vendored_libraries = 'ThirdSdk/AlipaySDK/libcrypto.a', 'ThirdSdk/AlipaySDK/libssl.a'

a文件不是一個source_files。

6. runtime不能返回Bool等基礎數據類型
return [target performSelector:action withObject:params];

如果返回的值是Bool或其他的基礎數據類型,那么將會Crash

需要將返回值設置為NSObject對象類型或nil。

7. 組件中獲取xib,png等resource時,bundle重新設置
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    NSString *bundlePath = [bundle pathForResource:@"StoreLocation" ofType:@"bundle"];
    if (bundlePath) {
        bundle = [NSBundle bundleWithPath:bundlePath];
    }

其中StoreLocation是與podSpec中的s.resource_bundles里設置的保持一致。

s.resource_bundles = {
    'StoreLocation' => ['StoreLocation/Assets/**/*', 'StoreLocation/Classes/Address/*.{xib}'']
  }
8. 圖片和URL的處理

1、每一個組件需要用到的圖片,都放到組件的Assets文件夾中。
在iOS8及更高系統,組件中Xib訪問image會從組件的Assets中讀取。但是iOS7系統,Xib還是從[NSBundle mainBundle]的Assets中讀取文件。
2、URL 不單獨放到一個頭文件中,放入組件的ViewModel中。

9. TAG的刷新

當有多人合作開發,TAG又一直維持在1.0.0時,那么需要每次更新所有的TAG。

更新標簽

需要選中“抓取并在本地存儲所有標簽”。

10. push 組件 *.podspec 文件
pod repo push Specs AppPod.podspec --allow-warnings

推送時,如果一直報錯,修護了Error還是一直報同樣的Error,這時可以執行下pod cache clean --all 清空緩存。

11. 不同configurations設置宏

當添加一個Ad-hoc的configuration,這個也需要設置DEBUG這個宏,并且在組件中也用到了DEBUG的宏定義,那么需要對所有的組件進行DEBUG的設置。方法如下

#設置Debug參數
post_install do |installer|

  installer.pods_project.targets.each do |target|

    target.build_configurations.each do |config|

      if config.name == 'Debug' 
       config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) DEBUG=1'
      
      end
      
      if config.name == 'Ad-hoc' 
       config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) DEBUG=1'
      
      end

end

end

end
#設置參數完成
12. 更新OpenSSL失敗

OpenSSL更新失敗,可以刪除/tmp/openssl 文件夾,重新嘗試。

13. OpenSSL 的使用

在使用支付寶SDK時,會有引入OpenSSL文件夾,其中文件中使用#include <openssl/e_os2.h>。將SDK放入組件中,會報錯,提示找不到文件。
解決方法: 在*.podspec文件中 添加 ss.header_dir = "openssl"用來添加頭文件夾openssl。

14. ARC 和 MRC 兼容

將使用MAC的放入subspec

s.subspec 'JTObjectMapping' do |ss|
    ss.source_files = 'StoreBase/Classes/JTObjectMapping/*.{h,m}'
    ss.public_header_files = 'StoreBase/Classes/JTObjectMapping/*.h'

    ss.requires_arc = false
  end

因為JTObjectMapping第三方庫使用的MRC所以特殊聲明ss.requires_arc = false. 在主spec中使用的ARC,則聲明ss.requires_arc = true.
此時主spec是ARC而JTObjectMapping是MRC,對JTObjectMapping中的文件都會添加一個-fno-objc-arc的編譯宏。

15. 基礎組件的版本更新

當主工程發布了版本,那么依賴的基礎組件版本也固定,不能再做修改。當需要對基礎組件進行修改,那么版本需要更新,版本號加一。
當某個基礎組件A的版本號修改,那么依賴這個組件的所有組件BCD也都要修改版本。然后依賴BCD的組件EFG也要修改版本號,以此類推

16. 發布版本修改

發布版本修改:
基礎組件,將基礎組件的所有依賴修改為大版本(如“=1.1.0”格式),必須指定版本號。比如有一個組件的版本號是1.0.5,那么這個版本進行了1.0.1到1.0.5的開發,在提交版本的需要修改1.1.0版本,并執行1.1.0版本。為了,(1)方便線上版本可以指定具體的版本。 (2)方便下一個版本的開發。
殼工程中,pod file文件的修改,將業務組件制定dev分支,修改為制定版本號,必須指定版本號。

業務組件的使用:
開發時,podfile中直接dev分支獲取代碼,并在dev上進行開發。
等需要發布版本進行冒煙測試時,修改podfile為制定版本號。如“=1.1.0”。

pod 'Location', :git => 'git@code.xxx.com:ios/Location.git', :branch => 'dev'

基礎組件的使用
在每個基礎組件中,使用“~>1.0.3”進行開發,并在每次修改的時候,添加版本號。
基礎組件依賴其他組件的時候,也修改為“~>1.0.3”, 那么在pod install的時候,就可以更新到最新的版本,不需要清理緩存。
發布版本的時候,在每一個組件中,指定版本號。如“=1.1.0”。保存每個發布的正式版本,都可以明確的知道所有組件的版本號。
發布版本后進行開發,那么需要修改依賴版本后“~>1.1.0”,不需要清理緩存,方便開發。
開發時,版本號的修改,僅僅針對需要修改的版本號。其他版本號保持不變。

圖片資源
創建一個圖片資源的pod, 使用s.resources的方式進行添加圖片。那么在xib中或imageNamed:"""直接可進行訪問。不需要進行bundle的控制。??????

未完待續
// END

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 項目組件化、平臺化是技術公司的共同目標,越來越多的技術公司推崇使用pod管理第三方庫以及私有組件,一方面使項目架構...
    swu_luo閱讀 22,217評論 0 39
  • 最近在學習vue.js的時候發現,vue的組件化的思想對于編寫代碼是一個非常有用的事情。 首先為什么需要組件化? ...
    拂曉的云閱讀 7,232評論 6 23
  • iOS組件化開發1 · 什么是組件化 組件化開發2 · 組件開發必備技能 這篇文章的主要內容list1、學會使用g...
    Biharry閱讀 8,899評論 2 114
  • 楊柳邀風近,江南四月春。碧波推影渡舟深,岸頭羇旅人。 鶯語繞花九轉,煙路分江各半。停船欲系且同登,把酒話故城。
    長安舊人閱讀 260評論 2 14
  • 成都的天雨蒙蒙。 昨晚和一個久不見面的朋友吃飯,初時有些拘謹,直到說些舊事,一下又打開了話匣。讓我想起友人的一句話...
    九零后空巢老人閱讀 168評論 0 0