小米推送服务iOS客户端SDK使用指南

 

小米推送iOS版提供存量用户无缝迁移方案
小米推送现已支持批量导入DeviceToken的功能,可以帮助您将IOS推送无缝迁移到小米推送服务上。

迁移方法是给我们提供所有现有设备的DeviceToken列表(CSV或TXT格式,每行一个DeviceToken),我们会为这拨设备直接生成小米推送的设备标识(regid),这样即使这批用户不升级到包含小米推送SDK的应用新版本也依然能收到通过小米推送服务发出的消息。

如有迁移需求,请在集成小米推送的应用版本正式发布前联系我们进行导入
导出方式:通过推送客服系统反馈,说明来意即可

如对小米推送有任何技术问题,也请通过推送客服系统与我们联系http://dev.xiaomi.com/mipush/feedback/fe/

修订历史
2014年3月 草稿
2014年4月 正式版
2014年9月 Add 常见问题
2015年5月 Add 应用内长连接
在集成过程中遇到任何问题,请联系我们:DevPush@xiaomi.com

1. 概要

SDK下载地址:http://dev.xiaomi.com/mipush/downpage/

iOS版本包括两部分。

1. 基于Apple的APNs(Apple Push Notification Service)

2.应用内长连接(即在App启动并运行在前台时,SDK内部会运行一个Socket长连接连接到Server端,以接收消息推送。此长连接跟APNs时分开的,不经过APNs服务器)
注意:测试环境暂时不提供长连接服务。如需调试长连接,请使用online环境

SDK以.a动态链接库的形式提供, 开发者只需在原有编码基础上添加少许代码,来注册小米推送服务与绑定手机设备。
客户端在注册成功后,会得到服务器颁发的regId(终端唯一标示),服务端通过regId发消息给终端。
除此以外,客户端还可以通过订阅Topic、设置别名Alias、帐号Acount来收发推送消息。

2. 使用说明

这里介绍如何配置和使用小米推送服务,您也可以参照Demo来配置和使用小米推送服务。

在使用小米推送服务前,开发者需要先登录小米开发者网站http://developer.xiaomi.com。注册App,申请对应的AppID, AppKey, AppSecret。

其中AppID和AppKey是客户端的应用标识,在客户端SDK初始化时使用;AppSecret是应用私钥,在使用服务器 SDK向客户端发送消息时使用。

2.1. 创建APNs证书

APNs证书是一种扩展名为p12的文件,它是我们发送消息给APNs的证明。在开发时我们分Development环境与Distribution环境。所以p12会有两个版本:Development
与 Distribution。NOTE: 创建证书时请携带密码。

1.在Mac中,开启Keychina Access
2.选中你对应bundleID 的证书,注意 Development/Distribution,不用选中对应的专用秘钥。右键Export。
Apple Push Services: com.xxx.xxx:线上证书
Apple Development IOS Push Services: com.xxx.xxx:测试证书
QQ20160622-2

 

3.Save。注意文件格式

4.保存时需要你给证书设定密码
5.上传证书到小米推送服务
QQ20160622-3

2.2. 搭建XCode运行环境

我们的libMiPushSDK库文件同时包含i386、x86_64、arm64、armv6和armv7的代码,所以libMiPushSDK将同时支持真机与模拟器。但由于Apple推送不支持模拟器,请使用真机测试功能。

  1. 小米推送服务IOS版支持的最低系统版本iOS5.0
  2. 添加libMiPushSDK.a , MiPushSDK.h 到工程
  3. 引入库 UserNotifications.framework(iOS10+), libresolv.dylib, libxml2.dylib, libz.dylib,
    SystemConfiguration.framework,MobileCoreServices.framework,CFNetwork.framework,CoreTelephony.framework
    (如果已经引入,请忽略)
  4. target的Capabilities选项卡打开Push Notifications(如果Xcode版本没有此选项忽略)

2.3. 配置SDK运行参数

