iOS 消息推送原理及简单实现(远程和本地)

    技术2023-08-30  93

    从图中可以很清楚的看出来推送的原理主要分为以下几步: 1.由App向iOS设备发送一个注册通知,用户需要同意系统发送推送。 2.iOS向APNs远程推送服务器发送App的Bundle Id和设备的UDID。 3.APNs根据设备的UDID和App的Bundle Id生成deviceToken再发回给App。 4.App再将deviceToken发送给远程推送服务器(自己的服务器), 由服务器保存在数据库中。 5.当自己的服务器想发送推送时, 在远程推送服务器中输入要发送的消息并选择发给哪些用户的deviceToken,由远程推送服务器发送给APNs。 6.APNs根据deviceToken发送给对应的用户。 · APNs 服务器就是苹果专门做远程推送的服务器。 ·deviceToken是由APNs生成的一个专门找到你某个手机上的App的一个标识码。 · deviceToken 可能会变,如果你更改了你项目的bundle Identifier或者APNs服务器更新了可能会变。

    再说一下简单实现,首先要去配置好证书,有调试和发布证书以及配置文件,这里不再细说。

    1.必须先进行注册,得到用户授权

     

    2.注册成功,接收从苹果服务器返回的唯一的设备token

     

    - (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSMutableString * devices_token = [NSMutableString stringWithFormat:@"%@",deviceToken]; }

    3.收到通知

     

    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { NSLog(@"%@", userInfo); }

    4.注册推送失败原因

     

    - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { NSLog(@"Registfail,注册推送失败原因%@",error); }

    iOS 本地推送(Local Push)  

    推送是我们平时开发中常用的一种机制,无论iOS还是Android系统都有推送,推送可以让不在前台运行的app,告知用户app内部发生的事情,可以提高app的打开次数,增加日活。 iOS中的推送分为 1.本地推送通知(Local Notification) 2.远程推送通知(Remote Notification) 我们在平时的开发中,使用远程推送可能比较多,远程推送依赖于服务器,需要联网才能收到,本地推送无需联网,添加好定时器即可在指定时间发送推送,平时使用场景多是闹钟,提醒等。

    这里要说一点,就是iOS系统限制了注册本地推送的数量,最大的注册量为64条。本片文章我们主要讲解本地推送

    2、本地推送(Local Push)

    无需联网即可推送不需要创建推送证书

    3、push交互(示例基于iOS8.0及以上)

    注册通知,获取用户授权 // 在AppDelegate.m中 // iOS10.0 需要导入 #import <UserNotifications/UserNotifications.h> - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [self registerAPN]; return YES; } // 注册通知 - (void)registerAPN { if (@available(iOS 10.0, *)) { // iOS10 以上 UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) { }]; } else {// iOS8.0 以上 UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:nil]; [[UIApplication sharedApplication] registerUserNotificationSettings:setting]; } } 添加通知 - (void)addLocalNotice { if (@available(iOS 10.0, *)) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; // 标题 content.title = @"测试标题"; content.subtitle = @"测试通知副标题"; // 内容 content.body = @"测试通知的具体内容"; // 声音 // 默认声音 // content.sound = [UNNotificationSound defaultSound]; // 添加自定义声音 content.sound = [UNNotificationSound soundNamed:@"Alert_ActivityGoalAttained_Salient_Haptic.caf"]; // 角标 (我这里测试的角标无效,暂时没找到原因) content.badge = @1; // 多少秒后发送,可以将固定的日期转化为时间 NSTimeInterval time = [[NSDate dateWithTimeIntervalSinceNow:10] timeIntervalSinceNow]; // NSTimeInterval time = 10; // repeats,是否重复,如果重复的话时间必须大于60s,要不会报错 UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:time repeats:NO]; /* //如果想重复可以使用这个,按日期 // 周一早上 8:00 上班 NSDateComponents *components = [[NSDateComponents alloc] init]; // 注意,weekday默认是从周日开始 components.weekday = 2; components.hour = 8; UNCalendarNotificationTrigger *calendarTrigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:YES]; */ // 添加通知的标识符,可以用于移除,更新等操作 NSString *identifier = @"noticeId"; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger]; [center addNotificationRequest:request withCompletionHandler:^(NSError *_Nullable error) { NSLog(@"成功添加推送"); }]; }else { UILocalNotification *notif = [[UILocalNotification alloc] init]; // 发出推送的日期 notif.fireDate = [NSDate dateWithTimeIntervalSinceNow:10]; // 推送的内容 notif.alertBody = @"你已经10秒没出现了"; // 可以添加特定信息 notif.userInfo = @{@"noticeId":@"00001"}; // 角标 notif.applicationIconBadgeNumber = 1; // 提示音 notif.soundName = UILocalNotificationDefaultSoundName; // 每周循环提醒 notif.repeatInterval = NSCalendarUnitWeekOfYear; [[UIApplication sharedApplication] scheduleLocalNotification:notif]; } } 移除通知 // 移除某一个指定的通知 - (void)removeOneNotificationWithID:(NSString *)noticeId { if (@available(iOS 10.0, *)) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center getPendingNotificationRequestsWithCompletionHandler:^(NSArray<UNNotificationRequest *> * _Nonnull requests) { for (UNNotificationRequest *req in requests){ NSLog(@"存在的ID:%@\n",req.identifier); } NSLog(@"移除currentID:%@",noticeId); }]; [center removePendingNotificationRequestsWithIdentifiers:@[noticeId]]; }else { NSArray *array=[[UIApplication sharedApplication] scheduledLocalNotifications]; for (UILocalNotification *localNotification in array){ NSDictionary *userInfo = localNotification.userInfo; NSString *obj = [userInfo objectForKey:@"noticeId"]; if ([obj isEqualToString:noticeId]) { [[UIApplication sharedApplication] cancelLocalNotification:localNotification]; } } } } // 移除所有通知 - (void)removeAllNotification { if (@available(iOS 10.0, *)) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center removeAllPendingNotificationRequests]; }else { [[UIApplication sharedApplication] cancelAllLocalNotifications]; } } 检查授权情况 - (void)checkUserNotificationEnable { // 判断用户是否允许接收通知 if (@available(iOS 10.0, *)) { __block BOOL isOn = NO; UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { if (settings.notificationCenterSetting == UNNotificationSettingEnabled) { isOn = YES; NSLog(@"打开了通知"); }else { isOn = NO; NSLog(@"关闭了通知"); [self showAlertView]; } }]; }else { if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone){ NSLog(@"关闭了通知"); [self showAlertView]; }else { NSLog(@"打开了通知"); } } } - (void)showAlertView { UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"通知" message:@"未获得通知权限,请前去设置" preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; [alert addAction:[UIAlertAction actionWithTitle:@"设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self goToAppSystemSetting]; }]]; [self presentViewController:alert animated:YES completion:nil]; } // 如果用户关闭了接收通知功能,该方法可以跳转到APP设置页面进行修改 - (void)goToAppSystemSetting { dispatch_async(dispatch_get_main_queue(), ^{ UIApplication *application = [UIApplication sharedApplication]; NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; if ([application canOpenURL:url]) { if (@available(iOS 10.0, *)) { if ([application respondsToSelector:@selector(openURL:options:completionHandler:)]) { [application openURL:url options:@{} completionHandler:nil]; } }else { [application openURL:url]; } } }); } 参数参考 //下面是iOS 8.0的,iOS10.0及以上类似,具体使用可查找 fireDate:启动时间 timeZone:启动时间参考的时区 repeatInterval:重复推送时间(NSCalendarUnit类型),0代表不重复 repeatCalendar:重复推送时间(NSCalendar类型) alertBody:通知内容 alertAction:解锁滑动时的事件 alertLaunchImage:启动图片,设置此字段点击通知时会显示该图片 alertTitle:通知标题,适用iOS8.2之后 applicationIconBadgeNumber:收到通知时App icon的角标 soundName:推送是带的声音提醒,设置默认的字段为UILocalNotificationDefaultSoundName userInfo:发送通知时附加的内容 category:此属性和注册通知类型时有关联,(有兴趣的同学自己了解,不详细叙述)适用iOS8.0之后 region:带有定位的推送相关属性,具体使用见下面【带有定位的本地推送】适用iOS8.0之后 regionTriggersOnce:带有定位的推送相关属性,具体使用见下面【带有定位的本地推送】适用iOS8.0之后

    通知示例

    补充:若要推送中添加图片,则可以添加以下代码:

    //3.下载图片 //3.1获取图片 NSString * imageUrlString = @"https://img1.doubanio.com/img/musician/large/22817.jpg"; NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrlString]]; //3.2图片保存到沙盒 NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; NSString *localPath = [documentPath stringByAppendingPathComponent:@"localNotificationImage.jpg"]; [imageData writeToFile:localPath atomically:YES]; //3.3设置通知的attachment(附件) if (localPath && ![localPath isEqualToString:@""]) { UNNotificationAttachment * attachment = [UNNotificationAttachment attachmentWithIdentifier:@"photo" URL:[NSURL URLWithString:[@"file://" stringByAppendingString:localPath]] options:nil error:nil]; if (attachment) { content.attachments = @[attachment]; } }

    4、总结

    本文主要介绍了本地通知的用法,将iOS8.0和10.0以上都进行了区分,具体的使用还需要大家根据具体情况而定,希望对大家有所帮助。 最后,附上Demo->>>>>传送门

    5、参考

    iOS10推送必看UNNotificationAttachment以及UNTimeIntervalNotificationTriggeriOS开发,本地推送的使用iOS系统与其他提示音汇总

    可能有的用户在使用的过程中出现了在iOS9以后调试出现错误信息:This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.

     

    Processed: 0.009, SQL: 9