iOS8 的 APNS(推送)-本地推送-服务器推送-地理位置推送

iOS Jan 5, 2015

iOS8 的推送改变了很多,一个一个来说吧。

从哪说起呢,感觉怎么这么乱。先列个列表吧。

  • 远程推送
  • 初级
  • 进阶(本地测试)(服务器测试)
  • 地理位置推送

服务器推送

先说服务器推送,使用之前必须先注册这个推送,用户要允许这个程序进行推送

最简单的方法:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 方法里

if (IOS8) {
        [[UIApplication sharedApplication] registerForRemoteNotifications];
        
        UIUserNotificationSettings *set = [UIUserNotificationSettings settingsForTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound categories:nil];
        [[UIApplication sharedApplication] registerUserNotificationSettings:set];
    }else{
        
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
         (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
    }

然后屏幕上就会询问是否允许推送

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken 方法中,可以得到 deviceToken 一般来说重装、更新都不会改变,手机还原、刷机才会改变。不过也不排除苹果突然用了什么新的算法,导致更改。所以妥当的做法是每次开启,都验证一次。

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error 方法里可以打印错误。

高级一点的做法

iOS8开放了通知的一些功能,比如左划处理

如果是未锁屏状态:

锁屏状态:

创建消息上面要添加的动作(按钮的形式显示出来)

UIMutableUserNotificationAction *action = [[UIMutableUserNotificationAction alloc] init];
action.identifier = @"action";  //按钮的标示
action.title=@"查看";  //按钮的标题
action.activationMode = UIUserNotificationActivationModeForeground;  //当点击的时候启动程序
//    action.authenticationRequired = YES;
//    action.destructive = YES;
UIMutableUserNotificationAction *action2 = [[UIMutableUserNotificationAction alloc] init];  //第二按钮
action2.identifier = @"action2";
action2.title=@"取消";
action2.activationMode = UIUserNotificationActivationModeBackground;  //当点击的时候不启动程序,在后台处理
action.authenticationRequired = YES;//需要解锁才能处理,如果action.activationMode = UIUserNotificationActivationModeForeground;则这个属性被忽略
action.destructive = YES;

创建动作(按钮)的类别集合

UIMutableUserNotificationCategory *categorys = [[UIMutableUserNotificationCategory alloc] init];
categorys.identifier = @"alert";//这组动作的唯一标示
[categorys setActions:@[action,action2] forContext:(UIUserNotificationActionContextMinimal)];

创建UIUserNotificationSettings,并设置消息的显示类类型

UIUserNotificationSettings *uns = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound) categories:[NSSet setWithObjects:categorys, nil]];

注册推送

[[UIApplication sharedApplication] registerUserNotificationSettings:uns];
[[UIApplication sharedApplication] registerForRemoteNotifications];

若要使用远程推送,满足两个条件:

  1. 用户需要调用注册用户推送 registerUserNotificationSettings;
  2. 在 info.plist 文件中UIBackgroundModes必须包含远程通知。

1 上面已经介绍了,具体说 2,打开项目设置,按图设置即可

离线push数据包带上特定Category字段(字段内容需要前后台一起定义,必须要保持一致),手机端收到时,就能展示上述代码对应Category设置的按钮,和响应按钮事件。

//payload example:  {"aps":{"alert":"杰哥是个天才!!", "sound":"default", "badge": 1, "category":"alert"}}

重大修改:离线push数据包之前能带的数据最多为256字节,现在APPLE将该数值放大到2KB。 这个应该是只针对IOS8的。

本地测试

- (void)applicationDidEnterBackground:(UIApplication *)application 方法中

    UILocalNotification *notification = [[UILocalNotification alloc] init];
    notification.fireDate=[NSDate dateWithTimeIntervalSinceNow:5];
    notification.timeZone=[NSTimeZone defaultTimeZone];
    notification.alertBody=@"测试推送的快捷回复";
    notification.category = @"alert";
    [[UIApplication sharedApplication]  scheduleLocalNotification:notification];

就有如下效果:

远程测试

我假设你已经有下面三个文件:

CSR文件

P12私有密钥文件,Push.p12

SSL证书文件,aps_development.cer

我们用的是php环境,所以制作pem证书即可,java 环境需要p12证书,详细教程下方提供几个。

  • 把.cer的SSL证书转换为.pem文件,执行命令
openssl x509 -in aps.cer -inform der -out apns-cer.pem
  • 把私钥Push.p12文件转化为.pem文件。首先输入Push.p12的密码,再给新文件设置密码(至少4位),确认一遍,完成
openssl pkcs12 -nocerts -out apns-key.pem -in EduPush.p12
  • 去除密码
openssl rsa -in apns-key.pem -out apns-key-nop.pem
  • 对生成的这两个pem文件再生成一个pem文件,来把证书和私钥整合到一个文件里
cat apns-key-nop.pem apns-cer.pem > cert.pem

php代码可以到这里下载,设置方法详见README文件

地理位置推送

这个推送是新的API才有的特性,必须配合CLLocation定位一起使用。

//Location Notification
CLLocationManager *locMan = [[CLLocationManager alloc] init];
locMan.delegate = self;
[locMan requestWhenInUseAuthorization];

#pragma mark - CLLocationManager

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status

{
     BOOL canUseLocationNotifications = (status == kCLAuthorizationStatusAuthorizedWhenInUse);
    if (canUseLocationNotifications) {
        [self startShowLocationNotification];
    }
}
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification

{
    CLRegion *region = notification.region;
    if (region) {
    }
}

- (void)startShowLocationNotification

{
    CLLocationCoordinate2D local2D ;
    local2D.latitude = 123.0;
    local2D.longitude = 223.0;
    UILocalNotification *locNotification = [[UILocalNotification alloc] init];
    locNotification.alertBody = @"你接收到了";
    locNotification.regionTriggersOnce = YES;
    locNotification.region = [[CLCircularRegion alloc] initWithCenter:local2D radius:45 identifier:@"local-identity"];
    [[UIApplication sharedApplication] scheduleLocalNotification:locNotification];
}

如果没有开启Core Location 那么上面的didReceiveLocalNotification不会被调用

Tags

Jie Li

🚘 On-road / 📉 US Stock / 💻 Full Stack Engineer / ®️ ENTJ