Xcode里的相关配置如下:
APP实现
获取Device Token
和处理通知,直接贴代码:
AppDelegate.h
:
#ifdef NSFoundationVersionNumber_iOS_9_x_Max
#import
#endif
@interface AppDelegate : UIResponder
@property (nonatomic, strong) UIWindow *window;
@end
AppDelegate.m
:
#import "AppDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
//iOS10特有
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
// 必须写代理,不然无法监听通知的接收与点击
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
// 点击允许
NSLog(@"注册成功");
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
NSLog(@"%@", settings);
}];
} else {
// 点击不允许
NSLog(@"注册失败");
}
}];
}
// 注册获得device Token
[[UIApplication sharedApplication] registerForRemoteNotifications];
return YES;
}
#pragma mark - RemoteNotifications
// 获得Device Token
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
const unsigned *tokenBytes = [deviceToken bytes];
NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
NSLog(@"DeviceToken: %@", hexToken);
// [[MyModel sharedModel] setApnsToken:hexToken];
}
// 获得Device Token失败
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error);
}
#pragma mark - UNUserNotificationCenterDelegate
// iOS 10收到通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
NSDictionary * userInfo = notification.request.content.userInfo;
UNNotificationRequest *request = notification.request; // 收到推送的请求
UNNotificationContent *content = request.content; // 收到推送的消息内容
NSNumber *badge = content.badge; // 推送消息的角标
NSString *body = content.body; // 推送消息体
UNNotificationSound *sound = content.sound; // 推送消息的声音
NSString *subtitle = content.subtitle; // 推送消息的副标题
NSString *title = content.title; // 推送消息的标题
if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
NSLog(@"iOS10 前台收到远程通知:%@", notification);
// NSLog(@"iOS10 前台收到远程通知:%@", [self logDic:userInfo]);
} else {
// 判断为本地通知
NSLog(@"iOS10 前台收到本地通知:{\\\\nbody:%@,\\\\ntitle:%@,\\\\nsubtitle:%@,\\\\nbadge:%@,\\\\nsound:%@,\\\\nuserInfo:%@\\\\n}",body,title,subtitle,badge,sound,userInfo);
}
completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert); // 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以设置
}
// 通知的点击事件
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler{
NSDictionary * userInfo = response.notification.request.content.userInfo;
UNNotificationRequest *request = response.notification.request; // 收到推送的请求
UNNotificationContent *content = request.content; // 收到推送的消息内容
NSNumber *badge = content.badge; // 推送消息的角标
NSString *body = content.body; // 推送消息体
UNNotificationSound *sound = content.sound; // 推送消息的声音
NSString *subtitle = content.subtitle; // 推送消息的副标题
NSString *title = content.title; // 推送消息的标题
if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
NSLog(@"iOS10 收到远程通知:%@", response);
// NSLog(@"iOS10 收到远程通知:%@", [self logDic:userInfo]);
}
else {
// 判断为本地通知
NSLog(@"iOS10 收到本地通知:{\\\\nbody:%@,\\\\ntitle:%@,\\\\nsubtitle:%@,\\\\nbadge:%@,\\\\nsound:%@,\\\\nuserInfo:%@\\\\n}",body,title,subtitle,badge,sound,userInfo);
}
// Warning: UNUserNotificationCenter delegate received call to -userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: but the completion handler was never called.
completionHandler(); // 系统要求执行这个方法
}
@end
这里的hexToken
(Device Token
)是需要记录的,传到后端存起来。处理这个token的时候需要注意,重新安装App后其值发生变化,后端存储的时候要考虑到这种情况。比如更新用户的token信息。
如果不开发后端,想直接测试推送的话,可以使用SmartPush模拟推送,进行测试。里面涉及到一个证书的配置,可以看这篇文章:手把手教你实现iOS 远程推送
后端(PHP,框架CodeIgniter)
有token-based和certificate-based两种方式发送请求。
我这里用的是token-based,这种方式需要以下几个信息:
- Key(后缀名是
.p8
)文件和对应的Key ID - Team ID
- App的Bundle ID
- Device Token(App里获取,见上文)
1. 创建Key
2. Team ID
后端代码
拿到这些信息后,就可以发送推送请求了。
把下载下来的Key(后缀名是.p8
)文件上传到服务器上。
几个重要信息可以存储在其它地方,这里只是为了方便。
define('AUTH_KEY_PATH', '');
define('AUTH_KEY_ID', '');
define('TEAM_ID', '');
define('BUNDLE_ID', '');
private function generateAuthenticationHeader() {
$header = base64_encode(json_encode([
'alg' => 'ES256',
'kid' => AUTH_KEY_ID
]));
$claims = base64_encode(json_encode([
'iss' => TEAM_ID,
'iat' => time()
]));
$pkey = openssl_pkey_get_private('file://' . AUTH_KEY_PATH);
openssl_sign("$header.$claims", $signature, $pkey, 'sha256');
$signed = base64_encode($signature);
return "$header.$claims.$signed";
}
private function sendNotification($debug, $token, $payload) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'apns-topic: ' . BUNDLE_ID,
'authorization: bearer ' . $this->generateAuthenticationHeader(),
'apns-push-type: alert'
]);
// 如果正式环境,$debug=false,不然下面$response会出现BadDeviceToken的错误
$server = $debug ? 'api.development' : 'api';
$url = 'https://'.$server.'.push.apple.com/3/device/'.$token;
curl_setopt($ch, CURLOPT_URL, $url);
$response = curl_exec($ch);
if ($response === false) {
return ["curl_exec failed: " . curl_error($ch)];
}
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($code === 400 || $code === 410) {
$json = @json_decode($response);
return ['Reason: '.$json->reason, $code];
// if ($json->reason === 'BadDeviceToken') {
// // echo 'BadDeviceToken';
// }
}
curl_close($ch);
return [$response, $code];
}
public function testpush() {
$deviceToken = '';
$payload = [
"aps"=>[
"alert"=>[
"title"=>"Game Request",
"body"=>"Bob wants to play poker",
],
"badge"=>1,
],
];
$result = $this->sendNotification(true, $deviceToken, $payload);
echo json_encode($result);
}
payload
的具体设置可见 Creating the Remote Notification Payload
相关参考
- 手把手教你实现iOS 远程推送
- iOS10 最新远程通知 详细开发教程
- Setting Up a Remote Notification Server
- How can I convert my device token (NSData) into an NSString?
- SmartPush