LPDModularKit 0.3.0

LPDModularKit 0.3.0

TestsTested
LangLanguage Obj-CObjective C
License MIT
ReleasedLast Release May 2017

Maintained by qiaomu, EyreFree, halfrost.



  • By
  • foxsofter

Requirements

Installation

LPDModularKit is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod "LPDModularKit"

Author

foxsofter, [email protected]

lpd-modular-kit简介

###背景 app的业务越来越复杂,跨app的业务模块开始出现,如何模块化?同一个app中使用的技术可能包括MVC、MVVM、H5、React Native,如何更好的融合各项技术?开发人员开始膨胀,如何解耦开发人员?要解决上述问题,必须具备模块化的能力。so,lpd-modular-kit的出现就是为了解决上述问题。 ###一切都是为了解耦 本案最基本的要解决的问题就是模块间解耦,解耦不是完全不相干了,而是将耦合最小化。模块间本身已经存在一类不可避免的耦合,那就是页面跳转。而模块间还存在一类耦合,那就是模块间的相互调用,这一类耦合是我们要去重点解耦的。 页面跳转的解耦方式目前主流做法是通过url router来实现的,这也是iOS系统提供的一种方式,最好直接用起来。另外的模块间调用,这一类不是强需求,但是会存在,还是得提供一个方案,本案的做法是将这一类耦合也通过url router来实现,最小化解耦代价。 ###URL设计 ####URL Scheme URL的Scheme区分inter-app,in-app,如inter-app:me.ele.lpd,in-app:lpd,app内部两者都可以访问,外部app访问的Scheme只能是me.ele.lpd,模块内部可以自行决定是否允许外部app访问。 ####URL for action [scheme]://[modular]/[target]/[action]?[parameters] modular是模块的唯一标识,target是模块下面的某一类型的唯一标识,target是模块对外暴露的接口类,action是接口类所提供的函数的签名,parameters所对应的就是函数的参数。 ####URL for push|present [scheme]://[modular]/[target]/[push|present]?[parameters] 在MVC框架下,target表示UIViewController的唯一标识,在MVVM框架下,target表示LPDViewModelProtocol的子类的唯一标识,parameters所对应的就是target提供的属性。

push和present可以当成导航的action来看待,所以url的结构是统一的。

###类图 此处输入图片的描述

###LPDModularRouter

- (BOOL)openURL:(NSURL *)url options:(nullable NSDictionary<NSString *, id> *)options;

这个接口主要是用于处理外部app的url调用,类似于

#ifdef __IPHONE_9_0

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
  return [[LPDModularRouter sharedInstance] openURL:url options:options];
}

#else

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(nullable NSString *)sourceApplication annotation:(id)annotation {
  return [[LPDModularRouter sharedInstance] openURL:url options:@{ @"sourceApplication" : sourceApplication, @"annotation" : annotation }];
}

#endif
- (BOOL)performActionWithUrl:(NSURL *)url;
- (BOOL)performActionWithUrl:(NSURL *)url completion:(nullable void(^)(__nullable id x))completion;

这两个接口主要处理的时in-app调用,如果是action,action的参数最多支持7个,当然url中的参数必须覆盖到action的参数,可以多传,对于push和present,对传递参数的个数并没有限制,url中的参数和LPDViewTargetProtocol子类所定义的属性遵循对应上则匹配的原则。参数类型目前只支持NSString,并不是完善的RPC调用,然而应该够了。

###LPDModular、LPDTarget objective-c中并没有很好的办法用来隔离模块,本案中也没能明确划分模块,在代码层面可以理解成通过solution来隔离模块。 每个模块都是虚拟的,模块需要实现一个类,此类实现了LPDModularProtocol,每个模块可以自定义LPDModularProtocol中的方法,达到根据需要处理Router的url。

- (BOOL)performActionWithTarget:(Class)targetClass
                         action:(NSString*)actionName
                     parameters:(NSDictionary *)parameters
                     completion:(void(^)(id x))completion;

- (BOOL)pushWithTarget:(Class)targetClass
            parameters:(NSDictionary *)parameters
            completion:(void(^)(id x))completion;

- (BOOL)presentWithTarget:(Class)targetClass
               parameters:(NSDictionary *)parameters
               completion:(void(^)(id x))completion;
@interface LPDViewTargetProtocol : LPDTargetProtocol

@property (nonatomic, copy, readonly) NSString *modular;

@property (nonatomic, copy) void (^completion)(id);

@end
  • 模块中的Target类主要是用来暴露action,实现LPDTargetProtocol,此Target类的命名需要遵循LPD[modular][target]Target的规范,因为运行时是根据这个命名规范来解析的,不规范的不会被当成Target类,接口1会调用此类target;
  • 针对MVC框架,模块中的ViewController类需要实现LPDViewTargetProtocol,属性modular表示这个ViewController属于那个模块,completion是回调,值来自接口2|3,需要在ViewDidLoad中调用,completion为nil的时候表示不需要回调;
  • 在MVVM框架下,模块中的ViewModel类需要实现模块中的ViewController类需要实现LPDViewTargetProtocol,其余跟MVC框架下的实现是一致的。
  • 在运行时会分别将这三类Target解析出来,并归类到对应的Modular,接口1在用的时候会判断所对应的action有没有提供所传参数对应的函数参数,如果不存在,则不掉用。接口2|3在调用的时候会判断对应的Target有没有提供所传参数的属性,如果不存在对应的属性,则不处理;
  • 以上3个接口在调用时,如果存在一个特殊的key:sourceApplication则表示这个调用来自其它app,action可以自行判断事否要处理,push|present会将页面显示,在页面加载后的行为可以区分来自其它app后处不一样的处理。
  • 对于LPDViewTargetProtocol子类,如果想知道push或者present来自于其它app,只需要实现 @property (nonatomic, copy) NSString *sourceApplication; 就可以了,接口2|3在调用时,会将该属性赋值,如果不为nil即表示来自其它app。