iOS使用观察者模式实现推送消息模块化


声明:本文转载自https://my.oschina.net/FEEDFACF/blog/1609909,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

背景

前段时间做了一些项目解耦重构和一些组件化的工作,推送是很多app种涉及到的应用场景,所以把推送模块做了一些重构的工作,让推送模块能够独立于业务适用于各种的业务场景。

分析

推送消息模块和其他模块从技术角度来看是数据同一级别的模块,推送消息模块为了能够和其他业务组件之间有联系和通信而又不能有耦合,这复合设计中的控制反转原则,依赖的双方依赖于对方的抽象而不依赖于具体的实现,观察者模式就是一种典型的控制反转场景,观察者模式很适合推送模块的独立和解耦。

实现

观察者模式简析

观察者模式类图
这是一个简单的观察者的类图结构

  • 抽象接口 Subject 有一个注册观察者的方法
  • 抽象接口 Observer 有一个获取 Subject 数据更新的方法
  • SubjectObserver 具体的子类重写对应的方法,处理数据
  • 有新的消息,ConcreateSubject 会把消息发送给已注册的ConcreateObserverConcreateObserver 负责接收消息进行处理

代码实现

抽象接口

OC接口是使用 protocol 实现的,定义观察者(Observer)和被观察主题(Subject)如下:

@protocol PTNotificationObservable;  @protocol PTNotificationObserver <NSObject>  - (void)update:(id<PTNotificationObservable>)sender data:(id)data;  @end   @protocol PTNotificationObservable <NSObject>  - (void)addObserver:(id<PTNotificationObserver>)observer;  @end 

具体事项

推送模块管理类对应的是被观察主题(Subject),定义了如下接口:

  • APP启动,初始化推送配置
  • 处理注册Token
  • 处理接收消息
  • 添加观察者

头文件如下:

#import <Foundation/Foundation.h> #import "PTNotificationProtocllDefine.h"  #undef    AS_SINGLETON #define AS_SINGLETON \ + (instancetype)sharedInstance;  #undef    DEF_SINGLETON #define DEF_SINGLETON \ + (instancetype)sharedInstance{ \ static dispatch_once_t once; \ static id __singleton__; \ dispatch_once( &once, ^{ __singleton__ = [[self alloc] init]; } ); \ return __singleton__; \ } \   @interface PTNotificationManager : NSObject <PTNotificationObservable>  AS_SINGLETON  // 处理APP启动,配置推送 - (void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions; // 处理注册Token - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; // 处理接收消息 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;  // 添加观察者 - (void)addObserver:(id<PTNotificationObserver>)observer;  - (void)testSendNotification;  @end 

集成的是第三方的友盟消息推送,所有里面包含了友盟一些API的使用。
实现文件:

