leadtext
leadtext
leadtext
Invoices, receipts, statements
Auto-title papers
Matter-ready naming
Field, onboarding, reporting ops
Shot logs, call sheets, deliverables
Configs, audit logs, incidents
Transcripts, lesson plans, records
Invoices, product sheets, shipping
Leases, inspections, work orders
Receipts, reports, reimbursements
BOLs, PODs, carrier invoices
Official SDK
Swift
Swift Package Manager support for iOS and server-side Swift. Modern async/await patterns with comprehensive error handling.
Rename PDFs, split multi-page documents, and extract structured data using the official Swift SDK for Renamed.to.
- 1Add the package via Swift Package Manager or Xcode
- 2Use async/await with rename(), pdfSplit(), or extract() on Data objects
- 3Deploy to iOS 15+, macOS 12+, or server-side Swift with typed error handling
MIT licensed, open source on GitHub, published on Swift Package Manager.
On this page
Installation
.package(url: "https://github.com/renamed-to/renamed-sdk", from: "0.1.0")Quickstart
Initialize the client and start making API calls.
import Renamedimport Foundation// Initialize the clientlet client = RenamedClient( apiKey: ProcessInfo.processInfo.environment["RENAMED_API_KEY"]!)// Rename a PDFlet pdfData = try Data(contentsOf: URL(fileURLWithPath: "invoice.pdf"))let result = try await client.rename(pdfData)print(result.suggestedFilename)// -> "2024-01-15_AcmeCorp_INV-1234.pdf"// Split a multi-page PDFlet docData = try Data(contentsOf: URL(fileURLWithPath: "documents.pdf"))let job = try await client.pdfSplit(docData, options: .init(mode: .smart))print(job.documents)// -> [Document(filename: "...", downloadUrl: "..."), ...]// Extract structured datalet receiptData = try Data(contentsOf: URL(fileURLWithPath: "receipt.pdf"))let extracted = try await client.extract(receiptData)print(extracted)// -> ["vendor": "...", "date": "...", "amount": "..."]Rename a PDF
Pass PDF data to get an AI-generated filename. Use custom instructions to control the naming format.
import Renamedimport Foundation// Initialize the clientlet client = RenamedClient( apiKey: ProcessInfo.processInfo.environment["RENAMED_API_KEY"]!, configuration: .init( timeout: 30.0, maxRetries: 3 ))// Read the PDF filelet pdfURL = URL(fileURLWithPath: "./documents/invoice.pdf")let pdfData = try Data(contentsOf: pdfURL)// Rename the PDFlet result = try await client.rename(pdfData)print("Suggested filename: \(result.suggestedFilename)")print("Confidence: \(result.confidence)")// Output:// Suggested filename: 2024-01-15_AcmeCorp_INV-1234.pdf// Confidence: 0.95// Access extracted metadataif let metadata = result.metadata { print("Vendor: \(metadata["vendor"] ?? "Unknown")") print("Date: \(metadata["date"] ?? "Unknown")") print("Type: \(metadata["type"] ?? "Unknown")")}// Use custom naming instructionslet customResult = try await client.rename(pdfData, options: RenameOptions( instructions: "Format: YYYY-MM-DD_VendorName_Amount"))print(customResult.suggestedFilename)// -> "2024-01-15_AcmeCorp_$1250.pdf"// Rename from a URLlet urlResult = try await client.rename( from: URL(string: "https://example.com/document.pdf")!)// Using with SwiftUI@MainActorclass DocumentViewModel: ObservableObject { @Published var suggestedFilename: String? @Published var isLoading = false @Published var error: Error? private let client = RenamedClient( apiKey: ProcessInfo.processInfo.environment["RENAMED_API_KEY"]! ) func renameDocument(_ data: Data) async { isLoading = true defer { isLoading = false } do { let result = try await client.rename(data) suggestedFilename = result.suggestedFilename } catch { self.error = error } }}Split a PDF
Split multi-page PDFs into separate documents. The API uses async jobs for large files—poll for completion or use the built-in wait helper.
import Renamedimport Foundationlet client = RenamedClient( apiKey: ProcessInfo.processInfo.environment["RENAMED_API_KEY"]!, configuration: .init(timeout: 300.0) // 5 minutes for large files)// Read a multi-page PDFlet pdfURL = URL(fileURLWithPath: "./documents/combined.pdf")let pdfData = try Data(contentsOf: pdfURL)// Submit the split job with smart mode (AI detects document boundaries)let job = try await client.pdfSplit(pdfData, options: SplitOptions(mode: .smart))print("Job ID: \(job.jobId)")print("Status: \(job.state)")// Option 1: Wait for completion (built-in polling)let completed = try await job.waitForCompletion()// Option 2: Manual polling for progress updates// var status = job// while status.state == .processing {// print("Progress: \(status.progress)%")// try await Task.sleep(nanoseconds: 1_000_000_000) // 1 second// status = try await client.getJob(status.jobId)// }// Process the split documentsprint("Split into \(completed.documents.count) documents:")for (i, doc) in completed.documents.enumerated() { let pages = doc.pages.map { String($0) }.joined(separator: ", ") print(" [\(i + 1)] \(doc.filename) (pages \(pages))") // Output: // [1] invoice_1.pdf (pages 1, 2) // [2] invoice_2.pdf (pages 3, 4) // Download each split document let docData = try await client.downloadFile(from: doc.downloadUrl) // Save to output directory let outputDir = URL(fileURLWithPath: "./output") try FileManager.default.createDirectory(at: outputDir, withIntermediateDirectories: true) let outputURL = outputDir.appendingPathComponent(doc.filename) try docData.write(to: outputURL) print(" Saved: \(outputURL.path)")}// With a custom timeoutlet completedWithTimeout = try await job.waitForCompletion(timeout: 300) // 5 minutesExtract Data
Extract structured data from invoices, receipts, and other documents. Use Codable structs for type-safe extraction.
import Renamedimport Foundation// Define typed structs for invoice extractionstruct LineItem: Codable { let description: String let quantity: Int let unitPrice: Double}struct Invoice: Codable { let invoiceNumber: String let vendor: String let date: String let lineItems: [LineItem] let subtotal: Double let tax: Double let total: Double}// Initialize the clientlet client = RenamedClient( apiKey: ProcessInfo.processInfo.environment["RENAMED_API_KEY"]!)// Read the PDF filelet pdfURL = URL(fileURLWithPath: "./documents/invoice.pdf")let pdfData = try Data(contentsOf: pdfURL)// Basic extraction (returns [String: Any])let data = try await client.extract(pdfData)print("Vendor: \(data["vendor"] ?? "Unknown")")print("Date: \(data["date"] ?? "Unknown")")print("Total: \(data["total"] ?? "Unknown")")// Output:// Vendor: Acme Corp// Date: 2024-01-15// Total: 1250.0// Typed extraction: decode to a Codable structlet jsonData = try JSONSerialization.data(withJSONObject: data)let decoder = JSONDecoder()let invoice = try decoder.decode(Invoice.self, from: jsonData)// Now you have fully typed accessprint("Invoice #\(invoice.invoiceNumber) from \(invoice.vendor)")print("Line items: \(invoice.lineItems.count)")for item in invoice.lineItems { print(" - \(item.description): \(item.quantity) x $\(String(format: "%.2f", item.unitPrice))")}print("Total: $\(String(format: "%.2f", invoice.total))")// Output:// Invoice #INV-2024-1234 from Acme Corp// Line items: 2// - Widget Pro: 5 x $199.00// - Support Plan: 1 x $342.50// Total: $1337.50// Using with a custom schema for typed extractionlet typedInvoice: Invoice = try await client.extract(pdfData, as: Invoice.self)Framework Integrations
Common integration patterns for popular frameworks.
Vapor Route Handler
Handle PDF uploads in a Vapor web server with proper file handling and error responses.
import Vaporimport Renamed// Configure the Renamed clientfunc configure(_ app: Application) throws { // Register the client as a service app.renamed = RenamedClient( apiKey: Environment.get("RENAMED_API_KEY")!, configuration: .init( timeout: 300.0, maxRetries: 3 ) ) // Register routes try routes(app)}// Extension to store client on Applicationextension Application { private struct RenamedClientKey: StorageKey { typealias Value = RenamedClient } var renamed: RenamedClient { get { storage[RenamedClientKey.self]! } set { storage[RenamedClientKey.self] = newValue } }}// Route handlersfunc routes(_ app: Application) throws { // Rename endpoint app.post("api", "rename") { req async throws -> RenameResponse in let file = try req.content.decode(FileUpload.self) guard file.file.contentType == .pdf else { throw Abort(.badRequest, reason: "Only PDF files are supported") } var buffer = file.file.data guard let data = buffer.readData(length: buffer.readableBytes) else { throw Abort(.badRequest, reason: "Failed to read file data") } do { let result = try await req.application.renamed.rename(data) return RenameResponse( suggestedFilename: result.suggestedFilename, confidence: result.confidence, metadata: result.metadata ) } catch RenamedError.rateLimited(let retryAfter) { throw Abort(.tooManyRequests, headers: ["Retry-After": "\(retryAfter)"]) } catch { throw Abort(.badGateway, reason: error.localizedDescription) } } // Split endpoint app.post("api", "split") { req async throws -> SplitResponse in let input = try req.content.decode(SplitInput.self) var buffer = input.file.data guard let data = buffer.readData(length: buffer.readableBytes) else { throw Abort(.badRequest, reason: "Failed to read file data") } let mode: SplitMode = input.mode.flatMap { SplitMode(rawValue: $0) } ?? .smart do { let job = try await req.application.renamed.pdfSplit( data, options: SplitOptions(mode: mode) ) let completed = try await job.waitForCompletion() return SplitResponse( jobId: completed.jobId, documents: completed.documents.map { doc in SplitDocument( filename: doc.filename, pages: doc.pages, downloadUrl: doc.downloadUrl ) } ) } catch { throw Abort(.badGateway, reason: error.localizedDescription) } } // Extract endpoint app.post("api", "extract") { req async throws -> [String: AnyCodable] in let file = try req.content.decode(FileUpload.self) var buffer = file.file.data guard let data = buffer.readData(length: buffer.readableBytes) else { throw Abort(.badRequest, reason: "Failed to read file data") } do { let result = try await req.application.renamed.extract(data) return result.mapValues { AnyCodable($0) } } catch { throw Abort(.badGateway, reason: error.localizedDescription) } }}// DTOsstruct FileUpload: Content { let file: File}struct SplitInput: Content { let file: File let mode: String?}struct RenameResponse: Content { let suggestedFilename: String let confidence: Double let metadata: [String: Any]? enum CodingKeys: String, CodingKey { case suggestedFilename, confidence, metadata }}struct SplitResponse: Content { let jobId: String let documents: [SplitDocument]}struct SplitDocument: Content { let filename: String let pages: [Int] let downloadUrl: String}SwiftUI Integration
Use the SDK in SwiftUI apps with proper state management and async handling.
import SwiftUIimport Renamedimport UniformTypeIdentifiers// View Model@MainActorclass DocumentRenameViewModel: ObservableObject { @Published var suggestedFilename: String? @Published var confidence: Double? @Published var metadata: [String: Any]? @Published var isLoading = false @Published var error: String? private let client: RenamedClient init() { // In production, use a secure method to store the API key guard let apiKey = ProcessInfo.processInfo.environment["RENAMED_API_KEY"] else { fatalError("RENAMED_API_KEY not set") } self.client = RenamedClient(apiKey: apiKey) } func renameDocument(_ data: Data) async { isLoading = true error = nil defer { isLoading = false } do { let result = try await client.rename(data) suggestedFilename = result.suggestedFilename confidence = result.confidence metadata = result.metadata } catch RenamedError.rateLimited(let retryAfter) { error = "Rate limited. Please try again in \(retryAfter) seconds." } catch RenamedError.invalidFile(let message) { error = "Invalid file: \(message)" } catch { error = error.localizedDescription } } func reset() { suggestedFilename = nil confidence = nil metadata = nil error = nil }}// SwiftUI Viewstruct DocumentRenameView: View { @StateObject private var viewModel = DocumentRenameViewModel() @State private var isShowingFilePicker = false @State private var selectedFileData: Data? var body: some View { NavigationStack { VStack(spacing: 20) { // File picker button Button { isShowingFilePicker = true } label: { Label("Select PDF", systemImage: "doc.badge.plus") .font(.headline) } .buttonStyle(.borderedProminent) .disabled(viewModel.isLoading) // Loading indicator if viewModel.isLoading { ProgressView("Analyzing PDF...") } // Results if let filename = viewModel.suggestedFilename { VStack(alignment: .leading, spacing: 12) { Text("Suggested Filename") .font(.headline) Text(filename) .font(.body) .padding() .background(Color.secondary.opacity(0.1)) .cornerRadius(8) if let confidence = viewModel.confidence { Text("Confidence: \(Int(confidence * 100))%") .font(.subheadline) .foregroundColor(.secondary) } } .padding() } // Error display if let error = viewModel.error { Text(error) .foregroundColor(.red) .padding() } Spacer() } .padding() .navigationTitle("Rename PDF") .fileImporter( isPresented: $isShowingFilePicker, allowedContentTypes: [UTType.pdf] ) { result in handleFileSelection(result) } } } private func handleFileSelection(_ result: Result<URL, Error>) { switch result { case .success(let url): do { // Start accessing the security-scoped resource guard url.startAccessingSecurityScopedResource() else { viewModel.error = "Cannot access file" return } defer { url.stopAccessingSecurityScopedResource() } let data = try Data(contentsOf: url) selectedFileData = data Task { await viewModel.renameDocument(data) } } catch { viewModel.error = error.localizedDescription } case .failure(let error): viewModel.error = error.localizedDescription } }}#Preview { DocumentRenameView()}Error Handling
The SDK throws typed errors that you can catch and handle appropriately using do-catch blocks.
import Renamedimport Foundationlet client = RenamedClient( apiKey: ProcessInfo.processInfo.environment["RENAMED_API_KEY"]!)do { let pdfURL = URL(fileURLWithPath: "invoice.pdf") let pdfData = try Data(contentsOf: pdfURL) let result = try await client.rename(pdfData) print("Renamed to: \(result.suggestedFilename)")} catch RenamedError.rateLimited(let retryAfter) { // Rate limited - wait and retry print("Rate limited. Retry after \(retryAfter) seconds") try await Task.sleep(nanoseconds: UInt64(retryAfter) * 1_000_000_000) // Retry the request...} catch RenamedError.invalidFile(let message) { // File is not a valid PDF or is corrupted print("Invalid file: \(message)")} catch RenamedError.authentication(let message) { // API key is invalid or expired print("Authentication failed: \(message)")} catch RenamedError.apiError(let code, let message, let details) { // Other API errors print("API Error [\(code)]: \(message)") if let details = details { print("Details: \(details)") }} catch RenamedError.networkError(let underlyingError) { // Network/connection errors print("Network error: \(underlyingError.localizedDescription)")} catch RenamedError.timeout { // Request timed out print("Request timed out")} catch { // Other errors (file I/O, etc.) print("Unexpected error: \(error)")}// Helper function with retry logicfunc renameWithRetry( client: RenamedClient, data: Data, maxRetries: Int = 3) async throws -> RenameResult { var retries = 0 while true { do { return try await client.rename(data) } catch RenamedError.rateLimited(let retryAfter) { retries += 1 guard retries <= maxRetries else { throw RenamedError.rateLimited(retryAfter: retryAfter) } let sleepTime = retryAfter > 0 ? retryAfter : Int(pow(2.0, Double(retries))) print("Rate limited, retrying in \(sleepTime)s (attempt \(retries)/\(maxRetries))") try await Task.sleep(nanoseconds: UInt64(sleepTime) * 1_000_000_000) } catch RenamedError.apiError(let code, let message, _) where code >= 500 { retries += 1 guard retries <= maxRetries else { throw RenamedError.apiError(code: code, message: message, details: nil) } let sleepTime = Int(pow(2.0, Double(retries))) print("Server error, retrying in \(sleepTime)s (attempt \(retries)/\(maxRetries))") try await Task.sleep(nanoseconds: UInt64(sleepTime) * 1_000_000_000) } }}Full documentation on GitHub
For more examples, advanced usage patterns, and detailed API documentation, see the full Swift SDK README on GitHub.
Read the Swift SDK docsFrequently asked questions
- What Swift versions are supported?
- The Swift SDK supports Swift 5.9 and above. It uses modern Swift concurrency with async/await.
- Does it work on iOS?
- Yes, the SDK works on iOS 15+, macOS 12+, tvOS 15+, watchOS 8+, and server-side Swift (Linux). It supports all Apple platforms.
- How do I install it?
- Add the package to your Package.swift dependencies or use Xcode to add the package URL directly via File > Add Package Dependencies.
- Is it compatible with Vapor?
- Yes, the SDK works great with Vapor and other server-side Swift frameworks. Use async/await in your route handlers.
- How do I handle background uploads on iOS?
- For background uploads, combine the SDK with URLSession background tasks. The SDK methods can be called when your app returns to the foreground.