前段時間作了一些項目解耦重構和一些組件化的工做,推送是不少app種涉及到的應用場景,因此把推送模塊作了一些重構的工做,讓推送模塊可以獨立於業務適用於各類的業務場景。html
本文的代碼連接:PTNotificationManagerios
推送消息模塊和其餘模塊從技術角度來看是屬於同一級別的模塊,推送消息模塊爲了可以和其餘業務組件之間既有通訊又能解耦,這複合設計中的控制反轉原則,依賴的雙方依賴於對方的抽象而不依賴於具體的實現,觀察者模式就是一種典型的控制反轉場景,觀察者模式很適合推送模塊的獨立和解耦。c++
Subject
有一個註冊觀察者的方法Observer
有一個獲取 Subject
數據更新的方法Subject
和 Observer
具體的子類重寫對應的方法,處理數據ConcreateSubject
會把消息發送給已註冊的ConcreateObserver
, ConcreateObserver
負責接收消息進行處理OC接口是使用 protocol 實現的,定義觀察者(Observer)和被觀察主題(Subject)以下:git
@protocol PTNotificationObservable;
@protocol PTNotificationObserver <NSObject>
- (void)update:(id<PTNotificationObservable>)sender data:(id)data;
@end
@protocol PTNotificationObservable <NSObject>
- (void)addObserver:(id<PTNotificationObserver>)observer;
@end
複製代碼
推送模塊管理類對應的是被觀察主題(Subject),定義了以下接口:github
頭文件以下:sql
#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的使用。
實現文件:ruby
#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
複製代碼
使用方法:session
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];
}
複製代碼
使用 pod lib create
命令建立私有庫app
➜ 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
文件以下ide
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項目就能夠跑起來了,完成了簡單的推送消息模塊的組件化和解耦,方便在不一樣的業主場景中使用。