为AppDelegate解耦和瘦身

在实际的开发过程中,AppDelegate应该除了负责应用生命周期之外,不应该再有多余的责任。但是往往在一个项目的业务不断扩展的时候一般都会伴随着 AppDelegate这个文件变的臃肿不堪,往往AppDelegate中会出现极光推送,友盟分享,微信支付宝支付等第三方的注册代码,变得十分的 凌乱。这时候你一般就会想着法的去封装代码,给这个臃肿的 AppDelegate瘦身。那么怎么合理的给AppDelegate瘦身呢?下面我们就介绍几种比较常用且好用的方法。

第一种为APPDelegate创建分类

将一些第三方初始化以及创建的代码抽离到APPDelegate的分类当中,实现APPDelegate的瘦身。

先以友盟推送为例
具体方法为先创建一个类别AppDelegate+UMengPush.h
给类别添加一个userInfo属性用来临时存放接收到的推送消息。

@property (nonatomic, strong) NSDictionary *userInfo;

以及一个配置友盟的方法

/**
 配置友盟推送

 @param appKey 友盟appkey
 @param launchOptions App launchOptions
 */
- (void)configureUMessageWithAppKey:(NSString *)appKey launchOptions:(NSDictionary *)launchOptions;

因为类别增加的属性不能直接赋值和取值, 还要再专门增加
getter / setter方法

/**
 给类别属性赋值

 @param userInfo 推送消息字典
 */
- (void)zx_setUserInfo:(NSDictionary *)userInfo;

/**
 获取类别属性值

 @return 暂存的推送消息
 */
- (NSDictionary *)zx_getUserInfo;

.m文件

#import "AppDelegate+UMengPush.h"
#import "UMessage.h"

#import <objc/runtime.h>

static char UserInfoKey;

@implementation AppDelegate (UMengPush)

#pragma mark - Configure UMessage SDK

- (void)configureUMessageWithAppKey:(NSString *)appKey launchOptions:(NSDictionary *)launchOptions {
    
    // 设置AppKey & LaunchOptions
    [UMessage startWithAppkey:appKey launchOptions:launchOptions];
    
    // 注册
    [UMessage registerForRemoteNotifications];
    
    // 开启Log
    [UMessage setLogEnabled:YES];
    
    // 检查是否为iOS 10以上版本
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10.0) {
        
        // 如果检查到时iOS 10以上版本则必须执行以下操作
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        center.delegate                  = self;
        UNAuthorizationOptions types10   = \
        UNAuthorizationOptionBadge | UNAuthorizationOptionAlert | UNAuthorizationOptionSound;
        
        [center requestAuthorizationWithOptions:types10 completionHandler:^(BOOL granted, NSError * _Nullable error) {
            
            if (granted) {
                
                // 点击允许
                // 这里可以添加一些自己的逻辑
            } else {
                
                // 点击不允许
                // 这里可以添加一些自己的逻辑
            }
        }];
        
    }
}

#pragma mark - UMessage Delegate Methods

- (void)application:(UIApplication *)application
            didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo {
    
    // 关闭友盟自带的弹出框
    [UMessage setAutoAlert:NO];
    
    [UMessage didReceiveRemoteNotification:userInfo];
    
    [self zx_setUserInfo:userInfo];
    
    // 定制自己的弹出框
    if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
        
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示"
                                                            message:userInfo[@"aps"][@"alert"]
                                                           delegate:self
                                                  cancelButtonTitle:@"确定"
                                                  otherButtonTitles:nil];
        [alertView show];
    }
}

// iOS 10新增: 处理前台收到通知的代理方法
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
    
    NSDictionary * userInfo = notification.request.content.userInfo;
    if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        
        //应用处于前台时的远程推送接受
        //关闭友盟自带的弹出框
        [UMessage setAutoAlert:NO];
        //必须加这句代码
        [UMessage didReceiveRemoteNotification:userInfo];
        
    }else{
        
        //应用处于前台时的本地推送接受
    }
    
    //当应用处于前台时提示设置,需要哪个可以设置哪一个
    completionHandler(UNNotificationPresentationOptionSound |
                      UNNotificationPresentationOptionBadge |
                      UNNotificationPresentationOptionAlert);
}

