iOS-APNs推送从证书配置到测试通知


一、证书相关

    1、首先我们要配置两个证书:推送测试证书、推送正式证书。   先打开AppID的推送功能


iOS-APNs推送从证书配置到测试通知_第1张图片
iOS-APNs推送从证书配置到测试通知_第2张图片

    在配置证书界面,Certificates-Development(这是开发证书即测试用,Production这是生产证书即正式)


iOS-APNs推送从证书配置到测试通知_第3张图片

        <1.测试证书:选择 Apple Push Notification service SSL (Sandbox)   选择 App ID   选择 创建mac的csr文件(csr创建步        骤:选择钥匙串访问工具-- 证书助理--从证书颁发机构请求证书--存到磁盘)


iOS-APNs推送从证书配置到测试通知_第4张图片

    下载并安装测试证书

        <2.正式证书:选择 App Store and Ad Hoc   其他与测试证书步骤一致

      2.为后台服务方提供p12文件

        不论是使用第三方(极光友盟等)还是后端人员自己写的推送,都需要将安装的证书导出为P12文件:在mac的钥匙串中,选择安装的推送证书,右键选择导出,选择.p12

(一定要是创建证书的电脑才可以导出为p12)。 有的推送第三方需要.pem文件,则需要对p12文件进行转换,具体:https://www.jianshu.com/p/cc952ea07a08?mType=Group

二、代码

    1.打开项目中的推送开关

        Capabilities--Push Notifications 若AppID的推送打开,且证书安装好,则Push Notifications下的两步都会打钩,证明准备步骤都已就绪


iOS-APNs推送从证书配置到测试通知_第5张图片

      2.各种方法的作用,Appdelegate要遵守UNUserNotificationCenterDelegate协议

                首先是启动方法didFinishLaunchingWithOptions,在这里注册推送服务

// 程序第一次启动的时候调用(点击通知启动 和 点击app图标启动 都会走这个方法,所以这种情况下 要在这里获取推送的内容)

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{

    self.isFirstLaunch = YES;

//这里是获取推送内容

    NSDictionary* remoteNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];

    if (remoteNotification) {

        //点击通知框进入app可以在此接收通知(只在杀死状态下可以接收到),之后会调用didReceiveRemoteNotification,因为此方法调用时,控制器已经创建完毕,可以跳转

    }else{

        //点击图标进入app

  }

//这里要注册推送服务

  if (kOS >= 10.0) { // 设置能够接收通知 的类型

      UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];               

        [center setDelegate:self];

      UNAuthorizationOptions type = UNAuthorizationOptionBadge|UNAuthorizationOptionSound|UNAuthorizationOptionAlert;

      [center requestAuthorizationWithOptions:type completionHandler:^(BOOL granted, NSError * _Nullable error) {

          if (granted) {

              Log(@"注册成功");

              [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {

                    Log(@"%@",settings);

                }];

          }else{

              Log(@"注册失败");

          }

      }];

  } else if (kOS >= 8.0){

      UIUserNotificationType notificationTypes = UIUserNotificationTypeBadge |

      UIUserNotificationTypeSound |

      UIUserNotificationTypeAlert;

      UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:notificationTypes categories:nil];

      [application registerUserNotificationSettings:settings];

  }

  // 注册通知

    [application registerForRemoteNotifications];

  return YES;

}

        接下来这是获得deviceToken的方法(deviceToken,是apns服务给每个设备的标示,通过这个标示才能推送到指定设备,且deviceToken是变化的,一般在应用卸载重装,或者系统升级才会改变)

// 1 每次程序启动都会调用

- (void)application:(UIApplication *)application

didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken

{

    //在这里把token传给后台

    NSString *deviceTokenStr = [[[[deviceToken description]

                                  stringByReplacingOccurrencesOfString:@"<" withString:@""]

                                stringByReplacingOccurrencesOfString:@">" withString:@""]

                              stringByReplacingOccurrencesOfString:@" " withString:@""];

  Log(@"deviceTokenStr:\n%@",deviceTokenStr);

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

    [defaults setObject:deviceTokenStr forKey:@"deviceToken"];

    [defaults synchronize];

//这个方法 是我向后端数据库更新token的请求,如果用第三方推送可忽略

    [PushService updateDeviceToken];

}

        didFailToRegisterForRemoteNotificationsWithError这个方法就不多说了,是token获取失败调用,我们可以在这里向后台注销这个用户的token

        willPresentNotification这个方法在iOS10才有的,用来设置应用在前台运行时,收到通知的提醒模型

#pragma mark- UNUserNotificationCenterDelegate(iOS10以上)

//在前台收到通知(程序运行时走此方法)

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{

  // 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以设置

    //如果加上这三个枚举,则用户即使在前台收到通知也会出现,有声音和角标。如果去掉在前台则不会收到

    completionHandler(UNNotificationPresentationOptionBadge|

                    UNNotificationPresentationOptionSound|

                    UNNotificationPresentationOptionAlert);

}

        didReceiveNotificationResponse是应用处于任何状态下,用户点击了推送通知的弹框,就会调用

