iOS中關(guān)于模塊化開發(fā)解決方案(純干貨)
關(guān)于iOS模塊化開發(fā)解決方案網(wǎng)上也有一些介紹,但真正落實在在具體的實例卻很少看到,計劃編寫系統(tǒng)文章來介紹關(guān)于我對模塊化解決方案的理解,里面會有包含到一些關(guān)于解耦、路由、封裝、私有Pod管理等內(nèi)容;并編寫的一個實例項目放在git進行開源[jiaModuleDemo],里面現(xiàn)在已經(jīng)放著一些封裝的功能模塊;會不斷的進行更新,假如你感興趣可以Star一下,項目也不斷的更新完善優(yōu)化;如果你有更好的方案或者說好的建議可以lssues,我會在短時間進行更新并修改相應(yīng)的問題;
一:項目中存在的問題
1:當公司里面有多個項目同時進行,并且有可能是多個人分別不同項目時,就會存在如上圖出現(xiàn)的情況,其實每個APP中都是有很多共同的模塊,當然有可能你會把相同功能模塊代碼復制一份在新項目中,但這其實并不是最好的方式,在后期不斷迭代過程中,不同的人會往里面增加很多帶有個人色彩的代碼;這樣就像相同的模塊項目后期對于多個項目統(tǒng)一管理也是災難性,有可能會失控,哪怕項目轉(zhuǎn)移別人接手也會無形中浪費很多時間,增加維護成本,所以實例中更注重對于一些相同模塊進行提取,求同存異;而模塊化結(jié)合私有Pods進行管理,對于常用功能的封裝,只要開放出一些簡單開關(guān)配置方式,就可以實現(xiàn)一個功能,比如日志記錄、網(wǎng)絡(luò)請求模塊、網(wǎng)絡(luò)狀態(tài)變化提示等;

2:對于頁面之間相互耦合,而頁面之間的傳參也各不相同,由于不同的開發(fā)人員或者簡便方式等原因,傳參的類型都有差異,包含如實體、簡單基本類型等,先前項目對于路由方式也不支持,導致要實現(xiàn)收到消息推送進行不同的頁面跳轉(zhuǎn)存在硬編碼情況,對于功能擴展存在相當大的問題;而右邊則是模塊化后頁面之間的交互方式;頁面之間也不存在耦合關(guān)系,都只跟JiaMediator這個中介者相依賴;而傳參都統(tǒng)一成以字典的形式;雖然可能犧牲一些方便跟隨意,卻可以解耦模塊化;并且加入對路由方式的處理;約定好相關(guān)的協(xié)議進行交互;用這種路由方式代替那些第三方的路由插件則是因為它的靈活性,最主要還是省去了第三方路由插件在啟動時要注冊路由的問題;