//iOS10新增:处理后台点击通知的代理方法
-(void)userNotificationCenter:(UNUserNotificationCenter *)center
            didReceiveNotificationResponse:(UNNotificationResponse *)response
                withCompletionHandler:(void (^)())completionHandler{
    
    NSDictionary * userInfo = response.notification.request.content.userInfo;
    if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        
        //应用处于后台时的远程推送接受
        //必须加这句代码
        [UMessage didReceiveRemoteNotification:userInfo];
        
    }else{
        
        //应用处于后台时的本地推送接受
    }
}


- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    
    [UMessage sendClickReportForRemoteNotification:[self zx_getUserInfo]];
}

- (void)zx_setUserInfo:(NSDictionary *)userInfo {
    
    objc_setAssociatedObject(self, &UserInfoKey, userInfo, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSDictionary *)zx_getUserInfo {
    
    if (objc_getAssociatedObject(self, &UserInfoKey)) {
        
        return objc_getAssociatedObject(self, &UserInfoKey);
    } else {
        
        return nil;
    }
}

@end

这样当们有项目需要继承友盟推送的时候, 只要配置好key, 在AppDelegate中只要简单一句话就完成了

#import "AppDelegate.h"
#import "AppDelegate+UMengPush.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    // 配置UMessage
    [self configureUMessageWithAppKey:UMessageAppKey launchOptions:launchOptions];
    
    return YES;
}

方法二. 组件化设计

