Implementing File Sharing in Mobile Chat
PDF, XLSX, ZIP, APK — users send everything. Task isn't reduced to "attach file" button: properly handle file picker on both platforms, correctly display file type, organize upload and secure download to recipient's device.
File Selection: Platform Pitfalls
On iOS system UIDocumentPickerViewController returns URL with security-scoped bookmark. File access opens through startAccessingSecurityScopedResource() and must close through stopAccessingSecurityScopedResource() after copying to temp directory. Forgetting the second call — security scope leak, and on next app launch file access is blocked by system.
Files from iCloud Drive don't arrive instantly: NSMetadataQuery shows download status. If file not downloaded to device — must wait for NSMetadataUbiquitousItemIsDownloadingKey completion before copying. Without this get empty 0-byte file in upload queue.
On Android — Intent(Intent.ACTION_OPEN_DOCUMENT) with addCategory(Intent.CATEGORY_OPENABLE). Uri from content provider can't directly go to network requests — must copy content through contentResolver.openInputStream() to app cache directory. Files from Google Drive and other providers don't have real filesystem path, only content:// URI.
MIME Type and Icons
Determine MIME by extension through UTType (iOS 14+) or MimeTypeMap (Android). On server — additionally check via magic bytes (first file bytes). PDF starts with %PDF, ZIP — with PK\x03\x04. Protects from renamed executables.
In chat show icon by category: document, spreadsheet, archive, audio, other. Don't try rendering preview for every type — only for PDF (through PDFKit on iOS or PdfRenderer on Android) and office formats through QuickLook / ACTION_VIEW with system app.
Upload and Download
Upload — same principles as video: chunking for files > 5 MB, background session on iOS, WorkManager on Android. Progress in bytes, not percentages — user understands 1.2 MB of 8.4 MB better than 14%.
Download on recipient's device — separate story. On iOS save file in FileManager.default.urls(for: .documentDirectory) and offer through UIActivityViewController to open in other app. Direct save to Files — through UIDocumentPickerViewController in export mode.
On Android — system DownloadManager: correctly handles background download, shows progress in notification shade, saves to Downloads. Direct path through FileOutputStream in getExternalFilesDir() — only for internal app cache, not visible to user in file manager.
Security
Whitelist of MIME types on server — mandatory. Block executable files (.exe, .apk, .ipa, .sh) or check through antivirus scan (ClamAV, VirusTotal API). Max file size — limit at API gateway level, not just on client.
Download links — presigned URL with TTL. Direct S3 links without signature mean public access to private chats.
Timeframe
Basic implementation (file picker, upload with progress, display in chat, download) — 2–3 days. Background upload + resumption + whitelist — another 1 day. Cost calculated individually.







