Fast, reliable concurrent uploads with real-time progress tracking and automatic URL generation
The r2Vault upload system is built for speed and reliability, with support for concurrent uploads, per-file progress tracking, and automatic public URL generation.
r2Vault uploads multiple files simultaneously using Swift’s structured concurrency, maximizing your upload speed while respecting R2’s limits.
AppViewModel.swift:472-494
Copy
Ask AI
private func uploadPendingTasks() async { guard let credentials else { showError("Please configure R2 credentials in Settings first (Cmd+,).") return } let pending = uploadTasks.filter { $0.status == .pending } await withTaskGroup(of: Void.self) { group in for uploadTask in pending { // Create and store the task handle on MainActor before it runs, // so the cancel button can reach it immediately. let handle = Task { await self.uploadSingleFile(uploadTask, credentials: credentials) } await MainActor.run { uploadTask.uploadTask = handle } group.addTask { await handle.value await MainActor.run { uploadTask.uploadTask = nil } } } }}
Each upload runs in its own concurrent task, allowing multiple files to upload simultaneously while maintaining full control over individual uploads.
Each upload is represented by a FileUploadTask observable object:
UploadTask.swift:1-46
Copy
Ask AI
@Observablefinal class FileUploadTask: Identifiable { let id: UUID let fileName: String let fileSize: Int64 let fileURL: URL var progress: Double = 0 // 0.0 – 1.0 var status: Status = .pending var errorMessage: String? var resultURL: URL? /// When set, used as the full R2 key instead of generating a random-prefix key. /// Used for folder-aware uploads from the browser. var uploadKey: String? /// Security-scoped bookmark for a parent folder (used when uploading folders). var parentFolderBookmark: Data? /// Security-scoped bookmark for the file itself (used when uploading individual files via file picker). var fileBookmark: Data? enum Status: Sendable { case pending case uploading case completed case failed case cancelled } /// The running upload task — held so it can be cancelled. var uploadTask: Task<Void, Never>? init(fileURL: URL, fileName: String, fileSize: Int64) { self.id = UUID() self.fileURL = fileURL self.fileName = fileName self.fileSize = fileSize } func cancel() { uploadTask?.cancel() uploadTask = nil status = .cancelled }}
On macOS, apps require explicit permission to access user files. r2Vault uses security-scoped bookmarks to maintain access to files across background tasks:
AppViewModel.swift:435-446
Copy
Ask AI
// Save a security-scoped bookmark while we still have access,// so uploadSingleFile can re-open the file later on a background task.let bookmark = try? url.bookmarkData(options: [.withSecurityScope], includingResourceValuesForKeys: nil, relativeTo: nil)let task = FileUploadTask(fileURL: url, fileName: fileName, fileSize: fileSize)task.fileBookmark = bookmarkif !currentPrefix.isEmpty { task.uploadKey = currentPrefix + fileName}tasks.append(task)
The bookmark is resolved when the upload actually runs:
AppViewModel.swift:518-533
Copy
Ask AI
var resolvedFileURL = fileURLvar fileAccessing = falseif let bookmark = uploadTask.fileBookmark { var isStale = false if let resolved = try? URL(resolvingBookmarkData: bookmark, options: [.withSecurityScope], relativeTo: nil, bookmarkDataIsStale: &isStale) { resolvedFileURL = resolved fileAccessing = resolved.startAccessingSecurityScopedResource() }} else { fileAccessing = fileURL.startAccessingSecurityScopedResource()}defer { if fileAccessing { resolvedFileURL.stopAccessingSecurityScopedResource() } }
This approach allows r2Vault to upload files even after the file picker dialog is closed, and supports cancellation and retry without requiring permission again.