解释:SOA面向服务的架构
面向服务的架构(SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的系统中的服务可以以一种统一和通用的方式进行交互。

作用:

1、SOA是面向服务的架构,所有的第三方功能都被分别封装成服务。
2、Component 表示这个类是用于引用的,不能用于继承。

实现步骤

一、首先创建服务类,服务类是对第三方服务的封装。第三方服务包括推送、支付、统计等

1、服务举例 BaiduPushService 头文件
新创建的服务类需要添加 <UIApplicationDelegate> 协议,根据需要实现协议中的方法。这里只添加了一个作为演示。

BaiduPushService.h

#import <Foundation/Foundation.h>  
#import <UIKit/UIKit.h>  
  
@interface BaiduPushService : NSObject  <UIApplicationDelegate>  
  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions ;  
  
@end  
BaiduPushService.m

#import "BaiduPushService.h"  
  
@implementation BaiduPushService  
  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  
{  
    NSLog(@"BaiduPushService didFinishLaunchingWithOptions");  
    return YES;  
}  
@end  

二、组件类

1、 SOAComponentAppDelegate.h 头文件
定义单例方法instance()和获取服务的方法services。

#import <Foundation/Foundation.h>  
  
@interface SOAComponentAppDelegate : NSObject  
 
+ (instancetype)instance ;  
//装服务的数组  
-(NSMutableArray*) services;  
  
@end  

2、SOAComponentAppDelegate.m实现
在实现类中,需要引用并注册第三方的服务类。

 
#import "SOAComponentAppDelegate.h"  
#import "BaiduPushService.h"  
  
@implementation SOAComponentAppDelegate  
{  
    NSMutableArray* allServices;  
}  
  
#pragma mark - 服务静态注册  
  
//需要运行程序之前,手工增加根据需要的新服务  
  
-(void)registeServices  
{  
    [self registeService:[[BaiduPushService alloc] init]];  
      
}  
  
#pragma mark - 获取SOAComponent单实例  
  
+ (instancetype)instance {  
      
    static SOAComponentAppDelegate *insance = nil;  
    static dispatch_once_t once;  
    dispatch_once(&once, ^{  
        insance = [[SOAComponentAppDelegate alloc] init];  
    });  
      
      
    return insance;  
}  
  
#pragma mark - 获取全部服务  
-(NSMutableArray *)services  
{  
      
    if (!allServices) {  
        allServices = [[NSMutableArray alloc]init];  
        [self registeServices];  
    }  
  
    return allServices;  
}  
  
#pragma mark - 服务动态注册  
-(void)registeService:(id)service  
{  
    if (![allServices containsObject:service])  
    {  
        [allServices addObject:service];  
    }     
}  
@end 

三、使用SOAComponentAppDelegate

1、AppDelegate.h 不做任何改动。

#import <UIKit/UIKit.h>  
  
@interface AppDelegate : UIResponder <UIApplicationDelegate>  
  
@property (strong, nonatomic) UIWindow *window;  
 
@end  

2、AppDelegate.m
导入 SOAComponentAppDelegate 和 BaiduPushService
在对应的方法里调用第三方服务中已经封装好的方法。

#import "AppDelegate.h"  
#import "SOAComponentAppDelegate.h"  
#import "BaiduPushService.h"  
  
@interface AppDelegate ()  
  
@end  
  
@implementation AppDelegate  
  
  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {  
      
    id<UIApplicationDelegate> service;  
    for(service in [[SOAComponentAppDelegate instance] services]){  
        if ([service respondsToSelector:@selector(application:didFinishLaunchingWithOptions:)]){  
            [service application:application didFinishLaunchingWithOptions:launchOptions];  
        }  
    }  
      
    return YES;  
}  
  
- (void)applicationWillResignActive:(UIApplication *)application {  
    id<UIApplicationDelegate> service;  
    for(service in [[SOAComponentAppDelegate instance] services]){  
        if ([service respondsToSelector:@selector(applicationWillResignActive:)]){  
            [service applicationWillResignActive:application];  
        }  
    }  
}  
  
- (void)applicationDidEnterBackground:(UIApplication *)application {  
    id<UIApplicationDelegate> service;  
    for(service in [[SOAComponentAppDelegate instance] services]){  
        if ([service respondsToSelector:@selector(applicationDidEnterBackground:)]){  
            [service applicationDidEnterBackground:application];  
        }  
    }  
}  
  
- (void)applicationWillEnterForeground:(UIApplication *)application {  
    id<UIApplicationDelegate> service;  
    for(service in [[SOAComponentAppDelegate instance] services]){  
        if ([service respondsToSelector:@selector(applicationWillEnterForeground:)]){  
            [service applicationWillEnterForeground:application];  
        }  
    }  
}  
  
- (void)applicationDidBecomeActive:(UIApplication *)application {  
    id<UIApplicationDelegate> service;  
    for(service in [[SOAComponentAppDelegate instance] services]){  
        if ([service respondsToSelector:@selector(applicationDidBecomeActive:)]){  
            [service applicationDidBecomeActive:application];  
        }  
    }  
}  
  
- (void)applicationWillTerminate:(UIApplication *)application {  
    id<UIApplicationDelegate> service;  
    for(service in [[SOAComponentAppDelegate instance] services]){  
        if ([service respondsToSelector:@selector(applicationWillTerminate:)]){  
            [service applicationWillTerminate:application];  
        }  
    }  
}  
@end  

上面的范例你是不是对SOA面向服务的架构有一个初略的了解了呢。
其实网上有一个很好用的模块管理小工具FRDModuleManager
FRDModuleManager 是一个简单的 iOS 模块管理工具由豆瓣开发人员开放的。FRDModuleManager 可以减小 AppDelegate 的代码量,把很多职责拆分至各个模块中去。使得 AppDelegate 会变得容易维护。
如果你发现自己项目中实现了 UIApplicationDelegate 协议的 AppDelegate 变得越来越臃肿,你可能会需要这样一个类似的小工具;或者如果你的项目实施了组件化或者模块化,你需要为各个模块在 UIApplicationDelegate 定义的各个方法中留下钩子(hook),以便模块可以知晓整个应用的生命周期,你也可能会需要这样一个小工具,以便更好地管理模块在 UIApplicationDelegate 协议各个方法中留下的钩子。

FRDModuleManager 可以使得留在 AppDelegate 的钩子方法被统一管理。实现了 UIApplicationDelegate 协议的 AppDelegate 是我知晓应用生命周期的重要途径。如果某个模块需要在应用启动时初始化,那么我们就需要在 AppDelegate 的 application:didFinishLaunchingWithOptions: 调用一个该模块的初始化方法。模块多了,调用的初始化方法也会增多。最后,AppDelegate 会越来越臃肿。FRDModuleManager 提供了一个统一的接口,让各模块知晓应用的生命周期。这样将使 AppDelegate 得以简化。

严格来说,AppDelegate 除了通知应用生命周期之外就不应该担负其他的职责。对 AppDelegate 最常见的一种不太好的用法是,把全局变量挂在 AppDelegate 上。这样就获得了一个应用内可以使用的全局变量。如果你需要对项目实施模块化的话,挂了太多全局变量的 AppDelegate 将会成为一个棘手的麻烦。因为,这样的 AppDelegate 成为了一个依赖中心点。它依赖了很多模块,这一点还不算是一个问题。但是,由于对全局变量的访问需要通过 AppDelegate,这就意味着很多模块也同时依赖着 AppDelegate,这就是一个大问题了。这是因为,AppDelegate 可以依赖要拆分出去的模块;但反过来,要拆分出去的模块却不能依赖 AppDelegate。

这个问题,首先要将全局变量从 AppDelegate 上剔除。各个类应该自己直接提供对其的全局访问方法,最简单的实现方法是将类实现为单例。变量也可以挂在一个能提供全局访问的对象上。当然,这个对象不应该是 AppDelegate。

其次,对于 AppDelegate 仅应承担的责任:提供应用生命周期变化的通知。就可以通过使用 FRDModuleManager 更优雅地解决。

这两步之后,AppDelegate 的依赖问题可以很好地解决,使得 AppDelegate 不再是项目的依赖中心点。

下面讲解一下如何使用FRDModuleManager。
已JPush为例
首先创建一个实现Jpush 的类。继承于NSObject,然后导入
"FRDModuleManager.h" 遵守 <FRDModule>协议

JPushModule.h

#import <Foundation/Foundation.h>
#import "FRDModuleManager.h"
@interface JPushModule : NSObject<FRDModule>

@end

然后将Jpush的实现放在JPushModule的.m内部
//#import "syste"
@interface JPushModule ()<JPUSHRegisterDelegate>
@property (nonatomic, strong) HYWPushServiceViewModel *viewModel;
@property (nonatomic, strong) NSTimer *homePageTimer;
/*是否已经成功将极光ID上传给后台。
 
 上传方法:[self uploadRegisterationIDAndUserID]
 上传时机:打开app + 从后台到前台 + 极光注册成功 + 登录成功
 
 YES:上传成功,不需要再上传
 NO: 上传失败,需要再次上传
 注意:登录,退出登录时要设置为NO,目的是为了再次上传
 **/
@property (nonatomic, assign) BOOL finishUpLoadJPUSHID;

@end
@implementation JPushModule


- (void)regisJPushWithOptions:(NSDictionary *)launchOptions
                pushViewModel:(HYWPushServiceViewModel *)viewModel
{
    self.viewModel = viewModel;
    //Required
    if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
        JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
        entity.types = UNAuthorizationOptionAlert|UNAuthorizationOptionBadge|UNAuthorizationOptionSound;
        [JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
        
    }else if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
        //可以添加自定义categories
        [JPUSHService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge |
                                                          UIUserNotificationTypeSound |
                                                          UIUserNotificationTypeAlert)
                                              categories:nil];
    }else {
        //categories 必须为nil
        [JPUSHService registerForRemoteNotificationTypes:(UNAuthorizationOptionBadge |
                                                          UNAuthorizationOptionSound |
                                                          UNAuthorizationOptionAlert)
                                              categories:nil];
    }
    // Optional
    // 获取IDFA
    //     如需使用IDFA功能请添加此代码并在初始化方法的advertisingIdentifier参数中填写对应值
    [JPUSHService setupWithOption:launchOptions appKey:JPUSHAPPKey
                          channel:CHANNEL
                 apsForProduction:YES
            advertisingIdentifier:nil];
    
    [self uploadRegisterationIDAndUserID];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    self.viewModel = [[HYWPushServiceViewModel alloc] init];
    [self regisJPushWithOptions:launchOptions pushViewModel:self.viewModel];
    
    return YES;
}
#pragma mark - 实现注册APNs失败接口
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
    //Optional
    NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error);
}

