An application can let someone attach a photo or a video, and cannot let them attach anything else. There is no way to choose a PDF, a CSV, a .fit or .gpx export, or a document, because no action opens a document picker for general content.
Asking for this from HQ.training, where direct messages should carry files as well as photos.
What is already here
Almost all of it, which is why this is a widening rather than a new capability.
CraftNativeFiles.swift:305 already runs a full document-picker flow inside pickVideo when source: "files":
var types: [UTType] = [.movie]
if let webm = UTType(filenameExtension: "webm") { types.append(webm) }
let picker = UIDocumentPickerViewController(forOpeningContentTypes: types, asCopy: true)with a UIDocumentPickerDelegate, startAccessingSecurityScopedResource() handling, and a copy into app-owned storage. describe() (CraftNativeFiles.swift:70) already returns exactly the shape an attachment needs:
["path": url.path, "name": url.lastPathComponent, "type": type, "size": size]Everything downstream of the pick works for any file already:
http.uploadsends a realmultipart/form-databody overuploadTask(with:fromFile:)Files.downloadfetches one backShare.shareacceptsfiles: [{ path, name }], so a recipient can open what they receivedFiles.write/Files.removemanage the local copy
What blocks it
Two places, both narrow.
The picker is nailed to video. types above is [.movie] plus webm, so the sheet will not offer anything else.
The copy enforces a video allowlist. CraftNativeFiles.swift:333:
let types = ["mp4": "video/mp4", "m4v": "video/x-m4v", "mov": "video/quicktime", "webm": "video/webm"]and line 335 rejects everything else with INVALID_VIDEO — "Choose an MP4, MOV or WebM video."
Nothing is registered. The dispatcher in CraftNativeActions.swift has pickImage (243), pickVideo (250), download (256), write (259), remove (269). There is no pickFile. pickFile / pickDocument appear nowhere in the TypeScript surface either — CraftFileSystemAPI exposes only readFile, writeFile, readDir, mkdir, remove, exists.
Suggested shape
("Files", "pickFile")in the dispatcher.- A
copyFilesibling tocopyVideo: same security-scoped copy and size limit, but the caller states what it will accept rather than the framework assuming video. pickFile(options?: { contentTypes?: string[], maxSelections?: number, maxBytes?: number }): Promise<FileInfo[]>onCraftMobileAPI, returningdescribe()'s{ path, name, type, size }.contentTypesas UTI strings or filename extensions, defaulting to "anything", so an application decides its own policy. An application that wants only PDFs should be able to say so in the sheet rather than filtering after the fact, which is both worse UX and a weaker check.
One thing that is not required
In-app preview. There is no QLPreviewController anywhere, so a received PDF cannot be previewed inside the app — but Share.share already covers opening it elsewhere, so this is a separate nice-to-have and not part of this issue.
Note for applications
enableShare and enableCamera both default to false. An application that adds file attachments and forgets enableShare will find Share.share answering CAPABILITY_DISABLED, so a recipient can download a file and then have no way to open it. Worth a line in the docs beside whatever gate pickFile ends up behind.