运行SDK注册小米推送服务的时候,需要使用AppID,AppKey,等参数来验证使用者的合法性。

  1. 打开工程下资源文件Info.plist
  2. 文件为源代码形式打开,添加以下信息
    <dict>
    	<key>MiSDKAppID</key>
    	<string>1000888</string>
    	<key>MiSDKAppKey</key>
    	<string>500088888888</string>
    	<key>MiSDKRun</key>
    	<string>Online</string>
    </dict>
  3. 如果开发版本较多,修改连接环境比较繁琐,提供以下方式。
    !--请注意MiSDKRun写法, 用buildSetting里面的宏替换--
    <dict>
    	<key>MiSDKAppID</key>
    	<string>1000888</string>
    	<key>MiSDKAppKey</key>
    	<string>500088888888</string>
    	<key>MiSDKRun</key>
    	<string>${MiSDKRun}</string>
    </dict>

    其中在Build Settings中添加添加宏MiSDKRun的方法为:PastedGraphic-9-1PastedGraphic-8

  4. MiSDKAppID, MiSDKAppKey 为在小米开发者网站http://developer.xiaomi.com,注册App后的AppID,AppKey。MiSDKRun 是设定SDK是连接Development环境/Distribution环境 对应参数为debug/online

2.4. 注册推送服务,绑定手机设备

工作流程:

  1. 当客户端应用启动时,调用以下代码注册推送服务注册时需要设置MiPushSDKDelegate
    // 只启动APNs.
     [MiPushSDK registerMiPush:self];
    // 同时启用APNs跟应用内长连接
     [MiPushSDK registerMiPush:self type:0 connect:YES];
  2. 推送服务注册成功后,SDK会请求APNs建立连接并获得deviceToken,请求获得deviceToken成功后请调用以下代码来绑定手机设备
     #pragma mark UIApplicationDelegate
     - (void)application:(UIApplication *)app 
             didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
     {
        // 注册APNS成功, 注册deviceToken
        [MiPushSDK bindDeviceToken:deviceToken];
     }
    
     - (void)application:(UIApplication *)app 
             didFailToRegisterForRemoteNotificationsWithError:(NSError *)err
     {
        // 注册APNS失败
        // 自行处理
     }

2.5. 实现MiPushSDKDelegate回调

SDK中所有请求操作都是异步的。调用成功与失败都会通过MiPushSDKDelegate来通知。
其中包括:注册小米推送,绑定deviceToken,设置Alias,订阅Topic,帐号Account,App统计打点等。

  1. 在AppDelegate.h中加入MiPushDelegate协议
     #import "MiPushSDK.h"

     // <--
    
     @interface MPAppDelegate : UIResponder
     
<
        MiPushSDKDelegate,      // <--
        UNUserNotificationCenterDelegate,      // <-- iOS10+
        UIApplicationDelegate

     >
     @property (strong, nonatomic) UIWindow *window;
      
     @end
  2. 在AppDelegate.m中实现回调方法
     #pragma mark MiPushSDKDelegate
      
     - (void)miPushRequestSuccWithSelector:(NSString *)selector data:(NSDictionary *)data
     {
          // 请求成功
          // 可在此获取regId
          if ([selector isEqualToString:@"bindDeviceToken:"]) {
                NSLog(@"regid = %@", data[@"regid"]);
          }
     }
      
     - (void)miPushRequestErrWithSelector:(NSString *)selector error:(int)error data:(NSDictionary *)data
     {
          // 请求失败
     }

2.6. 接收消息处理

启动长连接的话会分别收到APNs消息和长连接消息

  1. 长连接消息
    当App启动并运行在前台时,SDK内部会运行一个Socket长连接连接到Server端,以接收消息推送。收到消息时回通过MiPushSDKDelegate来通知。
    在AppDelegate.m中实现回调方法

     - ( void )miPushReceiveNotification:( NSDictionary *)data
     {
         // 长连接收到的消息。消息格式跟APNs格式一样
     }
  2. APNs消息
    同一条消息会通过APNs跟长连接分别收到。所以需要使用下面代码把APNs消息导入到SDK内部进行去重复。
    注意:处理点击通知进入app的逻辑需要放在点击进入app的苹果原生回调中。

     
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
        vMain = [[MPViewController alloc] init];
        vMain.iDelegate = self;
        UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vMain];
        self.window.rootViewController = nav;
        [self.window makeKeyAndVisible];
    
        NSLog(@"%@", [MiPushSDK getRegId]);
        [MiPushSDK registerMiPush:self type:0 connect:YES];
        
        // 处理点击通知打开app的逻辑
        NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
        if(userInfo){//推送信息
            NSString *messageId = [userInfo objectForKey:@"_id_"];
            if (messageId!=nil) {
                [MiPushSDK openAppNotify:messageId];
            }
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"gg" message:[NSString stringWithFormat:@"%@", userInfo] preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *act = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
                
            }];
            [alert addAction:act];
            [nav presentViewController:alert animated:YES completion:nil];
        }
        return YES;
    }
    
    - ( void )application:( UIApplication *)application didReceiveRemoteNotification:( NSDictionary *)userInfo
     {
         [ MiPushSDK handleReceiveRemoteNotification :userInfo];
         // 使用此方法后,所有消息会进行去重,然后通过miPushReceiveNotification:回调返回给App
     }
    
    // iOS10新加入的回调方法
    // 应用在前台收到通知
    - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
        NSDictionary * userInfo = notification.request.content.userInfo;
        if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
            [MiPushSDK handleReceiveRemoteNotification:userInfo];
        }
        //    completionHandler(UNNotificationPresentationOptionAlert);
    }
    
    // 点击通知进入应用
    - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
        NSDictionary * userInfo = response.notification.request.content.userInfo;
        if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
            [MiPushSDK handleReceiveRemoteNotification:userInfo];
        }
        completionHandler();
    }
    
    