#pragma mark - 实现注册APNs
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    /// Required - 注册 DeviceToken
    [JPUSHService registerDeviceToken:deviceToken];
}

#pragma mark---- ios 7

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    // Required,For systems with less than or equal to iOS6
    [JPUSHService handleRemoteNotification:userInfo];
    [self push_handle:userInfo application:application];
}

#pragma mark---- ios 10.0

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    
    if ([[UIDevice currentDevice].systemVersion floatValue]<10.0 || application.applicationState>0) {
        
    }
    [self push_handle:userInfo application:application];
    // Required, iOS 7 Support
    [JPUSHService handleRemoteNotification:userInfo];
    
    completionHandler(UIBackgroundFetchResultNewData);
    
    
    // 应用在前台 或者后台开启状态下,不跳转页面,让用户选择。
    if (application.applicationState == UIApplicationStateActive || application.applicationState == UIApplicationStateBackground) {
        
    }
    else
    {
        
        //跳转到指定页面
        [self pushToViewController];
    }
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    
    [self uploadRegisterationIDAndUserID];
    
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    if (![window.rootViewController isKindOfClass:[LoginViewController class]]) {
        [self.homePageTimer setFireDate:[NSDate date]];
    }

    application.applicationIconBadgeNumber = 0;
    [JPUSHService setBadge:0];
    
}

