@@ -70,7 +70,6 @@ class NotificationService: UNNotificationServiceExtension {
7070 guard let bestAttemptContent = bestAttemptContent else { return }
7171
7272 let senderName = payload. sender? . name ?? payload. senderName ?? " Unknown "
73- let senderId = payload. sender? . _id ?? " "
7473 let senderUsername = payload. sender? . username ?? " "
7574
7675 // Create avatar image for the sender
@@ -86,31 +85,43 @@ class NotificationService: UNNotificationServiceExtension {
8685 displayName: senderName,
8786 image: senderImage,
8887 contactIdentifier: nil ,
89- customIdentifier: senderId
88+ customIdentifier: nil
9089 )
9190
92- // Determine conversation name (room name for groups, sender name for DMs)
93- let conversationName : String
94- if payload. type == . group, let roomName = payload. name {
95- conversationName = roomName
96- } else {
97- conversationName = senderName
98- }
91+ // Determine if this is a group or channel conversation
92+ let roomName = payload. name
93+ let isGroupOrChannel = ( payload. type == . group || payload. type == . channel) && roomName != nil && !roomName!. isEmpty
94+
95+ // Create speakable group name for group/channel conversations
96+ let speakableGroupName : INSpeakableString ? = isGroupOrChannel && roomName != nil
97+ ? INSpeakableString ( spokenPhrase: roomName!)
98+ : nil
99+
100+ // Create a dummy recipient to ensure iOS treats this as a group conversation
101+ // This is necessary for iOS to use speakableGroupName instead of sender name
102+ let dummyRecipient = INPerson (
103+ personHandle: INPersonHandle ( value: " placeholder " , type: . unknown) ,
104+ nameComponents: nil ,
105+ displayName: nil ,
106+ image: nil ,
107+ contactIdentifier: nil ,
108+ customIdentifier: " recipient_ \( payload. rid ?? " group " ) "
109+ )
99110
100111 // Create the messaging intent
101112 let intent = INSendMessageIntent (
102- recipients: nil ,
113+ recipients: isGroupOrChannel ? [ dummyRecipient ] : nil ,
103114 outgoingMessageType: . outgoingMessageText,
104115 content: bestAttemptContent. body,
105- speakableGroupName: INSpeakableString ( spokenPhrase : conversationName ) ,
116+ speakableGroupName: speakableGroupName ,
106117 conversationIdentifier: payload. rid ?? " " ,
107118 serviceName: nil ,
108119 sender: sender,
109120 attachments: nil
110121 )
111122
112- // If it's a group chat, set the group avatar
113- if payload . type == . group {
123+ // Set group avatar for group/channel conversations
124+ if isGroupOrChannel {
114125 intent. setImage ( senderImage, forParameterNamed: \. speakableGroupName)
115126 }
116127
@@ -122,10 +133,9 @@ class NotificationService: UNNotificationServiceExtension {
122133 // Update the notification content with the intent
123134 do {
124135 let updatedContent = try bestAttemptContent. updating ( from: intent)
125- // Store the updated content directly - don't use mutableCopy() as it strips the intent association
126136 self . finalContent = updatedContent
127137 } catch {
128- // Keep bestAttemptContent as fallback
138+ // Fallback to bestAttemptContent if intent update fails
129139 self . finalContent = bestAttemptContent
130140 }
131141 }
@@ -170,6 +180,7 @@ class NotificationService: UNNotificationServiceExtension {
170180 if let messageId = data. messageId {
171181 self . rocketchat? . getPushWithId ( messageId) { notification in
172182 if let notification = notification {
183+ // Set body first, processPayload will strip sender prefix for groups/channels
173184 self . bestAttemptContent? . body = notification. text
174185
175186 // Update ejson with full payload from server for correct navigation
@@ -227,11 +238,28 @@ class NotificationService: UNNotificationServiceExtension {
227238 func processPayload( payload: Payload ) {
228239 // Set notification title based on payload type
229240 let senderName = payload. sender? . name ?? payload. senderName ?? " Unknown "
241+ let senderUsername = payload. sender? . username ?? payload. senderName ?? " "
242+
230243 if let roomType = payload. type {
231244 switch roomType {
232245 case . group, . channel:
233246 // For groups/channels, use room name if available, otherwise fall back to sender name
234247 bestAttemptContent? . title = payload. name ?? senderName
248+
249+ // Remove sender name prefix from body for groups/channels
250+ // Server sends body as "senderName: message", but we only want "message"
251+ if let body = bestAttemptContent? . body {
252+ let senderPrefix = " \( senderUsername) : "
253+ if body. hasPrefix ( senderPrefix) {
254+ bestAttemptContent? . body = String ( body. dropFirst ( senderPrefix. count) )
255+ } else {
256+ // Try with sender name (display name) as fallback
257+ let senderNamePrefix = " \( senderName) : "
258+ if body. hasPrefix ( senderNamePrefix) {
259+ bestAttemptContent? . body = String ( body. dropFirst ( senderNamePrefix. count) )
260+ }
261+ }
262+ }
235263 case . direct:
236264 // For direct messages, use sender name
237265 bestAttemptContent? . title = senderName
@@ -260,9 +288,6 @@ class NotificationService: UNNotificationServiceExtension {
260288
261289 if let decryptedMessage = decryptedMessage {
262290 bestAttemptContent? . body = decryptedMessage
263- if let roomType = payload. type, roomType == . group, let sender = payload. senderName {
264- bestAttemptContent? . body = " \( sender) : \( decryptedMessage) "
265- }
266291 }
267292 }
268293 }
0 commit comments