2.7. 别名Alias / 订阅Topic / 帐号Account

注册推送服务,绑定手机设备成功后,即可调用以下方法

 // 设置别名           
 [MiPushSDK setAlias:@“alias”]
  
 // 订阅内容
 [MiPushSDK subscribe:@“topic”] 

 // 设置帐号
 [MiPushSDK setAccount:@“account”]

Alias可以理解为regId的别名,开发者可以将alias设置为自己应用帐号系统的帐号,或者设备标识等。然后在使用ServerSDK发送消息的时候,即可直接指定发送给特定的alias,而不是regId,避免存储regId。

主题用来做广播消息。不同手机上的同一个App可以订阅同一个主题。通过发送主题消息的API,即可同时向所有订阅该主题的客户端发送消息。比如,您有一个新闻类的App,可以自定义“财经”、“体育”、“科技“等主题;对于经常阅读财经新闻的用户,您可以帮用户订阅“财经”类主题,当有新的财经要闻发生时,直接通过主题推送该新闻给所有订阅该主题的用户。

帐号,多设备设置同一个帐号, 发送消息时多设备可以同时收到。一般在多点登陆时使用,多设备使用同一个帐号,他们可以同时收到消息。

2.8. 解析消息,并记录开启事件

当App工作在前台或后台,收到推送消息后,请实现下面代码,已统计用户开启App事件。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ......
    [MiPushSDK registerMiPush:self type:0 connect:YES];
    NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    if(userInfo){
        NSString *messageId = [userInfo objectForKey:@"_id_"];
        if (messageId!=nil) {
            [MiPushSDK openAppNotify:messageId];
        }
    }
    ......
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    NSString *messageId = [userInfo objectForKey:@"_id_"];
    [MiPushSDK openAppNotify:messageId];
}

iOS10+

// 点击通知进入应用
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
    NSDictionary * userInfo = response.notification.request.content.userInfo;
    if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        [MiPushSDK handleReceiveRemoteNotification:userInfo];
        [MiPushSDK openAppNotify:messageId];
    }
    completionHandler();
}

当iOS7
你使用RemoteNotification时,请实现下面代码

- (void)application:(UIApplication *)application 
    didReceiveRemoteNotification:(NSDictionary *)userInfo 
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    NSString *messageId = [userInfo objectForKey:@"_id_"];
    [MiPushSDK openAppNotify:messageId];
}

2.9. iOS8推送消息快速回复

当用户收到消息的时候,提供一个快速回复的按钮。具体信息可参考

https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/IPhoneOSClientImp.html

#pragma mark MiPushSDKDelegate
- (void)miPushRequestSuccWithSelector:(NSString *)selector data:(NSDictionary *)data
{    
    ...
    if ([selector isEqualToString:@"bindDeviceToken:"]) {
        
        UIMutableUserNotificationAction *action = [[UIMutableUserNotificationAction alloc] init];
        action.identifier = @"action1"; //按钮的标示
        action.title=@"启动"; //按钮的标题
        action.activationMode = UIUserNotificationActivationModeForeground;//当点击的时候启动程序
        
        UIMutableUserNotificationAction *action2 = [[UIMutableUserNotificationAction alloc] init];
        action2.identifier = @"action2";
        action2.title=@"忽略";
        action2.activationMode = UIUserNotificationActivationModeBackground;//当点击的时候不启动程序,在后台处理
        action.authenticationRequired = YES;//需要解锁才能处理
        action.destructive = YES;
        
        UIMutableUserNotificationCategory *categorys = [[UIMutableUserNotificationCategory alloc] init];
        categorys.identifier = @"category"; //这组动作的唯一标示
        [categorys setActions:@[action,action2] forContext:(UIUserNotificationActionContextMinimal)];
        
        UIUserNotificationSettings *uns = [UIUserNotificationSettings settingsForTypes:
                  (UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound)
                  categories:[NSSet setWithObjects:categorys, nil]];
        [[UIApplication sharedApplication] registerUserNotificationSettings:uns];
    }
}