- (void)applicationDidBecomeActive:(UIApplication *)application {

    application.applicationIconBadgeNumber = 0;
    [JPUSHService setBadge:0];
}

- (void)applicationWillTerminate:(UIApplication *)application {
}

- (void)uploadRegisterationIDAndUserID
{
    if (self.finishUpLoadJPUSHID) {
        return;
    }
    //判断token是否存在
    if (![[WSOTokenAgent sharedAgent] getAccessToken]) {
        return;
    }
    __weak typeof(self) this = self;
    NSString *registerationID = [JPUSHService registrationID];
    NSNumber *user_id =  [[NSUserDefaults standardUserDefaults] objectForKey:@"uid"];
    
    if (registerationID.length > 0) {
        
        [self.viewModel setBlockWithReturnBlock:^(id returnValue) {
            this.finishUpLoadJPUSHID = YES;
        } WithErrorBlock:^(id errorCode, NSString *message) {
            this.finishUpLoadJPUSHID = NO;
        } WithFailureBlock:^{
            this.finishUpLoadJPUSHID = NO;
        }];
        [self.viewModel wso_uploadRegisterationID:registerationID userID:user_id];
    }
}

#pragma mark - <JPUSHRegisterDelegate>
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler
{
    // Required
    NSDictionary * userInfo = notification.request.content.userInfo;
    if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        [JPUSHService handleRemoteNotification:userInfo];
        
        
        NSLog(@"%ld",(long)[UIApplication sharedApplication].applicationState);
        
        if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
            //程序运行时收到通知,先弹出消息框
            NSLog(@"程序在前台");
        }
        
        else{
            //跳转到指定页面
            [self pushToViewController];
        }
    }
    completionHandler(UNNotificationPresentationOptionAlert); // 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以选择设置
}

- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler
{
    // Required
    NSDictionary * userInfo = response.notification.request.content.userInfo;
    NSLog(@"response====%@",response);
    //    NSString *type = userInfo[@"type"];
    
    if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        [JPUSHService handleRemoteNotification:userInfo];
        
        if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {//用户点击的时候执行跳转
            
            //程序运行时收到通知,先弹出消息框
            [self pushToViewController];//程序激活状态下点击跳转逻辑
            //            [self pushToViewControllerWithTypeStr:type];
            
        }
        
        else{
            //跳转到指定页面
            [self pushToViewController];
        }
    }
    completionHandler();  // 系统要求执行这个方法
    [self push_handle:@{} application:nil];
}

- (void)push_handle:(NSDictionary *)userInfo application:(UIApplication *)application{
    
    [[NSNotificationCenter defaultCenter] postNotificationName:@"JPUSH" object:nil];
    application.applicationIconBadgeNumber = 0;
    
}

- (void)pushToViewController{
    
    UIViewController *vc = nil;
    if ([self.window.rootViewController isKindOfClass:[UINavigationController class]]) {
        vc = ((UINavigationController *)self.window.rootViewController).viewControllers.lastObject;
    }else{
        vc = self.window.rootViewController;
    }
    
    //判断如果是在登录页面,那么不进行跳转
    if ([vc isKindOfClass:[LoginViewController class]]) {
        return;
    }
    //判断如果是在消息中心页面,那么不进行跳转
    if ([vc isKindOfClass:[MessageCenterViewController class]]) {
        return;
    }
    
    MessageCenterViewController *messageVC = [MessageCenterViewController new];
    if (vc.presentedViewController) {
        //根控制器不是导航控制器
        messageVC.isPresent = YES;
        UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:messageVC];
        //实现转场动画 present的时候实现push的动画效果。
        [vc.presentedViewController wxs_presentViewController:nav animationType:WXSTransitionAnimationTypeSysPushFromRight completion:nil];
    }else{
        //根控制器是导航控制器
        UINavigationController *nav = (UINavigationController *)vc;
        
        [nav pushViewController:messageVC animated:YES];
    }
}