#import "PTNotificationManager.h" #import "UMessage.h" #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 100000 #import <UserNotifications/UserNotifications.h> #endif  #define UMengAppKey @"xxxxx"  @interface PTNotificationManager ()<UNUserNotificationCenterDelegate> @property (nonatomic, strong) NSPointerArray* observers; @end  @implementation PTNotificationManager  DEF_SINGLETON  - (void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {     //设置 AppKey 及 LaunchOptions     [UMessage startWithAppkey:UMengAppKey launchOptions:launchOptions];     //注册通知     [UMessage registerForRemoteNotifications];          //iOS10必须加下面这段代码。     if ([[[UIDevice currentDevice] systemVersion]intValue] >= 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 {                 //点击不允许             }         }];     }          //如果你期望使用交互式(只有iOS 8.0及以上有)的通知,请参考下面注释部分的初始化代码     if (([[[UIDevice currentDevice] systemVersion]intValue]>=8)&&([[[UIDevice currentDevice] systemVersion]intValue]<10)) {  //        UIMutableUserNotificationAction *action1 = [[UIMutableUserNotificationAction alloc] init]; //        action1.identifier = @"action1_identifier"; //        action1.title=@"打开应用"; //        action1.activationMode = UIUserNotificationActivationModeForeground;当点击的时候启动程序 // //        UIMutableUserNotificationAction *action2 = [[UIMutableUserNotificationAction alloc] init];  第二按钮 //        action2.identifier = @"action2_identifier"; //        action2.title=@"忽略"; //        action2.activationMode = UIUserNotificationActivationModeBackground;当点击的时候不启动程序,在后台处理 //        action2.authenticationRequired = YES;需要解锁才能处理,如果action.activationMode = UIUserNotificationActivationModeForeground;则这个属性被忽略; //        action2.destructive = YES; //        UIMutableUserNotificationCategory *actionCategory1 = [[UIMutableUserNotificationCategory alloc] init]; //        actionCategory1.identifier = @"category1";这组动作的唯一标示 //        [actionCategory1 setActions:@[action1,action2] forContext:(UIUserNotificationActionContextDefault)]; //        NSSet *categories = [NSSet setWithObjects:actionCategory1, nil]; //        [UMessage registerForRemoteNotifications:categories];     }     //如果要在iOS10显示交互式的通知,必须注意实现以下代码     if ([[[UIDevice currentDevice] systemVersion]intValue]>=10) {  //        UNNotificationAction *action1_ios10 = [UNNotificationAction actionWithIdentifier:@"action1_ios10_identifier" title:@"打开应用" options:UNNotificationActionOptionForeground]; //        UNNotificationAction *action2_ios10 = [UNNotificationAction actionWithIdentifier:@"action2_ios10_identifier" title:@"忽略" options:UNNotificationActionOptionForeground];         //UNNotificationCategoryOptionNone         //UNNotificationCategoryOptionCustomDismissAction  清除通知被触发会走通知的代理方法         //UNNotificationCategoryOptionAllowInCarPlay       适用于行车模式 //        UNNotificationCategory *category1_ios10 = [UNNotificationCategory categoryWithIdentifier:@"category101" actions:@[action1_ios10,action2_ios10]   intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction]; //        NSSet *categories_ios10 = [NSSet setWithObjects:category1_ios10, nil]; //        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; //        [center setNotificationCategories:categories_ios10];      }          //如果对角标,文字和声音的取舍,请用下面的方法     //UIRemoteNotificationType types7 = UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeSound;     //UIUserNotificationType types8 = UIUserNotificationTypeAlert|UIUserNotificationTypeSound|UIUserNotificationTypeBadge;     //[UMessage registerForRemoteNotifications:categories withTypesForIos7:types7 withTypesForIos8:types8];      //for log     [UMessage setLogEnabled:YES];          // 应用从通知启动     NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];     if (userInfo) {         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{             [self handleNotificationUserInfo:userInfo];         });     } }  - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {     //1.2.7版本开始不需要用户再手动注册devicetoken,SDK会自动注册     // [UMessage registerDeviceToken:deviceToken];          NSString* tokenString = [self stringDevicetoken:deviceToken];     NSLog(@"==tokenString = %@", tokenString);     printf([[NSString stringWithFormat:@"\n\ntokenString = %@\n\n", tokenString] UTF8String]); }  /**  - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error  {  //如果注册不成功,打印错误信息,可以在网上找到对应的解决方案  //1.2.7版本开始自动捕获这个方法,log以application:didFailToRegisterForRemoteNotificationsWithError开头  } */  - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {     [[NSNotificationCenter defaultCenter] postNotificationName:@"userInfoNotification" object:self userInfo:@{@"userinfo":[NSString stringWithFormat:@"%@",userInfo]}];     //关闭友盟自带的弹出框     [UMessage setAutoAlert:NO];     [UMessage didReceiveRemoteNotification:userInfo];     if (application.applicationState != UIApplicationStateActive) {         [self handleNotificationUserInfo:userInfo];     } }  // 添加观察者 - (void)addObserver:(id<PTNotificationObserver>)observer {     [self.observers addPointer:(__bridge void * _Nullable)(observer)]; }  - (void)testSendNotification {     [self handleNotificationUserInfo:@{@"kkk": @"kkk"}]; }  - (void)handleNotificationUserInfo:(NSDictionary *)userInfo {     for (id<PTNotificationObserver> observer in self.observers.allObjects) {         [observer update:self data:userInfo];     } }  #pragma mark - ......::::::: UNUserNotificationCenterDelegate :::::::......  //iOS10新增:处理前台收到通知的代理方法 -(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{         //应用处于前台时的本地推送接受     } }  //iOS10新增:处理后台点击通知的代理方法 - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(nonnull void (^)(void))completionHandler {     NSDictionary * userInfo = response.notification.request.content.userInfo;     if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {         //应用处于后台时的远程推送接受         //必须加这句代码         [UMessage didReceiveRemoteNotification:userInfo];         [self handleNotificationUserInfo:userInfo];     }else{         //应用处于后台时的本地推送接受     } }   #pragma mark - ......::::::: Helper :::::::......  -(NSString *)stringDevicetoken:(NSData *)deviceToken {     NSString *token = [deviceToken description];     NSString *pushToken = [[[token stringByReplacingOccurrencesOfString:@"<"withString:@""] stringByReplacingOccurrencesOfString:@">"withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];     return pushToken; }   #pragma mark - ......::::::: Lazy Load :::::::......  - (NSPointerArray *)observers {     if (nil == _observers) {         _observers = [NSPointerArray weakObjectsPointerArray];     }     return _observers; }  @end 