二:解決方案實現(xiàn)之模塊化
1:JiaCore(基礎(chǔ)功能封裝)
JiaCore是整個APP最基礎(chǔ)模塊,所有的模塊化都要依賴,主要包含一些全局的功能模塊,比如JiaBaseViewController、JiaAppDelegate等;目前已經(jīng)把一些默認的功能進行集成在里面,包含網(wǎng)絡(luò)狀態(tài)變化判斷及提示、日志記錄功能等;并把一些相關(guān)配置的內(nèi)容用JiaCoreConfigManager這個管理類進行統(tǒng)一設(shè)置,比如是否打開日志記錄功能;JiaCoreConfigManager類則是開放給具體APP設(shè)置全局的相關(guān)配置;下面就以其中一個日志記錄功能進行講解:
//JiaCore基礎(chǔ)模塊相關(guān)配置 JiaCoreConfigManager *jiaCoreConfig=[JiaCoreConfigManager sharedInstance]; jiaCoreConfig.recordlogger=YES; 然后具體APP的PrefixHeader.pch引入命名空間并進行設(shè)置記錄日志的等級: #import "JiaCocoaLumberjack.h" //DDLog等級 static const int ddLogLevel = DDLogLevelVerbose;
這樣就完成的一個APP對于日志記錄模塊的引入,JiaCore已經(jīng)幫你完成的關(guān)于日志記錄的相關(guān)配置,并且錯誤內(nèi)容以一種可讀性較好的格式記錄到file文件中,而且這些file文件生成規(guī)則也都定義好了,當然如何時你要是在Xcode控制臺顯示不同等級色彩,只要安裝XcodeColors插件并簡單進行設(shè)置就可以了,對于不同等級不同色彩都已經(jīng)在JiaCore配置完成;
2:JSPatch熱更新功能
在JiaCore里面也默認集成了熱更新的功能,只要傳入簡單的對象數(shù)組就會啟動熱更新;其中JiaPathchModel已經(jīng)是定義好的模型,在APP中把接口請求轉(zhuǎn)化成模型數(shù)組,其中patchId是唯一值名稱、md5則是JS文件的MD5值、url是JS的下載路徑、ver則是對哪個版本起作用;因為一般我們在外面的APP都是多版本共存,熱更新也要進行版本區(qū)分,只下載與本版本相對應(yīng)的熱更新JS文件加載;而MD5值則是為了增加安全性,避免JS文件被別人進行修改而影響APP的運行,在JiaCore會對下載后的JS文件進行MD5計算并比較;對于沒有在jSPatchMutableArray以前的JS文件會被刪除;
//熱更新內(nèi)容 JiaPathchModel *sample=[[JiaPathchModel alloc]init]; sample.patchId = @"patchId_sample1"; sample.md5 = @"2cf1c6f6c5632dc21224bf42c698706b"; sample.url = @"http://test.qshmall.net:9090/demo1.js"; sample.ver = @"1"; JiaPathchModel *sample1=[[JiaPathchModel alloc]init]; sample1.patchId = @"patchId_sample2"; sample1.md5 = @"e8a4eaeadce5a4598fb9a868e09c75fd"; sample1.url = @"http://test.qshmall.net:9090/demo2.js"; sample1.ver = @"1"; //JiaCore基礎(chǔ)模塊相關(guān)配置 JiaCoreConfigManager *jiaCoreConfig=[JiaCoreConfigManager sharedInstance]; jiaCoreConfig.jSPatchMutableArray=[@[sample,sample1] mutableCopy];
3:JiaGT模塊(個推封裝)
消息推送對于一個APP是相當重要性,一般是采用第三方的SDK進行集成,其實大部分的SDK處理代碼都是差不多,在這實例中對差異化的內(nèi)容進行提取,實例中將以個推進行模塊化,因為消息推送的大部分代碼都集中在AppDelegate中,造成的一大堆雜亂代碼,當然也有一部分人對AppDelegate進行擴展分類進行移除代碼,實例中將采用另外一種解決方案進行抽取,可以達到完全解耦,在具體的APP里面將不會再出現(xiàn)個推SDK相關(guān)內(nèi)容,只要簡單進行配置跟處理消息就可以,下面只是簡單的列出部分代碼,其它封裝代碼見源代碼;
//設(shè)置個推模塊的配置
jiaGTConfigManager *gtConfig=[jiaGTConfigManager sharedInstance];
gtConfig.jiaGTAppId=@"0uuwznWonIANoK07JeRWgAs";
gtConfig.jiaGTAppKey=@"26LeO4stbrA7TeyMUJdXlx3";
gtConfig.jiaGTAppSecret=@"2282vl0IwZd9KL3ZpDyoUL7";
#pragma mark 消息推送相關(guān)處理
/**
* @author wujunyang, 16-07-07 16:07:25
*
* @brief 處理個推消息
*
* @param NotificationMessage
*/
-(void)gtNotification:(NSDictionary *)NotificationMessage
{
NSLog(@"%@",NotificationMessage[@"payload"]);
NSLog(@"-----接收到個推通知------");
}
/**
* @author wujunyang, 16-07-07 16:07:40
*
* @brief 處理遠程蘋果通知
*
* @param RemoteNotificationMessage
*/
-(void)receiveRemoteNotification:(NSDictionary *)RemoteNotificationMessage
{
NSLog(@"%@",RemoteNotificationMessage[@"message"]);
NSLog(@"-----接收到蘋果通知------");
}
/**
* @author wujunyang, 16-09-21 14:09:33
*
* @brief 獲得注冊成功時的deviceToken 可以在里面做一些綁定操作
*
* @param deviceToken <#deviceToken description#>
*/
-(void)receiveDeviceToken:(NSString *)deviceToken
{
NSLog(@"-----當前deviceToken:%@------",deviceToken);
}

