上海快风信息科技有限公司ios开发进阶iOS 进阶

iOS 组件化整理

2017-08-08  本文已影响1040人  02ec002ab1b2

iOS 组件开发

WTModule组件间通信

背景

关于组件化开发,经历过几家不同公司,正好处于不同的开发阶段。
第一家港股上市公司,移动端业务线很多,主要是即时通讯功能,类似与微博的社区功能,企业ERP功能,视频教育功能,开始各条业务线独立,相互调用只能通过彼此提供静态库或者framework进行,每次升级都需要重新集成,比较麻烦,问题也很多,问题反馈解决时间也很漫长。之后公司后台业务重构,移动端随之进行组件化开发,经历了整个过程。
第二家公司,国内知名互联企业,进入开发组时,已经完成组件化工作,各个业务人员在自己的业务线上进行开发,每条业务线独立提测,在这样的业务线上工作,可以更专一的完成个人负责的业务,效率更高。
目前所在公司,没有进行组件化,业务耦合严重,全局变量使用频繁,经常会因为A业务被修改,导致B业务产生bug。现阶段开始进行组件化。

个人思考: 什么时候做组件开发

如何做组件开

组件划分

架构图.png

按照架构图,我们将App分为壳工程 + 业务 + 公共组件

  1. 壳工程:主要包含
  1. 业务组件:

业务组件之间不相互依赖,只可以依赖公共业务组件和公共组件, 业务之间的调用通过统一的组件进行。

  1. 公共组件:可以被业务组件依赖

以下为我们目前的划分:

公共组件:

网络组件:

持久化组件

第三方业务组件,在第三方SDK基础上进行适配

基础业务组件

如何做组件

工具

方法

通过Pods 进划分好的组件,进行私有Pod化。可以参考这篇文档
发布私有CocoaPod Spec

组件的构造和通信

组件生命周期管理

源码


#WTAppDelegate.h

@interface WTAppDelegate : UIResponder<UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end
#WTAppDelegate.m
#import "WTAppDelegate.h"
#import "WTModuleLifecycle+AppDelegate.h"

@implementation WTAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    return [[WTModuleLifecycle instance] application:application
                                 didFinishLaunchingWithOptions:launchOptions];
}

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
    //注册远程通知
    [application registerForRemoteNotifications];
}

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    [[WTModuleLifecycle instance] application:application
       didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
    [[WTModuleLifecycle instance] application:application
       didFailToRegisterForRemoteNotificationsWithError:error];
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    [[WTModuleLifecycle instance] application:application
                           didReceiveRemoteNotification:userInfo];
}

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    [[WTModuleLifecycle instance] application:application
                            didReceiveLocalNotification:notification];
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
    return [[WTModuleLifecycle instance] application:application
                                          continueUserActivity:userActivity
                                            restorationHandler:restorationHandler];
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    [[WTModuleLifecycle instance] applicationDidEnterBackground:application];
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    [[WTModuleLifecycle instance] applicationDidBecomeActive:application];
}

- (void)applicationWillResignActive:(UIApplication *)application
{
    [[WTModuleLifecycle instance] applicationWillResignActive:application];
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    [[WTModuleLifecycle instance] applicationWillTerminate:application];
}

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
    [[WTModuleLifecycle instance] applicationDidReceiveMemoryWarning:application];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    return [[WTModuleLifecycle instance] application:application
                                                       openURL:url
                                             sourceApplication:sourceApplication
                                                    annotation:annotation];
}

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

- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void(^)(BOOL succeeded))completionHandler
{
    [[WTModuleLifecycle instance] application:application
                           performActionForShortcutItem:shortcutItem
                                      completionHandler:completionHandler];
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    [[WTModuleLifecycle instance] applicationWillEnterForeground:application];
}

组件间通信

现状:目前非常流行的方式为,蘑菇街的解决方案,通过路由协议,实现全部App的内页面间跳转。不过我们没有选择这样做。

通过上面的定义,我们会发现路由协议不适用于我们对组件的定义。
分析:页面路由,打破了件间的独立性,虽然是通过统一的协议进行访问。但是这样就存在一个问题。A组件的一个子界面,可以访问B组件的子界面。这对我们对组件的理解有些矛盾。A组件的子界面需要调用B的子界面,需要通过B组件提供的接口调用。

我们的实现

  1. 注册组件由WTModuleLifecycle最为媒介持有组件
  2. 通过统一API调用,实现组件间通信

代码

#import "WTModuleMessager.h"
#import <objc/message.h>
#import "WTModuleLifecycle.h"

@interface WTModuleMessager()

@property (nonatomic, strong) NSMutableArray *services;

@end

@implementation WTModuleMessager

+ (instancetype)instance
{
    static dispatch_once_t oncePredicate;
    static WTModuleMessager *instance;
    dispatch_once(&oncePredicate, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _services = [NSMutableArray array];
    }
    return self;
}

+ (id)performAPI:(NSString *)moduleName methodName:(NSString *)methodName withParams:(NSDictionary *)params
{
    Class targetClass = [WTModuleMessager serviceClassFromString:moduleName];
    SEL targetSelector = [WTModuleMessager selectorFromString:methodName hasCallBack:NO];
    if (![WTModuleMessager validateClass:targetClass selector:targetSelector]) {
        NSAssert(targetClass, @"%@ 名称不规范",moduleName);
    }
    
    //    id targetOpenService = [[targetClass alloc]init];
    id targetOpenService = [[WTModuleLifecycle instance] moduleInstanceByName:NSStringFromClass(targetClass)];
    if ([targetOpenService respondsToSelector:targetSelector]) {
        return ((id (*)(id, SEL, id))objc_msgSend)(targetOpenService, targetSelector, params);
    } else {
        NSAssert(targetClass, @"%@ 找不到对应的类方法",methodName);
        return nil;
    }
}

+ (BOOL)performAPI:(NSString *)moduleName methodName:(NSString *)methodName withParams:(NSDictionary *)params block:(WTModuleMessagerCallback)block
{
    Class targetClass = [WTModuleMessager serviceClassFromString:moduleName];
    SEL targetSelector = [WTModuleMessager selectorFromString:methodName hasCallBack:YES];
    if (![WTModuleMessager validateClass:targetClass selector:targetSelector]) {
        NSAssert(targetClass, @"%@ 名称不规范",moduleName);
    }
    
    id targetOpenService = [[targetClass alloc]init];
    if ([targetOpenService respondsToSelector:targetSelector]) {
        [[WTModuleMessager instance].services addObject:targetOpenService];
        return ((BOOL (*)(id, SEL, id, WTModuleMessagerCallback))objc_msgSend)(targetOpenService, targetSelector, params,[WTModuleMessager callbackBlock:block openService:targetOpenService]);
    } else {
        NSAssert(targetClass, @"%@ 找不到对应的类方法",methodName);
        return NO;
    }
}

+ (WTModuleMessagerCallback)callbackBlock:(WTModuleMessagerCallback)callbackBlock openService:(NSObject *)openService
{
    WTModuleMessagerCallback block = ^(NSError *error, id result){
        [[WTModuleMessager instance].services removeObject:openService];
        callbackBlock(error,result);
    };
    return [block copy];
}

+ (BOOL)validateClass:(Class)targetClass selector:(SEL)targetSelector
{
    if (targetClass && targetSelector) {
        return YES;
    }
    return NO;
}

+ (Class)serviceClassFromString:(NSString *)moduleName
{
    NSArray *pathArray = [moduleName componentsSeparatedByString:@"/"];
    NSMutableString *serviceClassMS = [[NSMutableString alloc] initWithString:@"WT"];
    [serviceClassMS appendString:[NSString stringWithFormat:@"%@%@", [[pathArray.firstObject substringToIndex:1] capitalizedString], [pathArray.firstObject substringFromIndex:1]]];
    [serviceClassMS appendString:@"Module"];
    return NSClassFromString(serviceClassMS);
}

+ (SEL)selectorFromString:(NSString *)methodName hasCallBack:(BOOL)hasCallBack
{
    NSArray *pathArray = [methodName componentsSeparatedByString:@"/"];
    NSMutableString *serviceClassMS = [[NSMutableString alloc] init];
    [serviceClassMS appendString:[NSString stringWithFormat:@"%@%@", [[pathArray.firstObject substringToIndex:1] lowercaseString], [pathArray.firstObject substringFromIndex:1]]];
    
    NSString *selectorString = nil;
    if (hasCallBack) {
        selectorString = [NSString stringWithFormat:@"%@:block:",serviceClassMS];
    } else {
        selectorString = [NSString stringWithFormat:@"%@:",serviceClassMS];
    }
    return NSSelectorFromString(selectorString);
}

组件化基础代码Github地址

上一篇下一篇

猜你喜欢

热点阅读