LPDModularKit 0.3.0

LPDModularKit 0.3.0

测试已测试
Lang语言 Obj-CObjective C
许可证 MIT
Released最后发布2017年5月

qiaomuhalfrostEyreFree维护。



  • 作者
  • foxsofter

需求

安装

LPDModularKit 通过 CocoaPods 提供。要安装它,只需在 Podfile 中添加以下行:

pod "LPDModularKit"

作者

foxsofter, [email protected]

lpd-modular-kit简介

###背景 app的业务越来越复杂,跨app的业务模块开始出现,如何模块化?同一个app中使用的技术可能包括MVC、MVVM、H5、React Native,如何更好的融合各项技术?开发人员开始膨胀,如何解耦开发人员?要解决上述问题,必须具备模块化的能力。因此,lpd-modular-kit的出现就是为了解决这些问题。###一切都是为了解耦 本案最基本要解决的问题就是模块间解耦,解耦不是完全不相干了,而是将耦合最小化。模块间本身已经存在一类不可避免的耦合,那就是页面跳转。而模块间还存在一类耦合,那就是模块间的相互调用,这一类耦合是我们要重点解耦的。页面跳转的解耦方式目前主流的做法是通过url router来实现,这也是iOS系统提供的一种方式,最好直接用起来。另外的模块间调用,这一类不是强需求,但是会存在,还是得提供一个方案。本案的做法是将这一类耦合也通过url router来实现,最小化解耦代价。

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类,其余跟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。