Pattern · AI Chat

New

Attachment Chat SwiftUI Block

An assistant chat with photos and files: thumbnails and file chips with upload rings and Retry, returned files to open or share.

A conversation where the person shares photos and files. Their photos lay out one large, two side by side or in a grid with +N, files show as chips with a type icon, name and size, and each attachment shows its upload progress or Retry. The assistant's replies can return files as cards that open in Quick Look or share, and tapping a photo opens a full-screen viewer with zoom.

  • SwiftUI
  • iOS 17.0+
  • chat-attachments
Get SwiftPieces Pro

$199 once, every piece included. See how installing works · Sign in

01

What's included

SwiftUI source you own, built on Apple frameworks only.

  • Photos one large, two side by side, or a grid with +N
  • File chips with an icon for the type, the name and the size
  • A progress ring per attachment while it uploads, and Retry when it fails
  • Returned files as cards with Open in Quick Look and Share
  • A full-screen photo viewer with pinch and double-tap zoom

02

Implementation notes

How it behaves, what it handles, and the decisions behind it.

The person's turns carry their photos over their text: one photo large, two side by side, three or more in a grid of four with +N on the last. Files show as chips with an icon for the type, the name and the size. While an attachment uploads it shows a progress ring and "Uploading 45%"; a failed upload dims the photo or turns the chip's line to the danger colour, with Retry on that attachment. The assistant's replies are plain text with inline bold and code, and can hand back files as cards with Open, which shows the file in Quick Look, and Share. Tapping a photo opens it full screen on black, with pinch and double-tap zoom, or calls your own viewer. Pass nil while the conversation loads and the block draws grey turns.

Notes

  • AttachmentChatBlock(messages:onRetryUpload:onOpenImage:) takes the conversation, oldest first. It is a section with no navigation stack, background, padding or scroll view of its own; the full-screen viewer is an overlay, so it brings its own container.
  • AttachmentChatBlock.Message has an id, a role (.user, .assistant), text, attachments and a date. Message.sample is the first exchange from the preview, with placeholder tiles for its photos.
  • AttachmentChatBlock.Attachment has an id, name, byteCount, contentType (a UTType, which sets the icon and whether it shows as a photo), an optional preview image, an optional url and an upload state: .done, .uploading(fraction) or .failed(reason). A returned file without a url shows no Open or Share.
  • Your app owns the messages and the upload progress: pass a new value as each fraction arrives and the ring follows.
  • onRetryUpload receives the message and the failed attachment; restart the upload and set it back to .uploading(0). Leave it out to hide Retry.
  • onOpenImage opens a photo in your own viewer. Leave it out and the block presents its own with fullScreenCover: pinch to zoom up to 4x, double-tap to zoom and back, drag when zoomed, and Close.
  • Icons follow the type: PDF, spreadsheet, presentation, image, movie, audio, archive, source code, text, and a plain document otherwise. Sizes use the system's file byte format.
  • An error haptic marks each failed upload.
  • VoiceOver reads each photo as "Photo" with its name and upload state, each chip as its name, type and size, and gives Open and Share their file names. Text grows with Dynamic Type.
  • AttachmentChatBlockExample at the end of AttachmentChatBlock.swift is the garden chat from the preview: backyard photos and a survey with the answer, then a photo of the corner at noon that uploads and gets a reply with a bed plan to open or share. Its photos are drawn with ImageRenderer in place of real ones. Pass wrapsInNavigationStack: false to push it in your own stack.

Theming

  • Reads SPTheme from @Environment(\.spTheme) and requires the design-system item.
  • Photos clip to radius.md with 4 point gaps and radius.xs tiles. Uploading dims them with bg at 35% and shows a ring on surfaceElevated: a fill track and textPrimary progress. +N sits on textPrimary at 45% in bg ink.
  • File chips are surface at radius.md; returned file cards are surface at radius.lg. Icons are textSecondary, names textPrimary, sizes type.footnote in textTertiary. Open is a fill capsule.
  • The person's text is type.body on fill at radius.md; replies are type.body in textPrimary, with bold set at semibold.
  • Failures use danger, with Retry on a danger capsule in statusOn over photos.
  • Turns appear with motion.smooth, a short fade under Reduce Motion.

Requirements

  • None for the block. Add NSPhotoLibraryUsageDescription only if your composer reads the photo library directly; PhotosPicker needs no key.