即时通讯
  • iOS : Objective-C
  • Android
  • macOS
  • Windows
  • Web
  • 小程序
  • Flutter
  • Unity3D
  • uni-app
  • React Native
  • 产品简介
  • 下载
  • 快速开始
    • 跑通示例源码
    • 实现基本消息收发
  • 用户相关
  • 房间相关
  • 群组相关
  • 消息相关
  • 呼叫邀请
  • 会话管理
  • 缓存管理
  • 离线推送
  • 语音组件
  • 客户端 API
  • 服务端 API
  • 迁移方案
  • SDK 错误码
  • 常见问题
  • 文档中心
  • 即时通讯
  • 离线推送
  • 最佳实践
  • 通知携带图片附件

通知携带图片附件

更新时间:2023-11-23 23:06

概述

ZPNs 支持在发送离线推送时携带一张图片。

实现原理

ZPNs 支持通过发送 APNs 推送时携带 "mutable-content":1 字段,以便您的 APP 拦截该推送消息,修改其内容再展示,详情请参考 Apple Developer 官网文档对 mutable-content 的描述

前提条件

  • 已实现离线推送,详情请参考 实现离线推送
  • iOS 11.0 或以上版本的 iOS 真机设备。

配置 resourceID

联系 ZEGO 技术支持配置携带 "mutable-content":1 的 resourceID。

发送端处理

ZPNs 支持两种发送图片附件的方式,通过 SDK 发送通过全员推送服务端 API 发送。其区别是,SDK 发送方式可用于所有使用 ZIMPushConfig 的接口且仅支持 iOS 端设备;全员推送服务端接口仅适用于全员推送场景,且仅支持 iOS、Google FCM 和华为设备。

通过 SDK 发送

通过携带 ZIMPushConfig 的接口发送离线推送时,请将上述 resourceID 填入其中。

以发送单聊文本消息为例:

ZIMTextMessage *txtMsg = [[ZIMTextMessage alloc] init];
txtMsg.message = @"消息内容";

ZIMMessageSendConfig *sentConfig = [[ZIMMessageSendConfig alloc] init];

ZIMPushConfig *pushConfig = [[ZIMPushConfig alloc] init];
pushConfig.title = @"推送标题,一般为本人 userName,对应 APNs title";
pushConfig.content = @"推送内容,一般与消息内容一致,对应 APNs body";
pushConfig.resourcesID = @"携带 "mutable-content":1 的 resourceID";
// 传入需要的图片 url 地址
pushConfig.payload = @"{\"image_attachment\":\"图片资源的 url\"}"; // 在 payload 中自定协议,增加携带图片 url 的字段,与 app 接收端解析时的协议一致即可。这里使用了一个 json 字符串。
sentConfig.pushConfig = pushConfig;

// 发送单聊文本消息
[[ZIM getInstance] sendMessage:txtMsg toConversationID:@"toUserID" conversationType:ZIMConversationTypePeer config:sentConfig notification:nil callback:^(ZIMMessage * _Nonnull message, ZIMError * _Nonnull errorInfo) {}];

通过全员推送服务端 API 发送

相关接口文档请参考 全员推送

接收端处理

1 配置 Capability

打开 Xcode,在 TARGETS 下选择目标,根据路径 Signing & Capabilities > Capabilities,开启 Push Notification(用于离线推送通知)。

2 设置 Notification Service Extension

  1. 添加 Notification Service Extension 到 Targets。

    1. 点击 “File > New > Target...”

    2. 在弹窗中,选择 “iOS > Notification Service Extension”。

    3. 为该 Extension 输入 Product Name 等信息。

      创建 Extension 后,会在项目工程中生成 "xxxExtension" 文件夹(xxx 为新增 Extension 时输入的 Product Name),您需要用到其中的 NotificationService 类文件。

  2. 为上述新增的 Extension 配置 Capability

    在 TARGETS 下选择 Extension 目标,然后选择 “Signing & Capabilities > Capabilities > Push Notification”,即可开启离线推送通知。

  3. 调整上述新增的 Extension 支持的最低版本为 iOS 11.0 或以上。

    如果设备的 iOS 版本低于此处要求,Extension 不会在此设备生效。

3 编写添加图片附件的逻辑

在 "xxxExtension" 文件夹(xxx 为新增 Extension 时输入的 Product Name)中的 NotificationService.m 文件中编写添加图片附件的业务逻辑,示例代码如下所示:

//  NotificationService.m
//  NotificationService

#import "NotificationService.h"
#import <Intents/Intents.h>

@interface NotificationService ()

@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;

@end

@implementation NotificationService


// 开启推送拦截后,收到携带 "mutable-content":1 的推送通知时,会触发该方法。
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    // 标题
    NSString *title = self.bestAttemptContent.title;
    // 副标题
    NSString *subtitle = self.bestAttemptContent.subtitle;
    // 内容
    NSString *body = self.bestAttemptContent.body;

    // 取出发送推送消息附带的 payload 字符串
    NSString *payload = [self.bestAttemptContent.userInfo objectForKey:@"payload"];

    if(payload == nil){
        self.contentHandler(self.bestAttemptContent);
        return;
    }


    // 解析 json 字符串,并转为 NSDictionary
    NSData *jsonData = [payload dataUsingEncoding:NSUTF8StringEncoding];
    NSError *error = nil;
    NSDictionary *payload_json_map = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
    if (error) {
        self.contentHandler(self.bestAttemptContent);
        return;
    }


    NSString *imageAttachmentUrl = [payload_json_map objectForKey:@"image_attachment"];
    if(imageAttachmentUrl == nil){
        self.contentHandler(self.bestAttemptContent);
        return;
    }

    [self downloadWithURLString:imageAttachmentUrl completionHandle:^(NSData *data, NSURL *localURL) {
        if(localURL){
            UNNotificationAttachment * attachment = [UNNotificationAttachment attachmentWithIdentifier:@"myAttachment" URL:localURL options:nil error:nil];
            self.bestAttemptContent.attachments = @[attachment];
        }
        contentHandler(self.bestAttemptContent);
    }];

}

// 下载并保存图片的方法
- (void)downloadWithURLString:(NSString *)urlStr completionHandle:(void(^)(NSData *data,NSURL *localURL))completionHandler{
    __block NSData *data = nil;
    NSURL *imageURL = [NSURL URLWithString:urlStr];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    [[session downloadTaskWithURL:imageURL completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {
        NSURL *localURL;
        if (error != nil) {
            NSLog(@"%@", error.localizedDescription);
        } else {
            NSFileManager *fileManager = [NSFileManager defaultManager];
            localURL = [NSURL fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:@".png"]];
            [fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];

            NSLog(@"localURL = %@", localURL);
            data = [[NSData alloc] initWithContentsOfURL:localURL];
        }
        completionHandler(data,localURL);

    }]resume];
}

@end
本篇目录