美文网首页PHP开发
iOS10推送(APNS)App+后端实现

iOS10推送(APNS)App+后端实现

作者: tomfriwel | 来源:发表于2021-10-26 16:47 被阅读0次

    Xcode里的相关配置如下:


    Capability设置

    APP实现

    获取Device Token和处理通知,直接贴代码:

    AppDelegate.h:

    #ifdef NSFoundationVersionNumber_iOS_9_x_Max
    #import <UserNotifications/UserNotifications.h>
    #endif
    
    @interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate>
    
    @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
    

    这里的hexTokenDevice Token)是需要记录的,传到后端存起来。处理这个token的时候需要注意,重新安装App后其值发生变化,后端存储的时候要考虑到这种情况。比如更新用户的token信息。

    如果不开发后端,想直接测试推送的话,可以使用SmartPush模拟推送,进行测试。里面涉及到一个证书的配置,可以看这篇文章:手把手教你实现iOS 远程推送

    后端(PHP,框架CodeIgniter)

    token-basedcertificate-based两种方式发送请求。

    我这里用的是token-based,这种方式需要以下几个信息:

    1. Key(后缀名是.p8)文件和对应的Key ID
    2. Team ID
    3. App的Bundle ID
    4. Device Token(App里获取,见上文)

    1. 创建Key

    创建Key
    输入Key Name并选择Apple Push Notifications service后 ,点击右上角Continue
    点击右上角Register
    下载Key文件(只能下一次,妥善保存),记录Key ID

    2. Team ID

    获取Team ID

    后端代码

    拿到这些信息后,就可以发送推送请求了。
    把下载下来的Key(后缀名是.p8)文件上传到服务器上。

    几个重要信息可以存储在其它地方,这里只是为了方便。

    define('AUTH_KEY_PATH', '<path-to-p8-file>');
    define('AUTH_KEY_ID', '<Key ID for your p8>');
    define('TEAM_ID', '<your team id>');
    define('BUNDLE_ID', '<your app bundle identifier>');
    
    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 = '<your device token>';
      $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

    相关参考

    相关文章

      网友评论

        本文标题:iOS10推送(APNS)App+后端实现

        本文链接:https://www.haomeiwen.com/subject/ebtyaltx.html