Pattern · AI Chat
NewAttachment 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
$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.Messagehas anid, arole(.user,.assistant),text,attachmentsand adate.Message.sampleis the first exchange from the preview, with placeholder tiles for its photos.AttachmentChatBlock.Attachmenthas anid,name,byteCount,contentType(aUTType, which sets the icon and whether it shows as a photo), an optionalpreviewimage, an optionalurland anuploadstate:.done,.uploading(fraction)or.failed(reason). A returned file without aurlshows 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.
onRetryUploadreceives the message and the failed attachment; restart the upload and set it back to.uploading(0). Leave it out to hide Retry.onOpenImageopens a photo in your own viewer. Leave it out and the block presents its own withfullScreenCover: 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.
AttachmentChatBlockExampleat the end ofAttachmentChatBlock.swiftis 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 withImageRendererin place of real ones. PasswrapsInNavigationStack: falseto push it in your own stack.
Theming
- Reads
SPThemefrom@Environment(\.spTheme)and requires thedesign-systemitem. - Photos clip to
radius.mdwith 4 point gaps andradius.xstiles. Uploading dims them withbgat 35% and shows a ring onsurfaceElevated: afilltrack andtextPrimaryprogress. +N sits ontextPrimaryat 45% inbgink. - File chips are
surfaceatradius.md; returned file cards aresurfaceatradius.lg. Icons aretextSecondary, namestextPrimary, sizestype.footnoteintextTertiary. Open is afillcapsule. - The person's text is
type.bodyonfillatradius.md; replies aretype.bodyintextPrimary, with bold set at semibold. - Failures use
danger, with Retry on adangercapsule instatusOnover photos. - Turns appear with
motion.smooth, a short fade under Reduce Motion.
Requirements
- None for the block. Add
NSPhotoLibraryUsageDescriptiononly if your composer reads the photo library directly;PhotosPickerneeds no key.
03
Keep building
Pieces that work well next to this one.