2.10. 开发环境/生产环境之间的切换

为了方便开发调试,除了Distribution服务器外,我们还提供Development服务器。为了避免影响已发布的App,您可以在Development服务器上开发调试,开发完毕再切换到Distribution服务器。

Apple签名APNs证书也分Development/Distribution。并且终端应用在不同环境下获得的deviceToken也不同。

开启Info.plist文件,修改MiSDKRun参数。debug:Development服务器。online:Distribution服务器

2.11. iOS10+通知新特性

iOS10之后苹果推送有了一些新的特性,小米推送提供的对应的支持:
一、title, subtitle, body,可以用新版Server SDK 发送,客户端不需要修改
二、 添加图片:
客户端需要创建Notification Service Extension,并添加代码,步骤如下:
1. File->New->Target,创建Notification Service Extension
2. 保证Embed in Application选择正确的target。
Create Notification Service
值得注意的是Notification Service Extension有自己的Bundle Id(比如:com.xiaomi.mipush.NotificationService),也就是说需要为它创建Apple App ID和Provisioning profile。
3. 在NotificationService.m添加如下代码:

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    // 下面是小米推送的url优先级高,可自行修改
    NSString *imageUrlString = [request.content.userInfo objectForKey:@"mutable_content_pic_url"]; // 小米推送image的url字段
    if (![imageUrlString isKindOfClass:[NSString class]]) {
        imageUrlString = [request.content.userInfo objectForKey:@"your_image_url_field"]; // 自己的url
        if (![imageUrlString isKindOfClass:[NSString class]]) {
            return;
        }
    }
    
    NSURL *url = [NSURL URLWithString:imageUrlString];
    if (!url)
        return;
    
    [[[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (!error) {
            NSString *tempDict = NSTemporaryDirectory();
            
            NSString *filenameSuffix = response.suggestedFilename ? response.suggestedFilename : [response.URL.absoluteString lastPathComponent];
            NSString *attachmentID = [[[NSUUID UUID] UUIDString] stringByAppendingString:filenameSuffix];
            NSString *tempFilePath = [tempDict stringByAppendingPathComponent:attachmentID];
            
            if ([[NSFileManager defaultManager] moveItemAtPath:location.path toPath:tempFilePath error:&error]) {
                UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:attachmentID URL:[NSURL fileURLWithPath:tempFilePath] options:nil error:&error];
                
                if (!attachment) {
                    NSLog(@"Create attachment error: %@", error);
                } else {
                    _bestAttemptContent.attachments = [_bestAttemptContent.attachments arrayByAddingObject:attachment];
                }
            } else {
                NSLog(@"Move file error: %@", error);
            }
        } else {
            NSLog(@"Download file error: %@", error);
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            self.contentHandler(self.bestAttemptContent);
        });
    }] resume];
}

4. 利用Server SDK设置mutable-content字段,并在extra中增加字段imageUrl(图片的地址)

2.12. 自定义通知声音

自定义通知声音需要满足以下条件:
1. 声音文件必须放在app bundle中
2. 声音文件的格式只支持aiff, wav, caf
3. 声音文件的时长不能超过30秒,否则会用默认系统通知声音替代。
具体做法:
1. 将声音文件 pushsound.aif 拖入Xcode工程的main bundle的目录下
mipush_copy_file
2. 发送推送的时候设置SDK自定义声音字段 soundURL = “pushsound.aif”,在收到消息的中,”aps”: { “sound” : “pushsound.aiff”}

3. API说明

MiPushSDK.h 分两部分 @interface MiPushSDK ,@protocol MiPushSDKDelegate

3.1. @interface MiPushSDK : NSObject

MiPushSDK是小米推送服务在iOS平台的接入类。单实例。并提供了一系列静态方法。

表 1. 

