背景
看到群里有同学在咨询,推送通知如何自定义左侧的icon 部位,因为iOS 10之后有推出,通知扩展,所以大家知道可以通过Notification Extension
可以给通知添加媒体资源即图片、音视频等。但是我们也知道,通过通知扩展添加的图片是展示在右侧的,如下图:
那么如何修改左侧的icon,实现苹果短信的效果呢?带着疑问我们向下看。
方案
Communication Notifications (通讯通知)
查找苹果 官方文档 及 WWDC 2021 发现,iOS 15之后,苹果推出了一个叫通讯通知
的功能。可以看看苹果官方给的定义。
就是说,苹果提供了将推送通知区分为通信通知的功能,用通信通知可以显示用户的头像等内容。
图例:
看完定义与效果,我们发现这正是我们需要的功能,那么具体如何实现呢?
通讯通知 Communication Notifications 具体实现
要使用通讯通知,APP 需要在 Xcode 中将通讯通知功能添加到其 APP,并在应用程序通知服务扩展中实现 UNNotificationContentProviding 协议。
1、 首先将以下键值添加到APP Info.plist 文件中
<key>NSUserActivityTypes</key>
<array>
<string>INStartCallIntent</string>
<string>INSendMessageIntent</string>
</array>
2、在Xcode
- Signing & Capabilities
中添加 Communication Notifications
功能
本地通知 - 实现通讯通知
1、首先导入以下头文件
#import <Intents/Intents.h>
#import <UserNotifications/UserNotifications.h>
2、通过使用 INPerson 和 INSendMessageIntent 创建对话信息将其添加到APP的推送通知消息中。
//创建一个名字
NSPersonNameComponents *nameComponents = [[NSPersonNameComponents alloc] init];
nameComponents.nickname = message.fromUsername;// 用户名
//创建参与SiriKit交互的用户消息发送者
INPerson *messageSender = [[INPerson alloc]initWithPersonHandle:[[INPersonHandle alloc]initWithValue:nil type:INPersonHandleTypeUnknown] nameComponents:nameComponents displayName:message.fromName image:avatarImage contactIdentifier:nil customIdentifier:message.fromUsername isMe:NO suggestionType:(INPersonSuggestionTypeNone)];
//创建发送消息请求
INSendMessageIntent *intent = [[INSendMessageIntent alloc] initWithRecipients:@[messageSender] outgoingMessageType:(INOutgoingMessageTypeOutgoingMessageText) content:contentAttribut.string speakableGroupName:[[INSpeakableString alloc]initWithSpokenPhrase:@""] conversationIdentifier:message.msgId serviceName:nil sender:messageSender attachments:nil];
[intent setImage:avatarImage forParameterNamed:@"speakableGroupName"];
//创建SiriKit 交互对象
INInteraction *interaction = [[INInteraction alloc]initWithIntent:intent response:nil];
interaction.direction = INInteractionDirectionIncoming;
[interaction donateInteractionWithCompletion:nil];
完整实现代码如下:
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = message.fromName;
content.body = contentAttribut.string;
content.sound = [UNNotificationSound defaultSound];
content.badge = @([UIApplication sharedApplication].applicationIconBadgeNumber + 1);
content.userInfo = @{
@"url":@"xxxxxxx",
};
if (@available(iOS 15.0, *)) {
//实现私信消息内容展示
if (message.fromUsername && message.fromAvatar && message.fromName && message.msgId)
{
//需要先将图片下载下来,我们这里使用的SDWebImageDownloader下载图片
NSURL *imageURL = [NSURL URLWithString:message.fromAvatar];
__block INImage *avatarImage = nil;
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (image) {
avatarImage = [INImage imageWithImageData:data];
}
// 消息发送方
NSPersonNameComponents *nameComponents = [[NSPersonNameComponents alloc] init];
nameComponents.nickname = message.fromUsername;// 用户名
INPerson *messageSender = [[INPerson alloc]initWithPersonHandle:[[INPersonHandle alloc]initWithValue:nil type:INPersonHandleTypeUnknown] nameComponents:nameComponents displayName:message.fromName image:avatarImage contactIdentifier:nil customIdentifier:message.fromUsername isMe:NO suggestionType:(INPersonSuggestionTypeNone)];
INSendMessageIntent *intent = [[INSendMessageIntent alloc] initWithRecipients:@[messageSender] outgoingMessageType:(INOutgoingMessageTypeOutgoingMessageText) content:contentAttribut.string speakableGroupName:[[INSpeakableString alloc]initWithSpokenPhrase:@""] conversationIdentifier:message.msgId serviceName:nil sender:messageSender attachments:nil];
[intent setImage:avatarImage forParameterNamed:@"speakableGroupName"];
INInteraction *interaction = [[INInteraction alloc]initWithIntent:intent response:nil];
interaction.direction = INInteractionDirectionIncoming;
[interaction donateInteractionWithCompletion:nil];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:message.msgId content:[content contentByUpdatingWithProvider:intent error:nil] trigger:nil];
[center addNotificationRequest:request withCompletionHandler:^(NSError *_Nullable error) {
NSLog(@"成功添加推送");
}];
}];
}else{
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:message.msgId content:content trigger:nil];
[center addNotificationRequest:request withCompletionHandler:^(NSError *_Nullable error) {
NSLog(@"成功添加推送");
}];
}
}else{
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:message.msgId content:content trigger:nil];
[center addNotificationRequest:request withCompletionHandler:^(NSError *_Nullable error) {
NSLog(@"成功添加推送");
}];
}
远程通知 - 实现通讯通知
1、首先添加通知扩展(Notification Service Extension
)
iOS10 之后的通知具有扩展功能,可以在系统收到通知、展示通知时做一些事情。
2、将以下键值对添加到通知扩展中
<key>NSUserActivityTypes</key>
<array>
<string>INStartCallIntent</string>
<string>INSendMessageIntent</string>
</array>
3、在通知扩展下 NotificationService
中的以下方法中,实现通讯通知,具体实现方式与本地推送大同小异。
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
}
核心代码如下:
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.bestAttemptContent = [request.content mutableCopy];
if (@available(iOS 15.0, *)) {
//实现消息内容展示
// 发送者名称
NSString *fromUsername = self.bestAttemptContent.userInfo[@"xxx"];
// 发送者头像url地址
NSString *fromAvatar = self.bestAttemptContent.userInfo[@"xxx"];
// 发送者昵称
NSString *fromNickName = self.bestAttemptContent.userInfo[@"xxx"];
// 消息 id
NSString *messageId = self.bestAttemptContent.userInfo[@"xxx"];
if (fromUsername && fromAvatar && fromNickName && messageId)
{
//需要先下载图片
NSURL *imageURL = [NSURL URLWithString:fromAvatar];
__block INImage *avatarImage = nil;
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageURL completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (image) {
avatarImage = [INImage imageWithImageData:data];
}
// 消息发送方
NSPersonNameComponents *nameComponents = [[NSPersonNameComponents alloc] init];
nameComponents.nickname = fromUsername;// 用户名
INPerson *messageSender = [[INPerson alloc]initWithPersonHandle:[[INPersonHandle alloc]initWithValue:nil type:INPersonHandleTypeUnknown] nameComponents:nameComponents displayName:fromNickName image:avatarImage contactIdentifier:nil customIdentifier:fromUsername isMe:NO suggestionType:(INPersonSuggestionTypeNone)];
INSendMessageIntent *intent = [[INSendMessageIntent alloc] initWithRecipients:@[messageSender] outgoingMessageType:(INOutgoingMessageTypeOutgoingMessageText) content:self.bestAttemptContent.body speakableGroupName:[[INSpeakableString alloc]initWithSpokenPhrase:@""] conversationIdentifier:messageId serviceName:nil sender:messageSender attachments:nil];
[intent setImage:avatarImage forParameterNamed:@"speakableGroupName"];
INInteraction *interaction = [[INInteraction alloc]initWithIntent:intent response:nil];
interaction.direction = INInteractionDirectionIncoming;
[interaction donateInteractionWithCompletion:nil];
self.bestAttemptContent = [[request.content contentByUpdatingWithProvider:intent error:nil] mutableCopy];
}];
}else{
contentHandler(self.bestAttemptContent);
}
}else{
contentHandler(self.bestAttemptContent);
}
}
效果
至此,通讯通知功能完成,可以测试了。我们实现后的效果如下图: