美文网首页ios2iOS Developer
史上最简单的消息推送

史上最简单的消息推送

作者: 函冰 | 来源:发表于2017-01-12 21:08 被阅读749次

    在iOS开发的过程中,涉及到两种推送,一种是本地推送,另一种就是远程推送。本地推送主要是在没有网络的情况下对用户进行事件或者消息的通知推送,例如系统自带的时间提醒和闹钟。远程推送则是必须要有网的情况下和苹果的APNs服务器建立长连接,从而推送消息,例如微信在后台的情况下,好友发来的信息,就是远程推送的内容。

    推送.jpg

    一、本地推送

    注:一个App最多只能设置64个本地推送,当超过此限制的时候,系统会自动忽略多余的本地推送,而保留能最快触发的64个。循环的本地推送会被系统认为是同一个本地推送。

    1. iOS 10之前的本地推送UILocalNotification

    1.1 注册本地推送

    UIUserNotificationType type = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
        UIUserNotificationSettings *setings = [UIUserNotificationSettings settingsForTypes:type categories:nil];
        
        [[UIApplication sharedApplication] registerUserNotificationSettings:setings];
    

    1.2 设置UILocalNotification的基本属性

    UILocalNotification *loc = [[UILocalNotification alloc] init];
        if (loc) {
            loc.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];
            loc.timeZone = [NSTimeZone defaultTimeZone];
            loc.alertBody = @"床前明月光";
            loc.repeatInterval = NSCalendarUnitMinute;
            loc.alertAction = NSLocalizedString(@"jump", nil);
            loc.alertTitle = NSLocalizedString(@"静夜思", nil);
            loc.soundName = UILocalNotificationDefaultSoundName;
            loc.applicationIconBadgeNumber = 1;
            loc.alertLaunchImage = @"1" ;
            
        }
        //调用此方法开启推送
        [[UIApplication sharedApplication] scheduleLocalNotification:loc];
    
    静夜思.jpg

    其中UILocalNotification的属性作用为

    fireDate:启动时间
    timeZone:启动时间参考的时区
    repeatInterval:重复推送时间(NSCalendarUnit类型),0代表不重复
    repeatCalendar:重复推送时间(NSCalendar类型)
    alertBody:通知内容
    alertAction:解锁滑动时的事件
    alertLaunchImage:启动图片,设置此字段点击通知时会显示该图片
    alertTitle:通知标题
    applicationIconBadgeNumber:收到通知时App icon的角标
    soundName:推送是带的声音提醒,设置默认的字段为UILocalNotificationDefaultSoundName
    userInfo:发送通知时附加的内容
    category:此属性和注册通知类型时有关联
     
    region:带有定位的推送相关属性
    regionTriggersOnce:带有定位的推送相关属性
    

    1.3 取消推送的方法

    //  取消某一个本地推送
    [[UIApplication sharedApplication] cancelLocalNotification:notification];
    //  取消所有的本地推送
    [[UIApplication sharedApplication] cancelAllLocalNotifications];
    

    1.4点击推送消息所调用事件

    //点击推送消息进入前台之后所要走的方法,当应用一直在前台的情况下每次进行推送都会触发这个方法
    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
    {
        NSLog(@"%@",notification);
    }
    

    1.5带有定位功能的本地推送,这个要配合CLLocation一起使用

    //设置位置管理者进行位置跟踪
    CLLocationManager *locManager = [[CLLocationManager alloc] init];
        locManager.delegate = self;
        [locManager requestWhenInUseAuthorization];
    //在位置信息改变的 时候调用下面的方法
    - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
        //检查状态判断App是否授权
        NSLog(@"%d",status);
        if (status == kCLAuthorizationStatusAuthorizedWhenInUse) {
            //  设置通知
            [self locationNotificWithlocation];
        }
    }
    //查看是否到了指定的位置
    - (void)locationNotificWithlocation
    {
        UILocalNotification *locNotification = [[UILocalNotification alloc] init];
        
        locNotification.alertBody = @"到达目的地!";
        locNotification.regionTriggersOnce = YES;
        CLLocationCoordinate2D loc = CLLocationCoordinate2DMake(22.28,114.15);
        locNotification.region = [[CLRegion alloc] initCircularRegionWithCenter:loc radius:1000000 identifier:@"北京"];
        
        [[UIApplication sharedApplication] scheduleLocalNotification:locNotification];
    }
    //发送通知之后调用下面的方法
    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
    {
        NSLog(@"%@",notification);
        if (notification.region) {
            NSLog(@"%@",notification.region);
        }
    }
    

    注:本地推送无论是在App处于什么状态,当有消息的时候,在手机下拉的Notifications中都是会有显示的。当App不是处于Foreground(前台)状态的时候,都是会有banner(横幅的弹窗)通知的,但是当App处于Foreground状态时,通知还是会一样发出,而且手机下拉Notifications中会有磁条通知,但是banner并不会出现。这样就会引起一个时间错觉问题。

    2.iOS 10之后的本地推送UNUserNotificationCenter
    iOS 10的时候系统废弃了之前的UILocalNotification,用专门的UNUserNotificationCenter处理推送的通知消息。
    下面就是iOS 10本地通知的发送方法

    //1. 注册通知
    // 使用 UNUserNotificationCenter 来管理通知
        UNUserNotificationCenter *centers = [UNUserNotificationCenter currentNotificationCenter];
        //监听回调事件
        centers.delegate = self;
        
        //iOS 10 使用以下方法注册,才能得到授权
        [centers requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge)
                              completionHandler:^(BOOL granted, NSError * _Nullable error) {
                                  // Enable or disable features based on authorization.
                              }];
        
        //获取当前的通知设置,UNNotificationSettings 是只读对象,不能直接修改,只能通过以下方法获取
        [centers getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            
        }];
    
    //2. 创建通知对象
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        center.delegate = self;
        
        UNMutableNotificationContent *noticontent = [[UNMutableNotificationContent alloc] init];
        noticontent.body = @"鹅鹅鹅,曲项向天歌";
        noticontent.badge = @1;
        noticontent.title = @"咏鹅";
        noticontent.userInfo = @{@"锄禾" : @"锄禾日当午"};
    //3. 设置触发机制
    UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:10 repeats:0];
    //4. 创建UNNotificationRequest通知请求对象
    UNNotificationRequest *req = [UNNotificationRequest requestWithIdentifier:@"id" content:noticontent trigger:trigger];
    //5. 通知中心发送通知
    [center addNotificationRequest:req withCompletionHandler:^(NSError * _Nullable error) {
            
        }];
    //在设置UNUserNotificationCenterDelegate之后,还可以监听通知的发送和接收事件
    - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
    {
        
    }
    - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler
    {
        
    }
    
    咏鹅.jpg
    注:在iOS10里面进行通知发送的时候要注意导入<UserNotifications/UserNotifications.h>这个类库,它包含几个新的类
    1. UNMutableNotificationContent通知内容
      通知内容就是设定通知的一些展示信息,iOS10之后可以设置subtitle。除了之前包含的内容外,声音的设置需要借助一个新类UNNotificationSound,通知文件要放到bundle里面。另外在实际的测试过程中发现,添加通知的声音有时候会无效。这应该是iOS10存在的一个bug,删除掉程序,再安装运行就好了。
    2. UNNotificationTrigger
      Trigger是新加入的一个功能,通过此类可设置本地通知触发条件。它一共有一下几种类型:
      1.UNPushNotificaitonTrigger 推送服务的Trigger,由系统创建
      2.UNTimeIntervalNotificaitonTrigger 时间触发器,可以设置多长时间以后触发,是否重复。如果设置重复,重复时长要大于60s
    1. UNCalendarNotificaitonTrigger 日期触发器,可以设置某一日期触发事件
    1. UNLocationNotificaitonTrigger 位置触发器,用于进行位置推送,到达指定范围之后,触发通知。通过CLRegion设定具体范围
    1. UNUserNotificationCenter通知中心 获取通知[UNUserNotificationCenter currentNotificationCenter],然后通过addNotificaitonRequest:就完成了一个通知的添加。

    二、 远程推送

    iOS远程推送的流程如下图:
    iOS远程推送原理.jpg
    1. 应用程序注册推送通知后,手机向APNS获取device token
    2. APNS将对应手机的device token 发送给注册通知的应用
    3. 注册通知的应用将device token发送给自己应用的服务器(若是第三方的推送服务则发送到相应的推送端服务器)储存起来
    4. 若要发送推送通知给用户则通过携带服务器储存的用户device token和推送详情发送给APNS
    5. APNS通过判断device token将推送通知发送给对应的手机应用程序

    要想完成远程推送,必不可少的就是对应项目的.p12文件,下面展示下证书的申请流程:

    1. 从钥匙串里的证书颁发机构申请CSR文件.png
    2. 申请推送项目的APP ID.png
    3.申请推送证书.png
    4选取对应的APP ID.png
    5.选取申请的CSR证书.png
    6.将cer文件拖进钥匙串,添加到如图所示的位置.png
    7.右击选取对应的证书导出.png
    8.导出所需要的.p12文件到指定位置.png

    保存好申请的.p12文件备用,特别提醒,生成.p12证书的密码要记得。

    A. 通过自己的服务器推送通知

    注:生成好的.p12文件发送给服务器就好,服务器同事会自行处理

    // 注册远程通知
    CGFloat version = [[[UIDevice currentDevice] systemVersion] floatValue];
            
            if (version >= 10.0)
            {
                UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
                [center requestAuthorizationWithOptions:UNAuthorizationOptionCarPlay | UNAuthorizationOptionSound | UNAuthorizationOptionBadge | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
                    
                    if (granted) {
                        NSLog(@" iOS 10 request notification success");
                    }else{
                        NSLog(@" iOS 10 request notification fail");
                    }
                }];
            }
            else if (version >= 8.0)
            {
                UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert categories:nil];
                [application registerUserNotificationSettings:setting];
            }else{
    //            iOS 7及以下
            UIRemoteNotificationType type = UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound;
            [application registerForRemoteNotificationTypes:type];
        }
            
            //注册通知
            [[UIApplication sharedApplication] registerForRemoteNotifications];
    
    
    // 发送deviceToken
    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
        // 将deviceToken转换成字符串并且通过网络请求发送到服务器
        NSString* dt = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
        NSString *token = [dt stringByReplacingOccurrencesOfString:@" " withString:@""];
         
          // 这里发个网络请求把deviceToken传给后台
    }
    // error
    -(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
        
    }
    // 消息
    - (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
    {
        // 可对userInfo进行处理
    }
    
    #pragma mark  iOS 10 获取推送信息 UNUserNotificationCenterDelegate  
      
    //APP在前台的时候收到推送的回调  
    - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler  
    {  
        UNNotificationContent *content =  notification.request.content;  
        NSDictionary *userInfo = content.userInfo;  
          
        [self handleRemoteNotificationContent:userInfo];  
          
        //可以执行设置 弹窗 和 声音  
        completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound);  
    }  
    //APP在后台,点击推送信息,进入APP后执行的回调  
    - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler  
    {  
        UNNotificationContent *content  = response.notification.request.content;  
        NSDictionary *userInfo = content.userInfo;  
          
        [self handleRemoteNotificationContent:userInfo];  
      
        completionHandler();  
    }  
      
    - (void)handleRemoteNotificationContent:(NSDictionary *)userInfo  
    {  
        NSLog(@" iOS 10 after Notificatoin message:\n %@",userInfo);  
    }
    
    #pragma mark iOS 10 之前 获取通知的信息  
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo  
    {  
        NSLog(@"iOS 10 before Notification message。\n  %@",userInfo);  
    } 
    

    特别提醒!!!推送的时候要打开下图位置的开关,不论是哪种远程推送的方法

    需要打开的位置一.png
    需要打开的位置二.png

    B. 第三方服务器推送服务(以极光推送为例)

    首先,成为第三方推送服务的开发者,并根据创建的.p12文件创建应用
    1. 登录到极光推送的官网注册为开发者并登录.png
    2. 创建应用.png
    3.上传相应的.p12文件和文件密码.png
    其次,下载对应的SDK,并将Lib文件拖入项目包内
    下载相应的SDK.png
    最后,根据开发者文档编写代码
    //以下代码均粘贴自开发者文档
    - (void)Jpush:(NSDictionary *)launchOptions
    {
        //Required
        //notice: 3.0.0及以后版本注册可以这样写,也可以继续用之前的注册方式
        JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
        entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound;
        if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
            // 可以添加自定义categories
            // NSSet<UNNotificationCategory *> *categories for iOS10 or later
            // NSSet<UIUserNotificationCategory *> *categories for iOS8 and iOS9
        }
        [JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
        
        /*
         appKey
         填写管理Portal上创建应用后自动生成的AppKey值。请确保应用内配置的 AppKey 与 Portal 上创建应用后生成的 AppKey 一致。
         channel
         指明应用程序包的下载渠道,为方便分渠道统计,具体值由你自行定义,如:App Store。
         apsForProduction
         1.3.1版本新增,用于标识当前应用所使用的APNs证书环境。
         0 (默认值)表示采用的是开发证书,1 表示采用生产证书发布应用。
         注:此字段的值要与Build Settings的Code Signing配置的证书环境一致
         */
        NSString *appkey = @"你创建应用的appkey";
        [JPUSHService setupWithOption:launchOptions appKey:appkey
                              channel:@"channel"
                     apsForProduction:0
                advertisingIdentifier:nil];
    }
    - (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
        
        /// Required - 注册 DeviceToken
        [JPUSHService registerDeviceToken:deviceToken];
    }
    - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
        //Optional
        NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error);
    }
    // iOS 10 Support
    - (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler {
        // Required
        NSDictionary * userInfo = notification.request.content.userInfo;
        if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
            [JPUSHService handleRemoteNotification:userInfo];
        }
        completionHandler(UNNotificationPresentationOptionAlert); // 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以选择设置
    }
    
    // iOS 10 Support
    - (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
        // Required
        NSDictionary * userInfo = response.notification.request.content.userInfo;
        if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
            [JPUSHService handleRemoteNotification:userInfo];
        }
        completionHandler();  // 系统要求执行这个方法
    }
    
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
        
        // Required, iOS 7 Support
        [JPUSHService handleRemoteNotification:userInfo];
        completionHandler(UIBackgroundFetchResultNewData);
    }
    
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
        
        // Required,For systems with less than or equal to iOS6
        [JPUSHService handleRemoteNotification:userInfo];
    }
    
    最最后,进行推送测试
    1.在极光的控制台上发送需要推送的消息.png 2.手机接收到推送的消息.png

    楼主吐血码完,腰酸背痛腿抽筋,能看完的都是亲人,那么就请亲人点个赞啦,拜谢!!!

    相关文章

      网友评论

        本文标题:史上最简单的消息推送

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