API 功能 使用场景
+ (void)registerMiPush:(id<MiPushSDKDelegate>)delegate 客户端注册设备 由开发者选择注册MiPushSDK时机。建议在程序启动时调用此方法
+ (void)registerMiPush:(id<MiPushSDKDelegate>)delegate type:(UIRemoteNotificationType)type 客户端注册设备,并设置消息类型 由开发者选择注册MiPushSDK时机。建议在程序启动时调用此方法
+ (void)registerMiPush:(id<MiPushSDKDelegate>)delegate type:(UIRemoteNotificationType)type connect:(BOOL)connect; 客户端注册设备,并设置消息类型与是否使用长连接 由开发者选择注册MiPushSDK时机。建议在程序启动时调用此方法
+ (void)unregisterMiPush 客户端设备注销 当App选择停止推送功能的时候调用此方法。
注意:iOS10之后此操作会导致一段时间无法重新注册设备。
+ (void)bindDeviceToken:(NSData *)deviceToken 绑定 PushDeviceToken 代码必须写在如下方法里。 – (void)application:(UIApplication *)app
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
+ (void)setAlias:(NSString *)alias 为指定用户设置别名 在成功注册设备并绑定DeviceToken后调用
+ (void)unsetAlias:(NSString *)alias 取消指定用户的别名 在成功注册设备并绑定DeviceToken后调用
+ (void)setAccount:(NSString *)account 为指定用户设置帐号 在成功注册设备并绑定DeviceToken后调用
+ (void)unsetAccount:(NSString *)account 取消指定用户的帐号 在成功注册设备并绑定DeviceToken后调用
+ (void)subscribe:(NSString *)topic 为某个用户设置订阅主题 在成功注册设备并绑定DeviceToken后调用
+ (void)unsubscribe:(NSString *)topic 取消某个用户的订阅主题 在成功注册设备并绑定DeviceToken后调用
+ (void)getAllAliasAsync 获取客户端设置的别名。 在成功注册设备并绑定DeviceToken后调用
+ (void)getAllTopicAsync 获取客户端订阅的主题。 在成功注册设备并绑定DeviceToken后调用
+ (void)getAllAccountAsync 获取客户端订阅的帐号。 在成功注册设备并绑定DeviceToken后调用
+ (void)openAppNotify:(NSString *)messageId 统计客户端通过推送,而开启App的行为。 在成功注册设备并绑定DeviceToken后调用
+ (NSString*)getSDKVersion 获取SDK版本号。
+ (NSString*)getRegId 获取设备的regId 在成功注册设备并绑定DeviceToken后调用,SDK版本号2.2.2及之后可用

 

3.2. @protocol MiPushSDKDelegate <NSObject>

MiPushSDKDelegate SDK的所有请求都是异步操作,用户需监听此方法。

表 2. 

API 功能
- (void)miPushRequestSuccWithSelector:(NSString *)selector data:(NSDictionary
*)data
当请求成功时返回
- (void)miPushRequestErrWithSelector:(NSString *)selector error:(int)error
data:(NSDictionary *)data
当请求失败时返回

 

3.3. 方法详细说明

3.3.1. + (void)registerMiPush:(id<MiPushSDKDelegate>)delegate;

建议在程序启动时调用此方法,代码放到以下代码块中。这是因为,如果用户重装了iOS或者用户换了设备并且恢复程序备份到一个新的设备,都将导致这个token值不一样。

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

表 3. 

参数列表 参数说明
(id<MiPushSDKDelegate>)delegate 加入请求成功与失败的监听

 

3.3.2. + (void)registerMiPush:(id<MiPushSDKDelegate>)delegate type:(UIRemoteNotificationType)type;

建议在程序启动时调用此方法,代码放到以下代码块中。这是因为,如果用户重装了iOS或者用户换了设备并且恢复程序备份到一个新的设备,都将导致这个token值不一样。

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

表 4. 

参数列表 参数说明
(id<MiPushSDKDelegate>)delegate 加入请求成功与失败的监听
type:(UIRemoteNotificationType)type 消息类型:UIRemoteNotificationTypeBadge, UIRemoteNotificationTypeSound, UIRemoteNotificationTypeAlert, UIRemoteNotificationTypeNewsstandContentAvailability

 

3.3.3. + (void)registerMiPush:(id<MiPushSDKDelegate>)delegate type:(UIRemoteNotificationType)type connect:(BOOL)connect;

建议在程序启动时调用此方法,代码放到以下代码块中。这是因为,如果用户重装了iOS或者用户换了设备并且恢复程序备份到一个新的设备,都将导致这个token值不一样。

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

表 5. 

参数列表 参数说明
(id<MiPushSDKDelegate>)delegate 加入请求成功与失败的监听
type:(UIRemoteNotificationType)type 消息类型:UIRemoteNotificationTypeBadge, UIRemoteNotificationTypeSound, UIRemoteNotificationTypeAlert, UIRemoteNotificationTypeNewsstandContentAvailability
connect:(BOOL)connect 是否启动应用内长连接

 

3.3.4. + (void)unregisterMiPush;

