Show Chat Avatars in iOS Notifications with Siri Intents

This article covers a useful iOS 15 messaging feature: making your chat notifications look more like Messages by attaching the sender avatar and conversation metadata. This article uses INPerson, INSendMessageIntent, and UNNotificationServiceExtension so the system can render richer communication notifications for both local and remote message flows.

A chat-style iOS notification showing an avatar next to the sender

This is mainly for chat and social apps. The point is not only prettier notifications, but giving iOS enough context to treat the notification like a real conversation.

This article highlights three practical outcomes. First, the notification can show the sender avatar instead of only your app icon. Second, iOS can use that conversation metadata for suggestions in places like Contacts or the share sheet. Third, remote pushes can still be upgraded before display by running the same intent-based enrichment inside a notification service extension.

Visual detail The avatar becomes the main image in the notification, while the app icon still appears as a smaller badge in the corner.

Start by enabling the communication-notification capability and telling the app which intent types it can donate.

The article first enables the Communication Notifications capability in Xcode. Then it adds the supported user activity types to the main app target's Info.plist:

NSUserActivityTypes (Array)
    - INStartCallIntent
    - INSendMessageIntent
Xcode project settings showing Communication Notifications capability and related configuration
The setup starts at the target level. Without the capability and activity types, the rest of the pipeline has nothing to build on.

The system needs a real description of the conversation participant, not only a message string.

The article builds an INPerson for the sender and stresses why the fields matter. A real email address or phone number helps iOS match the sender to a contact. A display name gives the notification something human-readable. An image turns into the visible avatar. A custom identifier lets your own app map the system object back to your account model.

let demoParticipant: INPerson = INPerson(
    personHandle: INPersonHandle(
        value: "John-Appleseed@mac.com",
        type: .emailAddress
    ),
    nameComponents: try? PersonNameComponents("John Appleseed"),
    displayName: "@john",
    image: INImage(imageData: UIImage(systemName: "applelogo")!.pngData()!),
    contactIdentifier: nil,
    customIdentifier: "john",
    isMe: false,
    suggestionType: .instantMessageAddress
)

In production, the placeholder symbol image should be replaced with the sender's actual avatar data.

Once the sender exists, wrap the message as an INSendMessageIntent and attach the sender image directly to the intent.

This article models the message itself with INSendMessageIntent, including the recipient, content, visible sender name, and a conversation identifier:

let intent = INSendMessageIntent(
    recipients: [currentUser],
    outgoingMessageType: .outgoingMessageText,
    content: chatMessage,
    speakableGroupName: INSpeakableString(
        spokenPhrase: sender.displayName
    ),
    conversationIdentifier: "chat001",
    serviceName: nil,
    sender: sender,
    attachments: nil
)

intent.setImage(sender.image, forParameterNamed: \.sender)

The key idea is that the notification is no longer just text. It becomes an intent-backed communication event that iOS can classify as a conversation.

The article proves the feature locally first: donate the interaction, update a UNMutableNotificationContent, then schedule the notification.

The smallest useful transformation happens here:

let interaction = INInteraction(intent: intent, response: nil)
interaction.direction = .incoming
interaction.donate(completion: nil)

do {
    content = try content.updating(from: intent) as! UNMutableNotificationContent
} catch {
    // Handle error
}

That updating(from:) call is the step that upgrades a plain notification into one carrying the conversation metadata and sender image.

A regular local notification without communication intent enrichment
A normal notification only shows the standard app presentation.
An enriched communication notification showing the sender avatar
After the intent update, the notification reads much more like a real chat thread.

Most real chat notifications come from your server, so the same enrichment has to happen inside a notification service extension.

The article adds a Notification Service Extension target, then configures its Info.plist so the extension can intercept push content before the system presents it.

Xcode dialog for adding a Notification Service Extension target
Add a notification service extension target so the push can be rewritten before display.
Notification Service Extension Info.plist configuration
The extension target still needs the right plist configuration to participate in the delivery path.

The push payload in the article carries sender metadata such as sender_id, sender_name, sender_nickname, sender_email, sender_image_url, and chat-session_id. The extension reads those values from bestAttemptContent.userInfo, rebuilds an INPerson and INSendMessageIntent, then updates the content before handing it back to the system.

if let senderAccountID = bestAttemptContent.userInfo["sender_id"] as? String,
   let senderName = bestAttemptContent.userInfo["sender_name"] as? String,
   let senderImageURLString = bestAttemptContent.userInfo["sender_image_url"] as? String,
   let senderDisplayName = bestAttemptContent.userInfo["sender_nickname"] as? String,
   let senderEmailAddr = bestAttemptContent.userInfo["sender_email"] as? String,
   let chatSessionID = bestAttemptContent.userInfo["chat-session_id"] as? String {
    // Rebuild INPerson and INSendMessageIntent here
}

The article's main service method then calls request.content.updating(from: intent) and returns the result through the extension's contentHandler.

Two practical notes matter: simulator testing is limited, and the demo project is available if you want a reference implementation.

This article explicitly warns that you cannot rely on a local .apns test file in the Simulator to validate the full experience. For remote pushes, use a real delivery path. The article also links to a sample project here: github.com/mszpro/iOS15-Intents-SNS.

The hard part is not the notification API itself. It is feeding the system enough conversation metadata that iOS can recognize the event as a real person-to-person message.

That is the throughline of this article. Enable the project capability, describe people with INPerson, represent the message with INSendMessageIntent, donate the interaction, and run the same logic again in a notification service extension for remote pushes. Once those pieces are in place, the notification can carry avatars and conversation context instead of feeling like a generic alert.