Official SDK

Swift

Swift Package Manager support for iOS and server-side Swift. Modern async/await patterns with comprehensive error handling.

Swift 5.9+Requires iOS 15+ or macOS 12+ for async/await supportMIT licensed

Rename PDFs, split multi-page documents, and extract structured data using the official Swift SDK for Renamed.to.

  1. 1Add the package via Swift Package Manager or Xcode
  2. 2Use async/await with rename(), pdfSplit(), or extract() on Data objects
  3. 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.

Installation

Shell
.package(url: "https://github.com/renamed-to/renamed-sdk", from: "0.1.0")

Quickstart

Initialize the client and start making API calls.

main.swift
swift
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.

Rename.swift
swift
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.

Split.swift
swift
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 minutes

Extract Data

Extract structured data from invoices, receipts, and other documents. Use Codable structs for type-safe extraction.

Extract.swift
swift
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.

routes.swift
swift
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.

DocumentRenameView.swift
swift
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.

ErrorHandling.swift
swift
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 docs

Frequently 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.

Related resources

Other languages