注意:此操作会导致一段时间内无法获取deviceToken而导致再次注册失败。苹果建议尽量少用注销操作:https://developer.apple.com/reference/uikit/uiapplication/1623093-unregisterforremotenotifications?language=objc

当客户端决定关闭推送功能的时候调用此方法。并且需要注意,在下次程序启动的时候不要调用registerMiPush:。否则推送又将被开启

小米推送服务关闭后,在调用其他功能性API都将提示错误。

3.3.5. + (void)bindDeviceToken:(NSData *)deviceToken

向MiPush注册deviceToken。为保证消息可达,必须在系统application:didRegisterForRemoteNotificationsWithDeviceToken:调用。SDK内部会处理是否重新同步上传

表 6. 

参数列表 参数说明
(NSData *)deviceToken 设备的唯一标示,有Apple分配。
application:didRegisterForRemoteNotificationsWithDeviceToken: 返回的内容.

 

3.3.6. + (void)setAlias:(NSString *)alias;

开发者可以为指定用户设置别名,然后给这个别名推送消息,效果等同于给regId推送消息。注:
一个regId可以被设置多个别名,如果设置的别名已经存在,会覆盖掉之前的别名。不要超过100个

在成功注册设备并绑定DeviceToken后调用,当关闭推送功能后再调用此方法将会失败

表 7. 

参数列表 参数说明
(NSString *)alias 为指定用户设置别名

 

3.3.7. + (void)unsetAlias:(NSString *)alias

开发者可以取消指定用户的某个别名,服务器就不会给这个别名推送消息了。

在成功注册设备并绑定DeviceToken后调用,当关闭推送功能后再调用此方法将会失败

表 8. 

参数列表 参数说明
(NSString *)alias 为指定用户设置别名

 

3.3.8. + (void)setAccount:(NSString *)account;

开发者可以为指定用户设置帐号,然后给这个帐号推送消息,效果等同于给regId推送消息。注: 一个regId可以被设置多个帐号。不要超过100个

在成功注册设备并绑定DeviceToken后调用,当关闭推送功能后再调用此方法将会失败

表 9. 

参数列表 参数说明
(NSString *)account 为指定用户设置帐号

 

3.3.9. + (void)unsetAccount:(NSString *)account;

开发者可以取消指定用户的某个帐号,服务器就不会给这个帐号推送消息了。

在成功注册设备并绑定DeviceToken后调用,当关闭推送功能后再调用此方法将会失败

表 10. 

参数列表 参数说明
(NSString *)account 为指定用户设置帐号

 

3.3.10. + (void)subscribe:(NSString *)topic

为某个用户设置订阅主题;根据用户订阅的不同主题,开发者可以根据订阅的主题实现分组群发。

支持同时设置多个topic, 中间使用”,”分隔

在成功注册设备并绑定DeviceToken后调用,当关闭推送功能后再调用此方法将会失败

表 11. 

参数列表 参数说明
(NSString *)topic 某个用户订阅的主题

 

3.3.11. + (void)unsubscribe:(NSString *)topic

为某个用户取消某个订阅主题。

支持同时设置多个topic, 中间使用”,”分隔

在成功注册设备并绑定DeviceToken后调用,当关闭推送功能后再调用此方法将会失败

表 12. 

参数列表 参数说明
(NSString *)topic 某个用户取消订阅的主题

 

3.3.12. + (void)openAppNotify:(NSString *)messageId

统计客户端,通过推送开启App行为。如果你想使用MiPush服务器帮你统计点击量 请自行调用此方法。然后登陆推送后台推送统计查看数据。

在成功注册设备并绑定DeviceToken后调用,当关闭推送功能后再调用此方法将会失败

表 13. 

参数列表 参数说明
(NSString *)messageId 每条通知都会有一条唯一索引, 保存在Payload里面的miid。统计时需要携带此参数。以作为消息跟踪

 

3.3.13. + (void)getAllAliasAsync

获取客户端设置的别名列表。

异步调用,调用之后会回调函数- (void)miPushRequestSuccWithSelector:(NSString *)selector data:(NSDictionary *)data

在成功注册设备并绑定DeviceToken后调用,当关闭推送功能后再调用此方法将会失败

3.3.14. + (void)getAllAccountAsync;

获取客户端设置的帐号列表。

异步调用,调用之后会回调函数- (void)miPushRequestSuccWithSelector:(NSString *)selector data:(NSDictionary *)data

在成功注册设备并绑定DeviceToken后调用,当关闭推送功能后再调用此方法将会失败

