推送通知和NSNotification有區(qū)別
- NSNotification是抽象的,不可見的
- 推送通知是能用眼睛看到的
兩種推送通知
- 本地通知(Local Notification):手機應用本身的通知,比如:鬧鐘。就是用來提醒用戶完成一些任務。
- 特點:不需要聯(lián)網(wǎng)就能發(fā)出推送通知(不需要服務器的支持)。
- 遠程(推送)通知(Remote Notification):遠程服務器聯(lián)網(wǎng)推送給客戶端的通知,以下列出5個常見的遠程通知。
- 1.qq在后臺,屏幕頂部出現(xiàn)橫幅,橫幅的內(nèi)容是別人給你發(fā)的信息,但是一會就消失了,這個就是遠程通知
- 2.UIAlertView彈框提示也是遠程通知
- 3.鎖屏狀態(tài)下能收到別人給你發(fā)的信息也是遠程通知
- 4.下拉屏幕出現(xiàn)的通知選項也是遠程通知
- 5.圖標右上角出現(xiàn)的1,2,3... 也是遠程通知
本地通知和遠程(推送)通知的區(qū)別
本地通知的局限性:只要用戶關閉了app,就無法跟app的服務器溝通,無法從服務器獲得最新的數(shù)據(jù)內(nèi)容
遠程通知的優(yōu)越性:用戶關閉了app,只要聯(lián)網(wǎng)了,也能收到服務器推送的遠程通知,能收到遠程通知的原因就是建立了長連接
遠程(推送)通知必須知道的知識點:
1.所有的蘋果設備,在聯(lián)網(wǎng)狀態(tài)下,都會與蘋果服務器建立長連接
2.長連接:只要聯(lián)網(wǎng),就一直建立連接,不聯(lián)網(wǎng),就無法建立連接.
3.長連接的作用:時間校準、系統(tǒng)升級、查找我的iPhone.
4.長連接的好處:數(shù)據(jù)傳輸速度快、數(shù)據(jù)保持最新狀態(tài)(實時更新).
Device Token = 手機的UDID + App的Bundle ID
遠程推送服務:APNS(Apple Push Notification Services)。
APNS和遠程推送通知的關系
遠程推送通知要經(jīng)過APNS才能推送到手機上。
device token,即設備令牌。根據(jù)自己的設備(UDID)和應用(Bundle ID)向APNS服務器發(fā)出請求來換取一個device token。
device token作用:如果應用需要發(fā)出通知給某個手機,但是應用發(fā)出的信息不是直接給手機的,而是必須統(tǒng)一交給APNS服務器。APNS服務器通過device token,知道應用要發(fā)的消息是給哪個手機設備的,并轉(zhuǎn)發(fā)該消息給手機,手機再通知應用程序。
做遠程(推送)通知的條件:
-
如果是真機調(diào)試階段(常用),需要如下準備:
- 真機調(diào)試的證書
- 遠程推送通知的開發(fā)證書
- Bundle ID
- 真機設備
- 根據(jù)上面4個生成的描述文件
-
如果是發(fā)布階段(不經(jīng)常用),需要如下準備:
- 發(fā)布階段的證書
- 遠程推送通知的生產(chǎn)證書
- Bundle ID
- 真機設備
- 根據(jù)上面4個生成的描述文件
-
總結(jié):
- 類似真機調(diào)試\打包測試\發(fā)布的做法.只不過限制電腦時,真機調(diào)試階段的話必須選擇Apple Push Notification service SSL(Sandbox),發(fā)布階段必須選擇Apple Push Notification service SSL (Sandbox & Production)
- 精華:看Idenfifiers-App IDs 選項的Development字段、Distribution字段下面是Enable還是Disabled,可以判斷出這兩個字段(開發(fā)階段、發(fā)布階段)的狀態(tài)
真機調(diào)試+遠程推送通知
1.創(chuàng)建請求文件(打開 keychain Access)把請求文件放到Certificates中安裝,并下載一個cer后綴的證書
2.登錄開發(fā)者賬號,生成真機調(diào)試階段需要的證書+遠程推送通知的開發(fā)證書+Bundle ID+真機設備+描述文件
3安裝真機調(diào)試的證書+安裝遠程推送通知的開發(fā)證書+安裝描述文件
4.創(chuàng)建用于真機調(diào)試+遠程推送通知階段的xcode文件并在xcode中指定Bundle ID、描述文件、證書
5.在AppDelegate.m文件中寫代碼完成遠程推送通知
6.利用PushMeBaby作為服務器,讓真機拿到通知的內(nèi)容顯示在真機設備上
相關代碼
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
if ([UIDevice currentDevice].systemVersion.doubleValue <= 8.0) {
// 不是iOS8
UIRemoteNotificationType type = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert;
// 當用戶第一次啟動程序時就獲取deviceToke
// 該方法在iOS8以及過期了
// 只要調(diào)用該方法, 系統(tǒng)就會自動發(fā)送UDID和當前程序的Bunle ID到蘋果的APNs服務器
[application registerForRemoteNotificationTypes:type];
}else
{
// iOS8
UIUserNotificationType type = UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound;
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:type categories:nil];
// 注冊通知類型
[application registerUserNotificationSettings:settings];
// 申請試用通知
[application registerForRemoteNotifications];
}
return YES;
}
/**
* 獲取到用戶對應當前應用程序的deviceToken時就會調(diào)用
*/
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSLog(@"%@", deviceToken);
}
@end
利用極光實現(xiàn)##真機調(diào)試+遠程推送通知
1.創(chuàng)建請求文件(打開 keychain Access)把請求文件放到Certificates中安裝,并下載一個cer后綴的證書
2.登錄開發(fā)者賬號,生成真機調(diào)試階段需要的證書+遠程推送通知的開發(fā)證書+遠程推送通知的生產(chǎn)證書+Bundle ID+真機設備+描述文件
3.安裝真機調(diào)試的證書+安裝遠程推送通知的開發(fā)證書+安裝遠程推送通知的發(fā)展證書+安裝描述文件+搭導出遠程推送通知的開發(fā)證書的p12文件+導出遠程推送通知的發(fā)展證書的p12文件
4.登錄https://www.jiguang.cn+創(chuàng)建應用(加載步驟3導出的兩個p12文件)
5.創(chuàng)建用于真機調(diào)試+遠程推送通知階段的xcode文件并在xcode中指定Bundle ID、描述文件、證書
6.下載極光的iOS SDK+查看極光的iOS SDK集成指南,按照集成指南來集成極光的iOS SDK
7.打開Xcode中的后臺模式+開啟手機的Wifi+運行添加到AppDelegate.m中的代碼
8.打開極光,找到"發(fā)送通知"按鈕,填寫通知的內(nèi)容,點擊立即發(fā)送,手機進入后臺,即可收到通知
獲得Device Token時提示如下問題的解決辦法
問題1:
You've implemented -[<UIApplicationDelegate> application:didReceiveRemoteNotification:fetchCompletionHandler:], but you still need to add "remote-notification" to the list of your supported UIBackgroundModes in your Info.plist.
問題1的解決辦法
問題2:
Not get deviceToken yet. Maybe: your certificate not configured APNs
```
#####解決辦法:手機聯(lián)wifi即可解決問題
---
#####問題3:
```
Use of undeclared identifier 'ASIdentifierManager'
```
#####解決辦法1:
```
導入#import <AdSupport/ASIdentifierManager.h>文件即可解決問題
```
#####解決辦法2:
```
刪除NSString *advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];代碼
```
---
#####問題4:
```
/BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3512.60.12/UIApplication.m:3401
```
#####解決辦法:
```
刪除AppDelegate.m文件中的
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
```
---
#遠程推送通知具體流程
---

---

---
- 推送通知的調(diào)試階段的證書+推送通知的發(fā)布階段的證書+普通調(diào)試階段的證書+描述文件+Bundle ID(開啟通知功能)+真機設備=既可以實現(xiàn)真調(diào)試,也可以實現(xiàn)推送通知
- .a文件就是靜態(tài)庫,本質(zhì)是.m文件,只不過將.m文件加密了,生成了.a文件
---
#本地通知
---
AppDelegate.m文件
```
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 注意: 在iOS8中, 必須提前注冊通知類型
if ([UIDevice currentDevice].systemVersion.doubleValue >= 8.0) {
UIUserNotificationType type = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound;
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:type categories:nil];
// 注冊通知類型
[application registerUserNotificationSettings:settings];
}
return YES;
}
// 代理方法:接收到本地通知時就會調(diào)用.程序必須在前臺/后臺,如果程序完全退出則不調(diào)用
// 當程序在前臺時, 會自動調(diào)用該方法
// 當程序在后臺時, 只有用戶點擊了提示的通知才會調(diào)用
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(@"%s", __func__);
// 點擊"注冊通知"按鈕,5秒后將會顯示灰色的UILabel,與此同時,通知的內(nèi)容也會一同顯示在灰色的UILabel上
static int count = 0;
count++;
UILabel *label = [[UILabel alloc] init];
label.frame = CGRectMake(self.window.frame.size.width/2-100, 300, 200, 200);
label.numberOfLines = 0;
label.textColor = [UIColor whiteColor];
label.text = [NSString stringWithFormat:@" %@", notification.userInfo];
label.font = [UIFont systemFontOfSize:21];
label.backgroundColor = [UIColor grayColor];
[self.window.rootViewController.view addSubview:label];
}
@end
```
ViewController.m文件
```
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
/*
@property(nonatomic,copy) NSDate *fireDate; 指定通知發(fā)送的時間
@property(nonatomic,copy) NSTimeZone *timeZone; 指定發(fā)送通知的時區(qū)
@property(nonatomic) NSCalendarUnit repeatInterval; 重復的周期, 枚舉
@property(nonatomic,copy) NSCalendar *repeatCalendar; 重復的周期 , 2014.12.12
@property(nonatomic,copy) NSString *alertBody; 通知內(nèi)容
@property(nonatomic) BOOL hasAction; 是否需要華東事件
@property(nonatomic,copy) NSString *alertAction; 鎖屏狀態(tài)的標題
@property(nonatomic,copy) NSString *alertLaunchImage; 點擊通知之后的啟動圖片
@property(nonatomic,copy) NSString *soundName; 收到通知播放的音樂
@property(nonatomic) NSInteger applicationIconBadgeNumber; 圖標提醒數(shù)字
@property(nonatomic,copy) NSDictionary *userInfo; 額外的信息
*/
#warning 驗證程序的四大功能
#warning 功能1:點擊"注冊通知"按鈕,等5秒看屏幕文字的變化.
#warning 功能2:點擊"注冊通知"按鈕,立刻將程序進入后臺,然后點擊收到的通知進入程序,看屏幕文字的變化 .
#warning 功能3:點擊"注冊通知"按鈕,立刻將程序關閉,然后點擊收到的通知進入程序,看屏幕文字的變化。
#warning 功能4:點擊"注冊通知"按鈕,然后立刻按住Command+L鎖屏,然后再次按住Command+L看到滑動解鎖的字樣,此時你看鎖屏界面的變化。
- (IBAction)AddNotification:(UIButton *)sender{
NSLog(@"%s", __func__);
// 1.創(chuàng)建本地通知對象
UILocalNotification *note = [[UILocalNotification alloc] init];
// 指定通知發(fā)送的時間(指定5秒之后發(fā)送通知,didReceiveLocalNotification代理方法5秒后才調(diào)用)
note.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];
// 注意: 在真實開發(fā)中一般情況下還需要指定時區(qū)(讓通知的時間跟隨當前時區(qū))
note.timeZone = [NSTimeZone defaultTimeZone];
// 指定通知內(nèi)容
note.alertBody = @"Hello,我是通知的內(nèi)容";
// 設置通知重復的周期(1分鐘通知一期)
// note.repeatInterval = NSCalendarUnitSecond;
// 指定鎖屏界面的信息(Command+L)
note.alertAction = @"Hello,我是鎖屏界面的信息";
// 設置點擊通知進入程序時候的啟動圖片
note.alertLaunchImage = @"Default";
// 收到通知播放的音樂
note.soundName = @"m_07.wav";
// 設置應用程序的提醒圖標
note.applicationIconBadgeNumber = 345;
// 注冊通知時可以指定將來點擊通知之后需要傳遞的數(shù)據(jù)
note.userInfo = @{@"name":@"zb",
@"age":@"25",
@"phone": @"13324233321"};
// 2.注冊通知(圖片的名稱建議使用應用程序啟動的圖片名稱)
UIApplication *app = [UIApplication sharedApplication];
// 每次調(diào)用添加方法都會將通知添加到scheduledLocalNotifications數(shù)組中
[app scheduleLocalNotification:note];
}
- (IBAction)CancelNotification:(UIButton *)sender {
UIApplication *app = [UIApplication sharedApplication];
// 清空通知
[app cancelAllLocalNotifications];
}
@end
```
截圖




---
#開源庫、閉源庫
---
- 開源庫:能看到具體實現(xiàn)的代碼(.m文件)。例如:AFNetworking、SDWebImage、MJRefresh
- 閉源庫:實現(xiàn)的代碼(.m)看不到,.m文件被加密成了.a文件。例如:Jpush、友盟、百度
- 閉源庫分為:
- 靜態(tài)庫: 以.a結(jié)尾 和.framework結(jié)尾
- 動態(tài)庫: 以.dylib結(jié)尾和.framework結(jié)尾
---
#MRC如何轉(zhuǎn)成ARC
---
- 將MRC環(huán)境中的文件打包成靜態(tài)庫,直接就可以在ARC環(huán)境中使用了。
---
#打包靜態(tài)庫
---
#####1.Framework & Library-->Cocoa Touch Static Library-->創(chuàng)建成功,可看到小房子圖標

#####2.在桌面新建一個文件夾,右鍵把文件名的后綴改為.bundle的,右鍵顯示包內(nèi)容將用到的圖片放進包中,最后將包放到項目中

#####3.創(chuàng)建類,寫上代碼,一定要選擇Build Phases->Copy Files->添加類的頭文件,讓頭文件參與編譯,否則編譯時不會生成.h文件

#####4.打包靜態(tài)庫,Command+B編譯模擬器狀態(tài)的xcode項目,然后再Command+B編譯真機狀態(tài)的xcode項目,這樣Products文件夾下的.a就會由紅色變?yōu)楹谏?右鍵.a文件,選擇Show in Finder可以看到模擬機的文件夾和真機的文件夾,每個文件夾中都有靜態(tài)庫+聲明文件

#####5.將模擬器的靜態(tài)庫和真機的靜態(tài)庫合并成一個靜態(tài)庫,可以解決默認情況下真機文件夾下的靜態(tài)庫只能用于真機上,模擬器下的靜態(tài)庫只能用于模擬器下的問題。具體做法:終端輸入cd xxx 切換到Xcode中的Products目錄下,再輸入lipo -create 模擬器的靜態(tài)庫1的路徑 真機的靜態(tài)庫2的路徑 -output 合并之后的文件名稱,以后可以拿合并之后的文件名稱,直接應用到模擬器上或者真機上都行。

#####6.將靜態(tài)庫(.a)+聲明文件+項目中的.bundle文件放到一個Lib文件夾中,然后拖入到另一個項目中使用

#####7.在另一個項目中導入Lib的類名,并調(diào)用類中的接口,具體實現(xiàn)的代碼在加密的.a文件中
- 注意:調(diào)用loadLogoImage方法加載Bundle中的文件目錄以及圖片名時,一定要和真實的目錄和圖片名一致,如果有大小寫字母不一致,那么在真機上是無法顯示Bundle中的圖片的,只是在模擬器中顯示,因為蘋果公司對手機app的代碼的精確度要求的非常非常嚴。我的步驟7就是出現(xiàn)了Bundle文件夾的B我寫成了小寫的,就出現(xiàn)了上面提到的情況。
---
#####將真機的靜態(tài)庫文件放到模擬器中運行時會提示如下錯誤 或 者將模擬器的靜態(tài)庫文件放到真機中運行也會提示如下錯誤

#####解決辦法:
- 將模擬器的靜態(tài)庫和真機的靜態(tài)庫合并成一個靜態(tài)庫,可以解決默認情況下真機文件夾下的靜態(tài)庫只能用于真機上,模擬器下的靜態(tài)庫只能用于模擬器下的問題。
- 即:lipo -create 模擬器的靜態(tài)庫1的路徑 真機的靜態(tài)庫2的路徑 -output 合并之后的文件名稱
---
#精態(tài)庫其他知識點
---
- 1.打包的靜態(tài)庫默認是打包的是Debug階段的靜態(tài)庫,也可以改成Release階段的靜態(tài)庫,但是最終都會生成這兩個階段的模擬器文件夾和真機文件夾
- 真機的Debug版本
- 真機的Release版本
- 模擬器的Debug版本
- 模擬器的Release版本
- 2.Debug版本和Release版本的比較
- 調(diào)試版本會包含完整的符號信息,以方便調(diào)試
- 調(diào)試版本不會對代碼進行優(yōu)化
- 發(fā)布版本不會包含完整的符號信息
- 發(fā)布版本的執(zhí)行代碼是進行過優(yōu)化的
- 發(fā)布版本的大小會比調(diào)試版本的略小
- 在執(zhí)行速度方面,發(fā)布版本會更快些,但不意味著會有顯著的提升
- 3.靜態(tài)庫的使用
- 新建項目時,是將編譯生成的.a靜態(tài)庫和 .h文件拖到新建的項目中使用的。
- 動態(tài)庫是不能上傳到app store中的,靜態(tài)庫可以上傳到app store
---
#打包動態(tài)庫
---
#####1.Framework & Library-->Cocoa Touch Framework-->創(chuàng)建成功,可看到工具箱圖標

#####2.在桌面新建一個文件夾,右鍵把文件名的后綴改為.bundle的,然后右鍵顯示包內(nèi)容將用到的圖片放進包中,最后將包放到項目中

#####3.創(chuàng)建類,寫上代碼,一定要選擇Build Phases->Headers->將Project選項中的.h文件拖動到Public選項中,目的就是讓剛才創(chuàng)建的類參與編譯,否則編譯時不會生成.h文件

#####4.打包動態(tài)庫,Command+B編譯模擬器狀態(tài)的xcode項目,然后再Command+B編譯真機狀態(tài)的xcode項目,這樣Products文件夾下的.a就會由紅色變?yōu)楹谏?右鍵.a文件,選擇Show in Finder可以看到模擬機的文件夾和真機的文件夾,每個文件夾中動態(tài)庫,動態(tài)庫中存儲著未加密的.h文件和加密d但是你看不見的.m文件以及存儲著圖片的Bundle文件(只要動態(tài)庫項目中有Bundle文件,那么打包動態(tài)庫時,動態(tài)庫會將將Bundle文件自動存儲在自己的庫中)

#####5.將模擬器的動態(tài)庫和真機的動態(tài)庫合并成一個動態(tài)庫,可以解決默認情況下真機文件夾下的動態(tài)庫只能用于真機上,模擬器下的動態(tài)庫只能用于模擬器下的問題。具體做法:終端輸入cd xxx 切換到Xcode中的Products目錄下,再輸入lipo -create 模擬器的動態(tài)庫1的路徑 真機佛你還態(tài)庫2的路徑 -output 合并之后的文件名稱,以后可以拿合并之后的文件名稱Dy,直接應用到模擬器上或者真機上都行。注意:兩個動態(tài)庫的路徑是和Info.plist在目錄中

#####6.拷貝模擬器的動態(tài)庫或者真機的動態(tài)庫的任意一個就行,重用名為MergeDy,并將合并之后的文件Dy替換掉MergeDy中的Dy,然后拖入到另一個項目中使用

#####7.運行則會提示image not found的錯誤,必須在General->Embedded Binaries中將動態(tài)庫添加進去

#####8.在另一個項目中導入#import <Dy/ZBTool.h>,并調(diào)用類中的接口即可

---
#動態(tài)庫其他知識點
---
- 1.默認創(chuàng)建的framework是動態(tài)庫,改成靜態(tài)庫非常簡單,即點擊Build Settings-->Linking-->Mach-O Type->Static Library .
- 如何驗證改成了靜態(tài)庫?
- 因為靜態(tài)庫不需要在General-->Embedded Binaries中導入framework就能運行成功,如果還是動態(tài)庫的話,不導入framework會提示錯誤。所以,經(jīng)過驗證,不導入framework也能運行成功,說明是改為了靜態(tài)庫
- 2.添加的framework默認是動態(tài)庫,必須在General->Embedded Binaries中將framework添加進來,否則提示image not found的錯誤
- 3.將模擬器的動態(tài)庫和真機的動態(tài)庫合并成一個動態(tài)庫,可以解決默認情況下真機文件夾下的動態(tài)庫只能用于真機上,模擬器下的動態(tài)庫只能用于模擬器下的問題。