4:JiaAnalytics模塊(友盟統(tǒng)計封裝)
JiaAnalytics模塊是在友盟統(tǒng)計SDK跟Aspect相結(jié)合基礎(chǔ)上完成,對于頁面的進出統(tǒng)計采用Aop切面方式進行,把原本應(yīng)該在每個頁面生命周期的統(tǒng)計代碼移除,App運用只要簡單配置友盟相對應(yīng)的信息,也可以設(shè)置要統(tǒng)計頁面的過濾條件,目前已經(jīng)有三種如要統(tǒng)計的開頭頁面的前綴字符串數(shù)組、要統(tǒng)計的頁面名稱字符串數(shù)組、不統(tǒng)計的頁面名稱字符串數(shù)組;可以結(jié)合使用,達到精確統(tǒng)計頁面的目的;而且把統(tǒng)計的代碼放在異步線程進行,不會影響主線程的響應(yīng);
__weak typeof(self) ws = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, BOOL animated){
UIViewController *controller = [info instance];
BOOL filterResult=[ws fileterWithControllerName:NSStringFromClass([controller class])];
if (filterResult) {
[ws beginLogPageView:[controller class]];
}
} error:NULL];
[UIViewController aspect_hookSelector:@selector(viewWillDisappear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, BOOL animated){
UIViewController *controller = [info instance];
BOOL filterResult=[ws fileterWithControllerName:NSStringFromClass([controller class])];
if (filterResult) {
[ws endLogPageView:[controller class]];
}
} error:NULL];
});

三:解決方案實現(xiàn)之頁面解耦
JiaMediator起到一個中介的作用,所有的模塊間響應(yīng)交互都是通過它進行,每個模塊都會對它進行擴展分類(例如:JiaMediator+模塊A),分類主要是為了用于本地間調(diào)用而又不想用路由的方式,若要用路由的方式則要注意關(guān)于路由約束準確編寫,它將會直接影響到能否正確響應(yīng)到目標;實例中也有關(guān)于使用通知的方式進行回調(diào)參數(shù)的回傳問題;

實例代碼如下:
NSDictionary *curParams=@{kDesignerModuleActionsDictionaryKeyName:@"wujunyang",kDesignerModuleActionsDictionaryKeyID:@"1001",kDesignerModuleActionsDictionaryKeyImage:@"designerImage"};
switch (indexPath.row) {
case 0:
{
UIViewController *viewController=[[JiaMediator sharedInstance]JiaMediator_Designer_viewControllerForDetail:curParams];
[self presentViewController:viewController animated:YES completion:nil];
break;
}
case 1:
{
UIViewController *viewController=[[JiaMediator sharedInstance]JiaMediator_Designer_viewControllerForDetail:curParams];
[self.navigationController pushViewController:viewController animated:YES];
break;
}
case 2:
{
NSString *curRoue=@"jiaScheme://Designer/nativeFetchDetailViewController?name=wujunyang&ID=1001&image=designerImage";
UIViewController *viewController=[[JiaMediator sharedInstance]performActionWithUrl:[NSURL URLWithString:curRoue] completion:^(NSDictionary *info) {
}];
[self.navigationController pushViewController:viewController animated:YES];
break;
}
case 3:
{
NSDictionary *userParaDictionary=@{kUserModuleActionsDictionaryKeyID:@"1"};
UIViewController *viewController=[[JiaMediator sharedInstance] JiaMediator_User_viewControllerForDetail:userParaDictionary];
[self.navigationController pushViewController:viewController animated:YES];
break;
}
default:
break;
}
四:模塊化結(jié)合私有Pods方案
實例中只是把相關(guān)模塊化的提取都在一個工程進行體現(xiàn),最后還是要落實結(jié)合Pods進行管理,把每個模塊分開管理,不同的APP可以簡單通過Pods指令就可以達到引入模塊的效果,對于一些相同模塊可以在不同的APP重復引用,減小重復開發(fā)成本;

以上所述是小編給大家介紹的iOS中關(guān)于模塊化開發(fā)解決方案(純干貨),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
iOS UIScrollView滾動視圖/無限循環(huán)滾動/自動滾動的實例代碼
這篇文章主要介紹了iOS UIScrollView滾動視圖/無限循環(huán)滾動/自動滾動,需要的朋友可以參考下2017-02-02
IOS 屏幕適配方案實現(xiàn)縮放window的示例代碼
這篇文章主要介紹了IOS 屏幕適配方案實現(xiàn)縮放window的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04
iOS 解決UICollectionView 計算 Cell 大小的問題
本文主要介紹iOS UICollectionView,這里給大家一個實例代碼作為參考,并指出經(jīng)常遇到的問題和解決辦法,希望能幫助有需要的小伙伴2016-07-07