3.3.15. + (void)getAllTopicAsync;

获取客户端订阅的主题列表。

异步调用,调用之后会回调函数- (void)miPushRequestSuccWithSelector:(NSString *)selector data:(NSDictionary *)data

在成功注册设备并绑定DeviceToken后调用,当关闭推送功能后再调用此方法将会失败

3.3.16. + (NSString*)getSDKVersion

获取SDK版本号

3.3.17. - (void)miPushRequestSuccWithSelector:(NSString *)selector data:(NSDictionary
*)data

当请求成功后,会回调此方法

表 14. 

参数列表 参数说明
(NSString *)selector 请求调用方法的名称。例: “setAlias:”
(NSDictionary *)data 请求后下发的服务器信息。

 

3.3.18. - (void)miPushRequestErrWithSelector:(NSString *)selector error:(int)error
data:(NSDictionary *)data

当请求失败后,会回调此方法

表 15. 

参数列表 参数说明
(NSString *)selector 请求调用方法的名称。例: “setAlias:” NOTE: 最后参数后面的冒号
(int)error 错误码。参加“错误码说明”
(NSDictionary *)data 请求后下发的服务器信息。

 

3.4. 错误码说明

表 16. 

错误码 说明
-1 访问超时,网络信号不好
-2 无网络
-3 未知错误,通常意味着MiPush服务器的业务错误
-4 操作太频繁
-5 请求无效参数
-6 返回结果无效
-100 无效的UserID
-101 无效的RegID

 

4. 常见问题

4.1. 收不到推送消息?

请检查以下几点

  1. 开发者网站是否启用推送服务,并成功上传证书
  2. 用Appsecret自行推送的,请确认此secret是否为iOS版本,
  3. 工程里面plist中MiSDKRun环境参数是否设置妥当。debug为测试环境,online为线上环境
  4. 必须真机调试,IPA的profile是否与push证书匹配。
  5. 代码中监听MiPushSDKDelegate,看注册与绑定token是否正常,alias,topic是否设置成功

其中线上环境收不到消息,线上证书配置检测:

1、确认Xcode环境是否配置正确?

check_xcode1

在配置为Distribution时,需要AdHoc的Provisioning Profiles

2、运行环境是否正确?

check_xcode2

check_xcode3

4.2. 接入时碰到如下提示

Error is Error Domain=NSCocoaErrorDomain Code=3000 "未找到应用程序的“aps-environment”的授权字符串"
        UserInfo=0x17dbf200 {NSLocalizedDescription=未找到应用程序的“aps-environment”的授权字符串}

检查事项 同4.1

4.3. 常见返回错误

  1. 调用SDK + (void)unregisterMiPush; 执行注销操作后,在执行其他方法就会导致报错-101
  2. 你的程序清除本地数据。NSUserDefaults导致本地保存的regID等丢失。就会导致报错-101
  3. 解决方法:当回调返回-101时,根据你程序需要,可以重新调用+ (void)registerMiPush
  4. code=10017; reason=’invalid package name’ 小米开发者网站 注册的bundleID 跟 你ipa的bundleID
    不是同一个

4.4. 脚标或通知栏无法清除?

清除方法:

[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];

如果无效请尝试,原因是,在推送时badge设置为0,这样你在app中再次设置就会失效

[[UIApplication sharedApplication] setApplicationIconBadgeNumber:1];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];

4.5. SetAlias Subscribe为什么有时成功,有时失败?

SDK中方法为异步操作,setAlias: subscribe:等操作必须在bindDeviceToken:返回成功后再调用。

因为bindDeviceToken成功请求后服务器才会分配账号给设备。如果setAlias操作在它前面执行,会找不到对应设备。

4.6. 接收到推送之后 如何取得里面的数据?

当App在后台,通过下面方法

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo

当新App的时候,通过下面方法

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

4.7. registerMiPush 在没有网络下调用,为什么还会返回成功?

SDK中会检查,如果此操作已经完成过,就不会重复请求服务器。

4.8. Topic 最多可以设置多少个,支持汉字吗?

一个设备最多可以设置300个, 支持汉字。

4.9. 如何使用 iOS7 Background Remote Notification (消息透传)

App在后台,收到消息后,会在后台运行一段代码。并不会提示用户。比如,App内容升级,Email更新,订阅内容等等

在server端发送消息时extra加入content-available字段。同时xcode工程下激活BackgroundModes-Remote notifications。

收到的消息会回调以下AppDelegate的函数

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

4.10. 为什么要使用长连接