// iOS 10之后 任何状态用户点击了通知 (用户点击了通知时走此方法)

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler{

    NSDictionary *userInfo = response.notification.request.content.userInfo;

//这里可以处理收到的推送消息

        if (userInfo[@"aps"][@"url"]) {

            [ToolsHelper receiveMessageAndPushcontroller:userInfo[@"aps"][@"url"]];

        }

    }

  completionHandler();

    return;

}

    didReceiveRemoteNotification  这个方法在iOS10以下,应用在前台的时候,有推送来,会直接来到这个方法,但通知栏不会有提示,角标也不会有,应用如果在后台后者关闭状态,点击推送来的消息也会来到这个方法,我们可以在这里处理业务逻辑(由于要处理iOS10以前设备在前台收到通知的提醒,所以要将通知转为本地通知进行弹窗提示)

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

  Log(@"userInfo: %@", userInfo.description);

  if ( application.applicationState == UIApplicationStateActive) {

        // 程序在运行过程中收到推送通知,转换成一个本地通知,显示到通知栏

        UILocalNotification *localNotification = [[UILocalNotification alloc] init];

        localNotification.userInfo = userInfo;

        localNotification.soundName = UILocalNotificationDefaultSoundName;

        localNotification.alertBody = [[userInfo objectForKey:@"aps"]objectForKey:@"alert"];

        localNotification.fireDate = [NSDate date];

        [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];

  } else {

      //在后台或者关闭状态收到推送通知

    }

  completionHandler(UIBackgroundFetchResultNewData);

}

以上就是常规推送需要处理的方法


在iOS10,推送已经可以推图片,git图,短视频了,也可以自定义弹窗的操作按钮。接下来就看一下,怎么推送简单的图片,这种推送有自己的UI和方法,也相当于一个target了。所以创建方法也跟普通推送不太一样。

    选择File-New-Target


iOS-APNs推送从证书配置到测试通知_第6张图片

创建后,系统会生成一个PushServiceEx的文件夹,里面有一个NotificationService的类,同时也会有一个PushServiceEx的target生成,如果未找到这个target  就在manger Schemes里面勾上。


iOS-APNs推送从证书配置到测试通知_第7张图片

下面是NotificationService类中的代码,这个类的方法会在手机接收到带图片的推送时调用,且负责下载这个图片,然后保存展示。(imageUrl是推送时,定义图片url的字段)

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {

    self.contentHandler = contentHandler;

    self.bestAttemptContent = [request.content mutableCopy];

    NSDictionary *dict =  self.bestAttemptContent.userInfo;

    NSDictionary *notiDict = dict[@"aps"];

    NSString *imgUrl = [NSString stringWithFormat:@"%@",notiDict[@"imageUrl"]];

  if (!imgUrl.length) {

      self.contentHandler(self.bestAttemptContent);

  }else{

//这是下载推片的方法

        [self loadAttachmentForUrlString:imgUrl withType:@"image" completionHandle:^(UNNotificationAttachment *attach) {

          if (attach) {

                self.bestAttemptContent.attachments = [NSArray arrayWithObject:attach];

            }

            self.contentHandler(self.bestAttemptContent);

        }];

    }

}

- (void)loadAttachmentForUrlString:(NSString *)urlStr

                          withType:(NSString *)type

                  completionHandle:(void(^)(UNNotificationAttachment *attach))completionHandler{

  __block UNNotificationAttachment *attachment = nil;

    NSURL *attachmentURL = [NSURL URLWithString:urlStr];

    NSString *fileExt = [self fileExtensionForMediaType:type];

  NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

    [[session downloadTaskWithURL:attachmentURL

                completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {

                    if (error != nil) {

                        NSLog(@"%@", error.localizedDescription);

                    } else {

                        NSFileManager *fileManager = [NSFileManager defaultManager];

                        NSURL *localURL = [NSURL fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:fileExt]];

                        [fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];

                      NSError *attachmentError = nil;

                        attachment = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:localURL options:nil error:&attachmentError];

                        if (attachmentError) {

//                            log(@"%@", attachmentError.localizedDescription);

                        }

                    }

                  completionHandler(attachment);

                }] resume];

}

- (NSString *)fileExtensionForMediaType:(NSString *)type {

    NSString *ext = type;

  if ([type isEqualToString:@"image"]) {

        ext = @"jpg";

    }

  if ([type isEqualToString:@"video"]) {

        ext = @"mp4";

    }

  if ([type isEqualToString:@"audio"]) {

        ext = @"mp3";

    }

  return [@"." stringByAppendingString:ext];

}


要完成这个图片推送  PushServiceEx  target有几个要注意的点:

    第一个:测试一定要选择这个target进行安装。

    第二个:这个target支持的development target 版本一定要填对,我之前就因为填了iOS11,但是用10的机子测了半天都只是普通推送没有图片。

    第三个:target的版本不能小于项目的版本,否则会在提交应用到appstore的时候,有警告

    第四个: "mutable-content" : 1  推送时一定要有这个参数,不然就会被手机认为是普通推送。推送的json应该像这样 。   

{

    "aps" : {

        "alert" : "Your message here.",

        "badge" : 9,

        "sound" : "default",

        "mutable-content" : 1,

        "imageUrl" : "https://www.xxxxxxxx.png"

    },

}


三、测试

    1.推送工具

        如果后台的小伙伴没时间跟我们测试推送,就可以自己用推送小工具,只需要我们下载的推送证书(后缀为cer的文件),自己进行推送测试,这里是工具地址:https://itunes.apple.com/cn/app/easy-apns-provider/id989622350?mt=12

注意,推送只能进行真机测试,模拟器是没有token的。 我们需要添加要推送设备的token,然后选择证书,如果是正式就选连接至sandbox那个选项。然后就可以开始推送了

iOS-APNs推送从证书配置到测试通知_第8张图片

    2.推送的环境说明

    不论是这个小工具还是服务器推送,如果APP是生产环境(Appstore下载的或Adhoc环境),则选择正式证书。我们自己开发安装的则选择测试证书。所以我们是没办法用生产证书进行上线前测试的(Adhoc除外)

你可能感兴趣的:(iOS-APNs推送从证书配置到测试通知)