使用方法:

  • ViewController 实现了 PTNotificationObserver Protocol 作为观察者
  • 重写 - (void)update:(id<PTNotificationObservable>)sender data:(id)data 方法打印接收到的消息
  • - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 模拟让消息模块发送消息
@interface PTViewController () <PTNotificationObserver>  @end  @implementation PTViewController  - (void)viewDidLoad {     [super viewDidLoad];      [[PTNotificationManager sharedInstance] addObserver:self];  }  - (void)didReceiveMemoryWarning {     [super didReceiveMemoryWarning];     // Dispose of any resources that can be recreated. }   // 接收到消息 - (void)update:(id<PTNotificationObservable>)sender data:(id)data {     NSLog(@"%@", data); }  - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {     [[PTNotificationManager sharedInstance] testSendNotification]; } 

使用cocopods集成

使用 pod lib create 命令创建私有库

➜  DevPods pod lib create PTNotificationManager Cloning `https://github.com/CocoaPods/pod-template.git` into `PTNotificationManager`. Configuring PTNotificationManager template.  ------------------------------  To get you started we need to ask a few questions, this should only take a minute.  If this is your first time we recommend running through with the guide:   - http://guides.cocoapods.org/making/using-pod-lib-create.html  ( hold cmd and double click links to open in a browser. )   What language do you want to use?? [ Swift / ObjC ]  > Objc  Would you like to include a demo application with your library? [ Yes / No ]  >  yes Which testing frameworks will you use? [ Specta / Kiwi / None ]  > None  Would you like to do view based testing? [ Yes / No ]  > No  What is your class prefix?  > PT  Running pod install on your new library.  Analyzing dependencies Fetching podspec for `PTNotificationManager` from `../` Downloading dependencies Installing PTNotificationManager (0.1.0) Generating Pods project Integrating client project  [!] Please close any current Xcode sessions and use `PTNotificationManager.xcworkspace` for this project from now on. Sending stats Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.  [!] Automatically assigning platform ios with version 9.3 on target PTNotificationManager_Example because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.   Ace! you're ready to go!  We will start you off by opening your project in Xcode   open 'PTNotificationManager/Example/PTNotificationManager.xcworkspace'  To learn more about the template see `https://github.com/CocoaPods/pod-template.git`. To learn more about creating a new pod, see `http://guides.cocoapods.org/making/making-a-cocoapod`. ➜  DevPods  

修改 PTNotificationManager.podspec 文件如下

Pod::Spec.new do |s|   s.name             = 'PTNotificationManager'   s.version          = '0.1.0'   s.summary          = 'A short description of PTNotificationManager.'   s.description      = <<-DESC     Oh PTNotificationManager                        DESC    s.homepage         = 'https://github.com/flypigrmvb/PTNotificationManager'   s.license          = { :type => 'MIT', :file => 'LICENSE' }   s.author           = { 'flypigrmvb' => '862709539@qq.com' }   s.source           = { :git => 'https://github.com/flypigrmvb/PTNotificationManager.git', :tag => s.version.to_s }    s.ios.deployment_target = '8.0'    s.source_files = 'PTNotificationManager/Classes/**/*'    # s.public_header_files = 'Pod/Classes/**/*.h'      # 添加依赖的系统静态库     s.libraries = 'xml2', 'z', 'c++', 'stdc++.6', 'sqlite3'     # 添加系统的farme依赖库     s.frameworks = 'UIKit', 'MapKit'     # 添加其他Pod依赖库     s.dependency 'UMessage' end 

Example项目的 Podfile 添加如下内容

 platform :ios, '8.0'  inhibit_all_warnings!  target 'PTNotificationManager_Example' do   pod 'PTNotificationManager', :path => '../'   pod 'UMessage', :podspec => 'https://raw.githubusercontent.com/kkme/UMessage/master/UMessage.podspec'  end 

以上步骤Example项目就可以跑起来了,完成了简单的推送消息模块的组件化和解耦,方便在不同的业主场景中使用。

本文发表于2018年01月20日 12:32
(c)注:本文转载自https://my.oschina.net/FEEDFACF/blog/1609909,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2051 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1