概述
近一年iOS業界討論元件化方案甚多,大體來說有3種。
-
Protocol註冊方案
-
URL註冊方案
-
Target-Action runtime呼叫方案
URL註冊方案據我瞭解很多大公司都在採用,蘑菇街 App 的元件化之路蘑菇街的Limboy在這篇部落格中做了很詳盡的闡述
Target-Action runtime呼叫方案Casa在 iOS應用架構談 元件化方案中也做了很詳盡的描述,前陣時間Casa開了一篇部落格在現有工程中實施基於CTMediator的元件化方案清楚講述瞭如何用這套方案實施元件化
Protocol方案我尚未看到有人做過詳盡的分享,也許是我孤陋寡聞,不過在這裡,我會教大家用Protocol方案實施元件化,不僅如此..
我會採用以上3種方案詳盡的實現3個Demo,Demo會在文尾給到,本文不過多闡述3種方案的優劣,我會在最後做一個總結,希望給想了解元件化方案的同學或者給在專案中準備實施元件化方案的同學提供一個借鑑。
業務模擬場景
首頁
展示商品列表商品詳情頁
展示商品的詳細資訊確認訂單頁
生成訂單
把業務連貫起來 點選首頁
中A商品,進入A商品的商品詳情頁
,點選商品詳情頁
中的 立即購買 進入確認訂單頁
,點選確認訂單頁
中的提交訂單 會返回到商品詳情頁
,並且在商品詳情頁
中告知使用者下單成功.
真實業務場景下確認訂單頁
點提交訂單 是不會回到商品詳情頁
的,模擬這個場景是想在Demo中實現2個模組中反向回撥。
一、Protocol註冊方案
正式實施前先奉上Demo,建議只下一個主專案就可以了(注:下載完不需要pod install或者pod update,pods在我私有源上 我沒有填寫.gitignore檔案,下載完都是可以直接跑的)
1.基本準備工作
- 先去gitHub建立一個專案存放私有Repo源,repo地址
https://github.com/sun6boys/CRRepositories.git
後面3種方案私有pod源都會放在這裡。 - 本地新增私有源 終端執行命令
pod repo add CRRepositories https://github.com/sun6boys/CRRepositories.git
(如果之前並未向gitHub push過檔案也沒有把SSH公鑰儲存到gitHub,這時候應該會提示你輸入gitHub賬號密碼) - 以上操作完成
cd ~/.cocoapods/repos
目錄下至少會有2個資料夾 *CRRepositories 和 master, master檔案下面存放的是公有原始檔,CRRepositories目錄下目前是空的,後面會存放我們私有原始檔 - 基本準備工作完成。
2.Xcode建立專案[CRProtocolManager]
CRProtocolManager和MGJRouter、CTMediator一樣屬於模組之間排程的中介軟體
在CRProtocolManager專案下建立名為CRProtocolManager的資料夾,後面我們需要做成私有pod的檔案均放在該資料夾下。
建立CRProtocolManager類(.h,.m),定義2個對外介面
1 2 3 4 5 6 7 |
@interface CRProtocolManager : NSObject + (void)registServiceProvide:(id)provide forProtocol:(Protocol*)protocol; + (id)serviceProvideForProtocol:(Protocol *)protocol; @end |
具體方法實現很簡單可以參看Demo,我這裡只是簡單處理。
接下來就是要把專案提交到gitHub,做私有pod了
- gitHub新建一個project名為CRProtocolManager
- 終端cd至CRProtocolManager專案目錄下執行命令
git remote add origin https://github.com/sun6boys/CRProtocolManager.git
- 因cocoaPods強制新增開源許可檔案執行命令
echo MIT>FILE_LICENSE
建立名為FILE_LICENSE的檔案 - 終端cd至CRProtocolManager目錄下執行命令
pod spec create CRProtocolManager
- 執行命令
vim .CRProtocolManager.podspec
編輯podspec檔案,具體如何編輯可參看Demo中的podspec檔案或者google - 退出編輯執行命令
git add .
- `git commit -m ‘log’
git tag 0.0.1
tag一定要和podspec中的version一致git push origin master --tags
–tags為了把剛才新增的tag提交上去- 執行命令
pod repo push CRRepositories CRProtocolManager.podspec --verbose --allow-warnings
注:CRRepositories即為準備工作中的私有源倉庫 - 成功後
pod search CRProtocolManager
應該就能搜尋到了
萬里長征終於走完第一步,基礎設施已經構建完畢
3.商品詳情業務模組
既然元件化了,那我們所有的業務模組都是單獨的project,但是這裡我會分2個project,一個是商品詳情業務入口模組,一個是商品詳情業務模組。業務入口模組即是定義該模組對外提供業務介面的protocol
,如果A模組需要呼叫到B模組,那A模組只需要引入CRProtocolManager
和B模組的protocol
,而不是引入整個B模組。
新建一個projectCRGoodsDetailServiceProtocol
,建立一個和專案名一樣的protocol檔案,定義介面如下
1 2 3 4 5 6 7 |
@protocol CRGoodsDetailServiceProtocol @required; - (UIViewController *)goodsDetailViewControllerWithGoodsId:(NSString*)goodsId goodsName:(NSString *)goodsName; @end |
參照CRProtocolManager做成私有pod
以上實施完畢,新建一個projectCRGoodsDetail
,新建2個類
1 2 |
CRGoodsDetailServiceProvide CRGoodsDetailViewController |
CRGoodsDetailServiceProvide
即是CRGoodsDetailServiceProtocol
的實現者 所以他依賴
CRGoodsDetailServiceProtocol
,因為商品詳情模組需要跳轉到訂單確認頁,所以他也依賴CRProtocolManager
。
新增Podfile檔案編輯如下
1 2 3 4 5 6 7 8 9 |
source 'https://github.com/sun6boys/CRRepositories.git' source 'https://github.com/CocoaPods/Specs.git' target 'CRGoodsDetail' do pod "CRProtocolManager" pod "CRGoodsDetailServiceProtocol" end |
執行pod install --verbose --no-repo-update
最終CRGoodsDetailServiceProvide
實現程式碼如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#import "CRGoodsDetailServiceProvide.h" #import #import #import "CRGoodsDetailViewController.h" @interface CRGoodsDetailServiceProvide() @end @implementation CRGoodsDetailServiceProvide + (void)load { [CRProtocolManager registServiceProvide:[[self alloc] init] forProtocol:@protocol(CRGoodsDetailServiceProtocol)]; } - (UIViewController *)goodsDetailViewControllerWithGoodsId:(NSString*)goodsId goodsName:(NSString *)goodsName { CRGoodsDetailViewController *goodsDetailVC = [[CRGoodsDetailViewController alloc] initWithGoodsId:goodsId goodsName:goodsName]; return goodsDetailVC; } @end |
CRGoodsDetailViewController
實現程式碼如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
#import "CRGoodsDetailViewController.h" @interface CRGoodsDetailViewController () @property (nonatomic, copy) NSString *goodsId; @property (nonatomic, copy) NSString *goodsName; @property (nonatomic, strong) UILabel *statusLabel; @property (nonatomic, strong) UIButton *buyButton; @end @implementation CRGoodsDetailViewController - (instancetype)initWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName { self = [super init]; if (self) { _goodsId = goodsId; _goodsName = goodsName; } return self; } - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.title = self.title; [self.view addSubview:self.statusLabel]; [self.view addSubview:self.buyButton]; } - (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; self.statusLabel.frame = CGRectMake(0, 0, 100, 20); self.statusLabel.center = self.view.center; self.buyButton.frame = CGRectMake(0, self.view.frame.size.height - 45, self.view.frame.size.width, 45); } #pragma mark - event - (void)didClickBuyButton:(UIButton *)button { } #pragma mark - getters - (UILabel *)statusLabel { if (_statusLabel == nil) { _statusLabel = [[UILabel alloc] init]; _statusLabel.textColor = [UIColor redColor]; _statusLabel.font = [UIFont systemFontOfSize:15.f]; _statusLabel.textAlignment = NSTextAlignmentCenter; _statusLabel.text = @"暫未購買"; } return _statusLabel; } - (UIButton *)buyButton { if (_buyButton == nil) { _buyButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_buyButton setTitle:@"立即購買" forState:UIControlStateNormal]; [_buyButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [_buyButton setBackgroundColor:[UIColor redColor]]; [_buyButton addTarget:self action:@selector(didClickBuyButton:) forControlEvents:UIControlEventTouchUpInside]; } return _buyButton; } @end |
把CRGoodsDetail
做成私有pod 記得編輯podspec檔案的時候新增dependencyCRProtocolManager
CRGoodsDetailServiceProtocol
4.新建主專案MainProject
為了少建一個專案首頁
模組我是直接放在主專案中的,按理首頁
也應該是一個獨立的pod.
首頁
業務場景是,顯示商品列表,點選某個商品進入該商品詳情頁. 所以他依賴CRGoodsDetailServiceProtocol
和CRProtocolManager
因為首頁模組即是主專案所以他還得依賴CRGoodsDetail
最終首頁核心程式碼如下
1 2 3 4 5 6 7 8 |
#pragma mark - event - (void)didClickGoodsButton:(UIButton *)button { id goodsServiceProvide = [CRProtocolManager serviceProvideForProtocol:@protocol(CRGoodsDetailServiceProtocol)]; UIViewController *goodsDetailVC = [goodsServiceProvide goodsDetailViewControllerWithGoodsId:@"123" goodsName:@"農夫山泉礦泉水"]; [self.navigationController pushViewController:goodsDetailVC animated:YES]; } |
5.確認訂單模組
參照商品詳情
新建確認訂單業務入口pod 以及確認訂單業務pod.和商品詳情
有區別的是,提交訂單完成後要回到商品詳情
並且通知商品詳情
使用者已經購買,所以CRConfirmOrderServiceProtocol
介面定義如下
1 2 3 4 5 |
@protocol CRConfirmOrderServiceProtocol - (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId sureComplete:(dispatch_block_t)sureComplete; @end |
最後記得在商品詳情
加上跳轉並且podspec裡面加上dependency