在App运行时 ,APNs会提示用户是否接收消息,很多时候,用户会禁止此功能。导致,推送消息无法送达到用户手机。所以使用长连接功能,可以在App运行时,获取消息推送。

4.11. 在现有版本MiPushSDK下,如何快速使用应用内长连接

操作步骤:

1. 添加libMiPushSDK.a , MiPushSDK.h 到工程
2. 引入库:libresolv.dylib, libxml2.dylib, libz.dylib, SystemConfiguration.framework,MobileCoreServices.framework,CFNetwork.framework,CoreTelephony.framework (如果已经引入,请忽略)
3. 加入如下代码, 启动长连接
+ (void)registerMiPush:(id<MiPushSDKDelegate>)delegate type:(UIRemoteNotificationType)type connect:(BOOL)connect;
4. 加入收到消息回调,当客户端收到MiPush推送时,此方法会被调用到,data格式同 application:didReceiveRemoteNotification:
- (void)miPushReceiveNotification:(NSDictionary*)data;
5. 去掉原有处理通知逻辑,使用下面代替
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    [MiPushSDK handleReceiveRemoteNotification:userInfo];
}

4.12. 如何获取RegId

bindDeviceToken之后,可以通过以下方式获取到regId

- (void)miPushRequestSuccWithSelector:(NSString *)selector data:(NSDictionary *)data
{
    if ([selector isEqualToString:@"bindDeviceToken:"]) {
        NSLog(@"regid = %@", data[@"regid"]);
        NSLog(@"regid = %@", [MiPushSDK getRegId]); // SDK版本号2.2.2及之后可用
    }
}

4.13. 适配iOS10

Xcode8+,并按照以下步骤适配iOS10
1. 使用SDK 2.2.3及之后版本
2. 在工程中添加UserNotifications.framework
3. 打开Push Notification
4. AppDelegate遵守协议UNUserNotificationCenterDelegate
5. 在AppDelegate中增加接受消息处理

// iOS10新加入的回调方法
// 应用在前台收到通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
    NSDictionary * userInfo = notification.request.content.userInfo;
    if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        [MiPushSDK handleReceiveRemoteNotification:userInfo];
    }
    //    completionHandler(UNNotificationPresentationOptionAlert);
}

// 点击通知进入应用
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
    NSDictionary * userInfo = response.notification.request.content.userInfo;
    if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        [MiPushSDK handleReceiveRemoteNotification:userInfo];
    }
    completionHandler();
}

4.14. swift中使用

方法:
1. 新建一个oc的类EmptyClass,用于创建桥接文件XXX-Bridging-Header.h
2. 可以删EmptyClass,只保留桥接文件
3. 在桥接文件中加入代码 #import “MiPushSDK.h”
4. 其他步骤可以参考文档

class AppDelegate: UIResponder, UIApplicationDelegate, MiPushSDKDelegate, UNUserNotificationCenterDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        MiPushSDK.registerMiPush(self, type: UIRemoteNotificationType(rawValue: UInt(0)), connect: true);
        
        return true
    }

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let nsdata = NSData(data: deviceToken)
        let token = nsdata.description
        print("APNS Token: \(token)")
        MiPushSDK.bindDeviceToken(deviceToken)
    }
    
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        // 自行处理失败
    }
    
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        MiPushSDK.handleReceiveRemoteNotification(userInfo)
        let log = "APNs notify: \(userInfo)"
        print(log)
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo: [AnyHashable : Any] = notification.request.content.userInfo;
        if notification.request.trigger is UNPushNotificationTrigger {
            MiPushSDK.handleReceiveRemoteNotification(userInfo)
        }
        completionHandler(UNNotificationPresentationOptions.alert)
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        let userInfo: [AnyHashable : Any] = response.notification.request.content.userInfo;
        if response.notification.request.trigger is UNPushNotificationTrigger {
            MiPushSDK.handleReceiveRemoteNotification(userInfo)
        }
        completionHandler()
    }
    
    func miPushRequestSucc(withSelector selector: String!, data: [AnyHashable : Any]!) {
        let log = "\(selector): \(data)"
        print(log)
        if selector == "bindDeviceToken:" {
            let regId = data["regid"]
            print("regId = \(regId)")
        }
    }
    
    func miPushRequestErr(withSelector selector: String!, error: Int32, data: [AnyHashable : Any]!) {
        let log = "command error(\(error)|\(selector)): \(data.description)"
        print(log)
    }
    
    func miPushReceiveNotification(_ data: [AnyHashable : Any]!) {
        let log = "MiPush notify: \(data)";
        print(log)
    }
}