//根据不同的type类型进行跳转
- (void)pushToViewControllerWithTypeStr:(NSString *)typeStr{
    UIViewController *vc = nil;
    if ([self.window.rootViewController isKindOfClass:[UINavigationController class]]) {
        vc = ((UINavigationController *)self.window.rootViewController).viewControllers.lastObject;
    }else{
        vc = self.window.rootViewController;
    }
    
    //判断如果是在登录页面,那么不进行跳转
    if ([vc isKindOfClass:[LoginViewController class]]) {
        return;
    }
    //判断如果是在告警提醒中心页面,那么不进行跳转
    if ([vc isKindOfClass:[MessageCenterViewController class]]) {
        return;
    }
//    //判断如果是在系统通知中心页面,那么不进行跳转
//    if ([vc isKindOfClass:[SystematicNotificationViewController class]]) {
//        return;
//    }
    if ([typeStr isEqualToString:@"msg"]) {
        MessageCenterViewController *messageVC = [MessageCenterViewController new];
        if (vc.presentedViewController) {
            //根控制器不是导航控制器
            messageVC.isPresent = YES;
            UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:messageVC];
            //实现转场动画 present的时候实现push的动画效果。
            [vc.presentedViewController wxs_presentViewController:nav animationType:WXSTransitionAnimationTypeSysPushFromRight completion:nil];
        }else{
            //根控制器是导航控制器
            UINavigationController *nav = (UINavigationController *)vc;
            [nav pushViewController:messageVC animated:YES];
        }
    }
    

在APPDelegate文件中导入#import "FRDModuleManager.h"

FRDModuleManager 使用一个 plist 文件注册所有模块

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    NSString* plistPath = [[NSBundle mainBundle] pathForResource:@"ModulesRegister" ofType:@"plist"];
    
    FRDModuleManager *manager = [FRDModuleManager sharedInstance];
    [manager loadModulesWithPlistFile:plistPath];
    
    [manager application:application willFinishLaunchingWithOptions:launchOptions];
    
    return YES;
}

在 UIApplicationDelegate 各方法中留下钩子

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  NSString* plistPath = [[NSBundle mainBundle] pathForResource:@"ModulesRegister" ofType:@"plist"];
  FRDModuleManager *manager = [FRDModuleManager sharedInstance];
  [manager loadModulesWithPlistFile:plistPath];

  [manager application:application didFinishLaunchingWithOptions:launchOptions];

  return YES;
}


- (void)applicationWillResignActive:(UIApplication *)application {
  [[FRDModuleManager sharedInstance] applicationWillResignActive:application];
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
  [[FRDModuleManager sharedInstance] applicationDidEnterBackground:application];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
  [[FRDModuleManager sharedInstance] applicationWillEnterForeground:application];
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
  [[FRDModuleManager sharedInstance] applicationDidBecomeActive:application];
}

- (void)applicationWillTerminate:(UIApplication *)application {
  [[FRDModuleManager sharedInstance] applicationWillTerminate:application];
}

#pragma mark - Handling Remote Notification

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

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

- (void)application:(UIApplication *)application
  didReceiveRemoteNotification:(NSDictionary *)userInfo
  fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler
{
  [[FRDModuleManager sharedInstance] application:application
                    didReceiveRemoteNotification:userInfo
                          fetchCompletionHandler:completionHandler];
}

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

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  [[FRDModuleManager sharedInstance] userNotificationCenter:center
                                    willPresentNotification:notification
                                      withCompletionHandler:completionHandler];
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)())completionHandler
{
  [[FRDModuleManager sharedInstance] userNotificationCenter:center
                             didReceiveNotificationResponse:response
                                      withCompletionHandler:completionHandler];
}
在 FRDModuleManager 被 UIApplicationDelegate 各方法内留下的钩子调用时,会调用注册的每个模块的相同的方法。这样每个模块就都知晓了应用的生命周期

其实FRDModuleManager的内部原理就是将所有的第三方的实现封装到各自的FRDModule内部,然后将FRDModule添加到自己的服务数组中,然后在程序的不同生命周期中遍历所有的服务,然后去调用FRDModule内部的注册方法,实现对第三方服务的封装,这样可以让APPdelegate内部的代码更加整洁清晰,不同的FRDModule实现不同的服务注册,方便管理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 严格来讲,AppDelegate除了负责应用生命周期之外,不应该再有多余的责任。 但在iOS 实际开发过程中,很多...
    CoderDancer阅读 3,918评论 7 20
  • 原文链接http://www.cocoachina.com/ios/20161103/17938.html?utm...
    wycandyy阅读 2,273评论 0 22
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • (本文为原创作品,如需转载,请与本人联系。谢谢。) 文/润香 《普洱“喜茶”记》看见文章题目的那一霎那,您是否在想...
    润香阅读 1,005评论 2 1