相關(guān)文章:iOS開(kāi)源:模塊通信YLBModule
稀土掘金:iOS架構(gòu):從開(kāi)發(fā)到跨端架構(gòu)師,實(shí)現(xiàn)iOS開(kāi)發(fā)對(duì)Android進(jìn)行跨端開(kāi)發(fā),一套代碼多端使用。
本文架構(gòu)由作者本人經(jīng)過(guò)多年對(duì)架構(gòu)的探索和理解,從理論依據(jù)到源代碼實(shí)現(xiàn),并在開(kāi)發(fā)項(xiàng)目的實(shí)際應(yīng)用中逐步完善,形成了一套對(duì)項(xiàng)目的組件化,模塊的獨(dú)立化,并且完全實(shí)現(xiàn)了插件化(熱更新方案)的架構(gòu)方案。特別是插件化(熱更新)方案,會(huì)讓人眼前一亮,可以使iOS開(kāi)發(fā)迅速轉(zhuǎn)變?yōu)榭缍碎_(kāi)發(fā),對(duì)移動(dòng)端進(jìn)行統(tǒng)一。
YLBDesign開(kāi)源架構(gòu)
YLBDesign項(xiàng)目:https://github.com/ProBobo/YLBDesign
為什么開(kāi)源
在作者看來(lái),iOS架構(gòu)因?yàn)橘Y料少,所以大大增加了開(kāi)發(fā)的掌握成本,所以普及率也不高。然而一些大型的項(xiàng)目或多或少需要解決一些代碼復(fù)用和模塊獨(dú)立的問(wèn)題。為了提升開(kāi)發(fā)效率,架構(gòu)在一些項(xiàng)目發(fā)展的后期就顯得非常重要。因此,開(kāi)源架構(gòu)就有其現(xiàn)實(shí)意義。
對(duì)iOS架構(gòu),作者也進(jìn)行了自己的思考:因?yàn)?code>架構(gòu)不是一種單一的技術(shù)或者組件,而是一系列組件、模塊、資源、理論的綜合體。因?yàn)樾枰褬I(yè)務(wù)代碼當(dāng)作一個(gè)完整的管理對(duì)象,所以架構(gòu)更像是一個(gè)代碼和資源的管理系統(tǒng)。所以很多時(shí)候一些開(kāi)源庫(kù)只是實(shí)現(xiàn)了架構(gòu)中的一部分功能,而不是全部?jī)?nèi)容。
前言
在此作者提供了iOS架構(gòu)開(kāi)源代碼。讓希望能掌握iOS架構(gòu)的人少奮斗幾年,迅速掌握,并實(shí)現(xiàn)跨端開(kāi)發(fā)。
相信很多人在探索iOS架構(gòu)的過(guò)程中都會(huì)發(fā)現(xiàn)沒(méi)有現(xiàn)成的完整的開(kāi)源架構(gòu)方案,基本都是開(kāi)源了一些可以實(shí)現(xiàn)架構(gòu)的部分組件,而沒(méi)有形成真正的可以完美解決項(xiàng)目模塊化,熱更新的方案。所以很多時(shí)候完美的架構(gòu)方案還停留在理論階段。
由于iOS架構(gòu)并不是很普及,很多人甚至都沒(méi)有聽(tīng)說(shuō)過(guò)什么是iOS架構(gòu),對(duì)組件化和模塊化更是一竅不通。當(dāng)碰到項(xiàng)目需要掌握基本組件化能力的時(shí)候就會(huì)顯得很被動(dòng)。然而掌握組件化并不是一個(gè)很容易的過(guò)程,雖然它只是iOS架構(gòu)開(kāi)始的入門階段。
最麻煩的事,可能是想要開(kāi)始iOS架構(gòu),卻發(fā)現(xiàn)相關(guān)的資料并不是很豐富,大部分都是探討的內(nèi)容。所以作者特意開(kāi)源了代碼。
YLBDesign的架構(gòu)組件
架構(gòu)組件是構(gòu)成架構(gòu)的具體代碼基礎(chǔ)。
架構(gòu)包括:架構(gòu)組件、業(yè)務(wù)模塊組件、資源等。
這里需要說(shuō)明的是,架構(gòu)組件只是架構(gòu)理論的一部分,可以是自研的組件,也可以是開(kāi)源的組件。作為架構(gòu)應(yīng)該可以支持同類型功能的不同組件,實(shí)現(xiàn)架構(gòu)組件的替換,所以架構(gòu)還會(huì)一直發(fā)展和豐富。作者盡量使用開(kāi)源組件,因?yàn)樽髡呦嘈努F(xiàn)在的App基本都會(huì)使用開(kāi)源庫(kù),因此可以實(shí)現(xiàn)無(wú)縫連接。當(dāng)沒(méi)有合適的開(kāi)源組件可以使用時(shí),作者會(huì)使用自研的開(kāi)源組件。
可以選擇的組件
模塊通信:YLBModule、BeeHive
路由跳轉(zhuǎn):JLRoutes、MGJRouter
插件化(熱更新):小程序(https://nativesupport.dcloud.net.cn/README)、React Native、自研小程序語(yǔ)言、自研跨端語(yǔ)言
YLBDesign使用的架構(gòu)組件
為了使作者的架構(gòu)理論能完全實(shí)現(xiàn),作者在參考BeeHive的基礎(chǔ)上自研了YLBModule模塊通信。
建議大家使用作者在YLBDesign中使用的架構(gòu)組件,這樣可以確保和作者所實(shí)現(xiàn)的效果一致,以免出現(xiàn)效果上的不一致而困惑。
作者所建立的iOS架構(gòu)支持線上修復(fù)App頁(yè)面問(wèn)題,不需要每次修復(fù)問(wèn)題都上線AppStore,小程序在這方面有天然優(yōu)勢(shì)(后期會(huì)對(duì)這方面做詳細(xì)描述)。
模塊通信:YLBModule(https://github.com/ProBobo/YLBModule)
路由跳轉(zhuǎn):JLRoutes(https://github.com/joeldev/JLRoutes)、YLBDRouter(https://github.com/YuliboTeam/YLBDRouter)
插件化(熱更新):小程序(https://nativesupport.dcloud.net.cn/README、https://www.dcloud.io)
說(shuō)明:這里只有小程序沒(méi)有進(jìn)行組件化封裝,而是使用官方文檔進(jìn)行集成,主要是為了便于理解,和官方文檔保持一致。當(dāng)然,要對(duì)小程序進(jìn)行Pod組件化封裝也是能做到的。由于uni-app提供的小程序依賴包太大,有1.6G,上傳Github受到限制,當(dāng)然下載也會(huì)很消耗時(shí)間。所以作者提供了小程序的集成鏈接:https://nativesupport.dcloud.net.cn/README、https://www.dcloud.io。
為什么使用uni-app的小程序
引用牛頓的話:站在巨人的肩膀上。
作者受微信小程序和支付寶小程序啟發(fā),發(fā)現(xiàn)小程序作為App中web頁(yè)面的替代品,支持在線更新,可以在架構(gòu)中充當(dāng)插件的角色,實(shí)現(xiàn)跨端開(kāi)發(fā)。這時(shí)候又發(fā)現(xiàn)uni-app支持App集成小程序,所以采用了uni-app的小程序。按照uni-app的說(shuō)法,uni-app先于微信開(kāi)發(fā)出了小程序。
uni-app官網(wǎng)原話:很多人以為小程序是微信先推出的,其實(shí),DCloud才是這個(gè)行業(yè)的開(kāi)創(chuàng)者。
App小程序可以快速轉(zhuǎn)化為微信小程序或者支付寶小程序等:小程序作為一個(gè)獨(dú)立開(kāi)發(fā)的語(yǔ)言,應(yīng)用也非常廣泛,掌握以后不僅可以在iOS、Android端使用,也可以獨(dú)立開(kāi)發(fā)小程序。uni-app還支持發(fā)布為前端web項(xiàng)目。
這是關(guān)于uni-app的描述:開(kāi)發(fā)者編寫一套代碼,可發(fā)布到iOS、Android、Web(響應(yīng)式)、以及各種小程序(微信/支付寶/百度/頭條/飛書(shū)/QQ/快手/釘釘/淘寶)、快應(yīng)用等多個(gè)平臺(tái)。
1、有完整的開(kāi)發(fā)文檔,容易掌握。
2、一套代碼,多端運(yùn)行。
2.1、支持在iOS端、Android端、H5端運(yùn)行。
2.2、使用的是前端開(kāi)發(fā)語(yǔ)言,可以進(jìn)行跨端開(kāi)發(fā)。
2.3、可以實(shí)現(xiàn)App小程序快速轉(zhuǎn)化為微信、支付寶等主流小程序。
2.4、有獨(dú)立的開(kāi)發(fā)工具,可以獨(dú)立修改小程序,無(wú)需編譯iOS項(xiàng)目進(jìn)行發(fā)版。
2.5、有自己的插件市場(chǎng)。
3、替代H5,優(yōu)化用戶體驗(yàn)。
模塊通信:YLBModule
YLBModule實(shí)現(xiàn)了模塊通信以及模塊消息回傳。
每個(gè)模塊都可以獨(dú)立為一個(gè)App,模塊和模塊之間沒(méi)有依賴關(guān)系。
為了使模塊具有App的能力,我們需要給模塊設(shè)計(jì)生命周期,模塊需要有自己的AppDelegate,并且模塊間的通信需要通過(guò)通信接口協(xié)議(protocol)實(shí)現(xiàn)。
我們需要在模塊的AppDelegate里面注冊(cè)通信接口協(xié)議,同時(shí)需要指定模塊接口類。即在AppDelegate中需要使通信接口協(xié)議和模塊接口類相對(duì)應(yīng),這樣通過(guò)通信接口協(xié)議就可以找到對(duì)應(yīng)模塊。
通信接口協(xié)議將會(huì)放在YLBDServices組件中。
YLBModule使用說(shuō)明
注意:YLBModule依賴庫(kù)在YLBDBasePod組件中建立依賴關(guān)系,對(duì)YLBDBasePod進(jìn)行依賴即可對(duì)YLBModule進(jìn)行依賴。
1、主工程YLBDesign引入YLBModule依賴庫(kù)(即對(duì)YLBDBasePod進(jìn)行依賴)。
2、在主工程的AppDelegate中引入YLBAppDelegate.h文件,并繼承YLBAppDelegate
@import UIKit;
#import <YLBModule/YLBAppDelegate.h>
NS_ASSUME_NONNULL_BEGIN
@interface AppDelegate : YLBAppDelegate <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
NS_ASSUME_NONNULL_END
在didFinishLaunchingWithOptions里面調(diào)用super方法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
//super的位置決定當(dāng)前AppDelegate的優(yōu)先級(jí)是否最高,放在最底部表示當(dāng)前AppDelegate優(yōu)先級(jí)最高
[super application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
3、創(chuàng)建首頁(yè)模塊YLBDHome,并引入YLBModule依賴庫(kù)。
4、在YLBDHome中創(chuàng)建YLBDHomeAppDelegate,用于管理模塊生命周期。
5、創(chuàng)建YLBDServices組件,并創(chuàng)建YLBDHomeProtocol協(xié)議文件。
6、注冊(cè)模塊:實(shí)現(xiàn)模塊生命周期
[[YLBModuleManager sharedInstance] registerModuleClass:[self class]];
7、注冊(cè)服務(wù):通過(guò)服務(wù)協(xié)議可以找到服務(wù)類
[[YLBServiceManager sharedInstance] registerService:@protocol(YLBDHomeProtocol) implClass:NSClassFromString(@"YLBDHomeController")];
8、通過(guò)服務(wù)協(xié)議(YLBDHomeProtocol)獲取服務(wù)類(YLBDHomeController)
//
// YLBDesignTabBarController.m
// YLBDesign
//
// Created by 余禮缽 on 2022/10/2.
//
#import "YLBDesignTabBarController.h"
#import "YLBDesignNavigationController.h"
#import <YLBCommon/YLBCommon.h>
#import <YLBModule/YLBServiceManager.h>
#import <YLBDServices/YLBDHomeProtocol.h>
#import <YLBDServices/YLBDMineProtocol.h>
@interface YLBDesignTabBarController ()<UITabBarControllerDelegate>
@property(nonatomic, strong) NSMutableArray *childVCArray;
@end
@implementation YLBDesignTabBarController
- (void)viewDidLoad {
[super viewDidLoad];
self.delegate = self;
[self setUpChildViewController];
}
#pragma mark - 設(shè)置tab
- (void)setUpChildViewController {
id<YLBDHomeProtocol> homeVC = [[YLBServiceManager sharedInstance] createService:@protocol(YLBDHomeProtocol)];
UIImage *homeSelectImage = [UIImage imageNamed:@"icon_tabbar_uikit_selected"];
UIImage *homeNormalImage= [UIImage imageNamed:@"icon_tabbar_uikit"];
[self setUpChildController:(UIViewController *)homeVC image:homeSelectImage selectImage:homeNormalImage title:@"首頁(yè)"];
id<YLBDMineProtocol> mineVC = [[YLBServiceManager sharedInstance] createService:@protocol(YLBDMineProtocol)];
UIImage *mineSelectImage = [UIImage imageNamed:@"icon_tabbar_lab_selected"];
UIImage *mineNormalImage= [UIImage imageNamed:@"icon_tabbar_lab"];
[self setUpChildController:(YLBDesignNavigationController *)mineVC image:mineSelectImage selectImage:mineNormalImage title:@"我的"];
self.viewControllers = self.childVCArray;
}
- (void)setUpChildController:(UIViewController *)vc image:(UIImage *)image selectImage:(UIImage *)selectImage title:(NSString *)title {
vc.tabBarItem.title = title;
vc.tabBarItem.image = image;
vc.tabBarItem.badgeValue = nil;
vc.tabBarItem.selectedImage = selectImage;
YLBDesignNavigationController *navi = [[YLBDesignNavigationController alloc] initWithRootViewController:vc];
[self.childVCArray addObject:navi];
}
- (NSMutableArray *)childVCArray {
if (!_childVCArray) {
_childVCArray = [@[] mutableCopy];
}
return _childVCArray;;
}
@end
業(yè)務(wù)模塊組件
首頁(yè)模塊
YLBDHome
pod 'YLBDHome', :git =>'https://github.com/YuliboTeam/YLBDHome.git'
個(gè)人中心模塊
YLBDMine
pod 'YLBDMine', :git => 'https://github.com/YuliboTeam/YLBDMine.git'
架構(gòu)服務(wù)組件
pod 'YLBDServices', :git => 'https://github.com/YuliboTeam/YLBDServices.git'
業(yè)務(wù)基礎(chǔ)組件
YLBDBasePod
pod 'YLBDBasePod', :git => 'https://github.com/YuliboTeam/YLBDBasePod.git'
使用的基礎(chǔ)開(kāi)源庫(kù)
pod 'YLBCommon', :git =>'https://github.com/ProBobo/YLBCommon.git'
pod 'YLBProUI', :git => 'https://github.com/YuliboTeam/YLBProUI.git'
pod 'QMUIKit'
關(guān)于小程序的開(kāi)發(fā)
1、uni小程序的應(yīng)用資源集成方式
https://nativesupport.dcloud.net.cn/UniMPDocs/UseSdk/ios?id=uni小程序的應(yīng)用資源集成方式
生成的 uni小程序 wgt 資源包可以部署到遠(yuǎn)程服務(wù)器動(dòng)態(tài)下發(fā)也可以直接內(nèi)置到工程中。
2、uni小程序自帶膠囊按鈕,如果想讓小程序頁(yè)面和原生一致,可以在pages.json文件中設(shè)置titleNView為false,這樣小程序中就不會(huì)出現(xiàn)膠囊按鈕。
{
"path": "pages/order/order",
"style": {
"navigationBarTitleText": "",
"app-plus": {
"titleNView": false,//禁用原生導(dǎo)航欄
"bounce":"none"
}
}
}
3、為了避免小程序出現(xiàn)版本檢查提示框,需要設(shè)置manifest.json文件中的ignoreVersion為true
"compatible": {
"ignoreVersion": true //true表示忽略版本檢查提示框,HBuilderX1.9.0及以上版本支持
},
4、uni小程序使用的也是路由跳轉(zhuǎn),可以和App的路由跳轉(zhuǎn)進(jìn)行統(tǒng)一。App端的路由配置文件ClassName.json也可以部署到遠(yuǎn)程服務(wù)器動(dòng)態(tài)下發(fā)也可以直接內(nèi)置到工程中。
{
"scheme":"YLDesign202210",
"home":"YLBDHomeController",
"mine":"YLBDMineController",
"detail":"YLBDDetailController",
}
所以,小程序頁(yè)面和原生頁(yè)面可以實(shí)現(xiàn)互換,在修復(fù)線上bug時(shí)可以節(jié)約發(fā)版時(shí)間。