DocIQ: From Real Estate Documents to Universal Document Intelligence with SwiftUI and AI

What happens when you build a feature so useful that it deserves to become its own product? That's the story of DocIQ—a document intelligence engine that started as the extraction layer inside Capvera and evolved into a standalone application for processing documents across healthcare, legal, finance, and a dozen other industries.

The extraction capabilities in Capvera were designed specifically for real estate documents—leases, purchase agreements, insurance certificates. But the underlying architecture was general enough to handle any structured document. So I extracted it, generalized it, and built DocIQ: a macOS application that turns unstructured PDFs and Word documents into structured, searchable, actionable data.

Everything was built with Claude Opus 4.5 in the Cursor IDE. The entire codebase—from the Swift Package to the dashboard analytics—generated through AI-assisted development.

DocIQ Extracted InformationDocIQ Dashboard

Quick Links

The Vision: Universal Document Intelligence

Every industry drowns in documents. Healthcare practices process insurance forms, patient intake documents, and medical records. Law firms handle contracts, court filings, and discovery materials. Accounting teams wade through invoices, receipts, and tax forms. Real estate investors manage leases, closing documents, and inspection reports.

The problem is the same everywhere: extracting structured data from unstructured documents is tedious, error-prone, and expensive.

DocIQ solves this by combining:

  • AI-powered classification to automatically identify document types
  • Industry-specific extraction that knows what fields matter for each document
  • Structured output that integrates with your existing workflows
  • Dashboard analytics to understand your document portfolio at a glance

Key Features

Smart Classification: Drop a document, and DocIQ identifies whether it's an invoice, contract, medical form, or one of 200+ other document types—automatically routing it to the appropriate extraction pipeline.

Industry-Aware Extraction: A healthcare intake form needs different fields than a commercial lease. DocIQ understands these distinctions and extracts the right information for each context.

Live Extraction Feed: Watch as AI processes your documents in real-time, extracting entities, dates, amounts, and relationships as they're discovered.

Analytics Dashboard: Visualize your document portfolio with charts showing document types, processing timelines, category distributions, and file size patterns.

Structured Views: Extracted data is presented in organized, hierarchical views—not just raw JSON, but meaningful cards showing parties, dates, amounts, and key terms.

Supported Industries

DocIQ ships with extraction templates for 15 industries, each with document types and field definitions tailored to real-world workflows:

IndustryExample DocumentsKey Extracted Fields
HealthcarePatient intake, insurance claims, medical recordsPatient info, diagnosis codes, provider details, dates of service
LegalContracts, court filings, discovery documentsParties, effective dates, terms, obligations, case numbers
FinanceInvoices, bank statements, loan documentsAmounts, account numbers, transaction dates, payment terms
Real EstateLeases, purchase agreements, inspection reportsProperty address, rent amounts, lease terms, parties
InsurancePolicies, claims, certificates of insuranceCoverage amounts, policy numbers, effective dates, named insureds
AccountingReceipts, expense reports, tax formsVendor, amount, date, category, tax ID
Human ResourcesEmployment contracts, W-4s, I-9sEmployee info, compensation, start dates, tax withholding
ConstructionContracts, change orders, lien waiversProject details, amounts, parties, completion dates
ManufacturingPurchase orders, invoices, quality reportsPart numbers, quantities, specifications, vendors
RetailReceipts, purchase orders, vendor agreementsItems, prices, quantities, payment terms
EducationTranscripts, enrollment forms, financial aidStudent info, courses, grades, award amounts
GovernmentPermits, licenses, regulatory filingsLicense numbers, expiration dates, compliance requirements
TransportationBills of lading, shipping manifests, customs formsOrigin, destination, cargo details, carrier info
EnergyUtility bills, service agreements, compliance docsUsage, rates, service periods, account details
Non-ProfitGrant applications, donation receipts, 990 formsDonor info, amounts, program details, tax status

Each industry template was designed by analyzing real documents and identifying the fields that matter most for operational workflows.

Document Types

Beyond industry categorization, DocIQ recognizes 200+ document types with specialized extraction logic, organized into 25 categories:

enum DocumentType: String, CaseIterable, Codable {
    
    // MARK: - General / Cross-Industry
    case receipt = "Receipt"
    case invoice = "Invoice"
    case photo = "Photo"
    case report = "Report"
    case correspondence = "Correspondence"
    
    // MARK: - Procurement & Finance
    case purchaseOrder = "Purchase Order"
    case quote = "Quote / Estimate"
    case creditMemo = "Credit Memo"
    case accountStatement = "Account Statement"
    case bankStatement = "Bank Statement"
    case expenseReport = "Expense Report"
    
    // MARK: - Contracts & Legal
    case nda = "NDA / Confidentiality Agreement"
    case msa = "Master Service Agreement"
    case sow = "Statement of Work"
    case contract = "Contract"
    
    // MARK: - Logistics & Shipping
    case billOfLading = "Bill of Lading"
    case packingList = "Packing List"
    case proofOfDelivery = "Proof of Delivery"
    case customsDocument = "Customs Document"
    
    // MARK: - Real Estate
    case purchaseAgreement = "Purchase Agreement"
    case deed = "Deed"
    case titleCommitment = "Title Commitment"
    case lease = "Lease"
    case leaseAmendment = "Lease Amendment"
    case estoppel = "Estoppel Certificate"
    case rentRoll = "Rent Roll"
    
    // MARK: - Financing
    case loanAgreement = "Loan Agreement"
    case promissoryNote = "Promissory Note"
    case mortgageDeed = "Mortgage/Deed of Trust"
    case payoffLetter = "Payoff Letter"
    
    // MARK: - Insurance & Tax
    case insurancePolicy = "Insurance Policy"
    case insuranceClaim = "Insurance Claim"
    case taxBill = "Tax Bill"
    case taxReturn = "Tax Return"
    case appraisal = "Appraisal"
    
    // MARK: - Healthcare
    case patientIntake = "Patient Intake"
    case clinicalNote = "Clinical Note"
    case labResult = "Lab Result"
    case prescription = "Prescription"
    case eob = "Explanation of Benefits"
    case dischargeSummary = "Discharge Summary"
    
    // MARK: - Construction
    case permit = "Permit"
    case certificateOfOccupancy = "Certificate of Occupancy"
    case changeOrder = "Change Order"
    case lienWaiver = "Lien Waiver"
    case punchList = "Punch List"
    
    // ... and 150+ more across Manufacturing, Government,
    // Education, Legal Services, Technology, Agriculture,
    // Oil & Gas, Transportation, Hospitality, Nonprofit,
    // Retail, and Pharmaceutical industries
}

Document Type Categories

The full taxonomy spans 25 categories:

CategoryDocument CountExamples
General / Cross-Industry6Receipt, Invoice, Report, Correspondence
Procurement & Finance7Purchase Order, Quote, Credit Memo, Bank Statement
Contracts & Legal5NDA, Master Service Agreement, Statement of Work
Operations & Compliance5Work Order, Audit Report, Incident Report, Safety Data Sheet
Logistics & Shipping4Bill of Lading, Packing List, Proof of Delivery
Identity & KYC3Driver's License, Passport, Government ID
Real Estate14Purchase Agreement, Deed, Lease, Rent Roll, Estoppel
Financing7Loan Agreement, Promissory Note, Mortgage, Payoff Letter
Insurance & Tax7Insurance Policy, Claims, Tax Bill, Tax Return, Appraisal
Entity / Corporate5Operating Agreement, Articles of Organization, Board Minutes
Property & Construction14Survey, Permit, Change Order, Lien Waiver, Punch List
Healthcare7Patient Intake, Clinical Note, Lab Result, Prescription
Manufacturing & Quality7Bill of Materials, Batch Record, Certificate of Conformance
Government & Grants5RFP, Government Contract, Grant Agreement
Education5Enrollment Agreement, Transcript, Financial Aid Letter
Legal Services5Engagement Letter, Pleading, Court Order, Discovery
Technology & SaaS5SaaS Agreement, DPA, SLA, SOC Report
Agriculture6Farm Lease, Crop Plan, Grain Ticket, Organic Certification
Oil & Gas6Oil & Gas Lease, Division Order, AFE, Run Ticket, Well Log
Transportation5Rate Confirmation, Driver Log, Freight Invoice, DOT Inspection
Hospitality5Reservation, Folio, Banquet Event Order, Liquor License
Nonprofit3Donation Receipt, Program Report, 990 Filing
Retail & eCommerce5Supplier Agreement, Product Catalog, RMA, Inventory Report
Pharmaceutical4Clinical Trial Protocol, Informed Consent, IRB Approval

The classification service uses AI to analyze document content and structure, returning both the detected type and a confidence score:

struct ClassificationResult {
    let documentType: DocumentType
    let confidence: Double
    let suggestedIndustry: Industry
    let extractedTitle: String?
}

The Technical Architecture

DocIQ is built as two components: DocIQKit (a Swift Package containing the extraction engine) and the DocIQ app (a SwiftUI macOS application with dashboard and document management).

DocIQKit: The Extraction Engine

The Swift Package provides a clean API for document processing:

import DocIQKit

// Classify a document
let classifier = DocumentClassificationService()
let classification = try await classifier.classify(document: pdfURL)

// Extract structured data
let extractor = DocumentExtractionService()
let extractedInfo = try await extractor.extract(
    from: pdfURL,
    documentType: classification.documentType,
    industry: classification.suggestedIndustry
)

// Access structured fields
print(extractedInfo.parties)      // ["Acme Corp", "John Smith"]
print(extractedInfo.dates)        // [Date: "Effective Date", Date: "Expiration"]
print(extractedInfo.amounts)      // [Decimal: "Total", Decimal: "Deposit"]
print(extractedInfo.keyTerms)     // ["30-day notice", "auto-renewal"]

Document Classification Service

The classification pipeline analyzes document structure and content to determine type:

class DocumentClassificationService {
    private let llmService: LLMService
    private let pdfExtractor: PDFTextExtractorService
    private let wordExtractor: WordTextExtractorService
    
    func classify(document url: URL) async throws -> ClassificationResult {
        // Extract text based on file type
        let text = try await extractText(from: url)
        
        // Build classification prompt
        let prompt = ClassificationPromptBuilder.build(
            documentText: text.prefix(4000),
            availableTypes: DocumentType.allCases,
            availableIndustries: Industry.allCases
        )
        
        // Get AI classification
        let response = try await llmService.complete(prompt)
        
        // Parse structured response
        return try parseClassificationResponse(response)
    }
    
    private func extractText(from url: URL) async throws -> String {
        switch url.pathExtension.lowercased() {
        case "pdf":
            return try await pdfExtractor.extractText(from: url)
        case "docx", "doc":
            return try await wordExtractor.extractText(from: url)
        default:
            throw ExtractionError.unsupportedFormat
        }
    }
}

Extraction Prompt Builder

The key to reliable extraction is well-crafted prompts. The ExtractionPromptBuilder generates industry and document-type specific prompts:

class ExtractionPromptBuilder {
    static func build(
        documentText: String,
        documentType: DocumentType,
        industry: Industry
    ) -> String {
        let schema = schemaFor(documentType: documentType, industry: industry)
        
        return """
        You are a document analysis expert specializing in \(industry.displayName) documents.
        
        Analyze the following \(documentType.displayName) and extract structured information.
        
        Return your response as valid JSON matching this schema:
        \(schema)
        
        Guidelines:
        - Extract exact values as they appear in the document
        - Use ISO 8601 format for dates (YYYY-MM-DD)
        - Use numeric values without currency symbols for amounts
        - Include confidence scores (0.0-1.0) for uncertain extractions
        - Mark fields as null if not found in the document
        
        Document text:
        ---
        \(documentText)
        ---
        
        Extracted JSON:
        """
    }
    
    private static func schemaFor(
        documentType: DocumentType,
        industry: Industry
    ) -> String {
        // Return JSON schema based on document type
        switch documentType {
        case .invoice:
            return invoiceSchema
        case .lease:
            return leaseSchema
        case .medicalRecord:
            return medicalRecordSchema
        // ... schemas for each document type
        default:
            return genericDocumentSchema
        }
    }
}

Extracted Document Info Model

All extractions conform to a unified model that captures common patterns across document types:

struct ExtractedDocumentInfo: Codable {
    // Core identification
    let documentType: DocumentType
    let documentTitle: String?
    let documentDate: Date?
    
    // Parties and entities
    let parties: [ExtractedParty]
    let organizations: [String]
    
    // Temporal data
    let dates: [ExtractedDate]
    let effectiveDate: Date?
    let expirationDate: Date?
    
    // Financial data
    let amounts: [ExtractedAmount]
    let totalAmount: Decimal?
    let currency: String?
    
    // Content analysis
    let keyTerms: [String]
    let obligations: [String]
    let summaryText: String?
    
    // Metadata
    let extractionConfidence: Double
    let processingTime: TimeInterval
    let warnings: [String]
}

struct ExtractedParty: Codable {
    let name: String
    let role: String?  // "Landlord", "Tenant", "Vendor", etc.
    let address: String?
    let email: String?
    let phone: String?
}

struct ExtractedDate: Codable {
    let date: Date
    let label: String  // "Effective Date", "Due Date", etc.
    let confidence: Double
}

struct ExtractedAmount: Codable {
    let value: Decimal
    let label: String  // "Monthly Rent", "Total Due", etc.
    let currency: String?
    let confidence: Double
}

The Dashboard Experience

The DocIQ app wraps the extraction engine in a polished macOS interface with analytics and document management.

Analytics Charts

The dashboard provides visual insights into your document portfolio:

Document Types Pie Chart: See the distribution of document types at a glance—how many invoices vs. contracts vs. medical records.

struct DocumentTypesPieChart: View {
    let data: [DocumentTypeCount]
    
    var body: some View {
        Chart(data) { item in
            SectorMark(
                angle: .value("Count", item.count),
                innerRadius: .ratio(0.5),
                angularInset: 1
            )
            .foregroundStyle(by: .value("Type", item.type.displayName))
            .cornerRadius(4)
        }
        .chartLegend(position: .bottom, spacing: 20)
    }
}

Category Bar Chart: Compare document volumes across categories or time periods.

Document Timeline Chart: Visualize when documents were created, signed, or expire—critical for compliance tracking.

File Size Histogram: Understand the distribution of document sizes in your portfolio.

Insight Cards

Beyond charts, the dashboard surfaces actionable insights:

Upcoming Dates Card: Documents with approaching deadlines—lease expirations, contract renewals, compliance due dates.

struct UpcomingDatesCard: View {
    let upcomingDates: [UpcomingDate]
    
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Label("Upcoming Dates", systemImage: "calendar.badge.exclamationmark")
                .font(.headline)
            
            ForEach(upcomingDates.prefix(5)) { item in
                HStack {
                    VStack(alignment: .leading) {
                        Text(item.documentTitle)
                            .font(.subheadline.weight(.medium))
                        Text(item.dateLabel)
                            .font(.caption)
                            .foregroundStyle(.secondary)
                    }
                    
                    Spacer()
                    
                    Text(item.date, style: .date)
                        .font(.caption.weight(.semibold))
                        .foregroundStyle(item.isUrgent ? .red : .primary)
                }
            }
        }
        .padding()
        .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12))
    }
}

Parties Insight Card: Most frequent parties across your documents—vendors, clients, counterparties.

Sensitivity Summary Card: Documents flagged as containing sensitive information (SSNs, financial data, medical records).

Document List and Detail Views

The document management interface provides:

  • Sortable, filterable document list with search across all extracted fields
  • Quick Look preview for viewing original documents
  • Structured extraction view showing all extracted data in organized cards
  • Export capabilities for extracted data (JSON, CSV)

The Live Extraction Feed

DocIQ Live Extraction Feed

One of my favorite features is the LiveExtractionFeed—a real-time view that shows extraction progress as it happens:

struct LiveExtractionFeed: View {
    @ObservedObject var extractionService: DocumentExtractionService
    
    var body: some View {
        ScrollView {
            LazyVStack(alignment: .leading, spacing: 8) {
                ForEach(extractionService.extractionEvents) { event in
                    ExtractionEventRow(event: event)
                }
            }
        }
    }
}

struct ExtractionEventRow: View {
    let event: ExtractionEvent
    
    var body: some View {
        HStack(spacing: 12) {
            Image(systemName: event.icon)
                .foregroundStyle(event.color)
            
            VStack(alignment: .leading) {
                Text(event.message)
                    .font(.subheadline)
                
                if let detail = event.detail {
                    Text(detail)
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            }
            
            Spacer()
            
            Text(event.timestamp, style: .time)
                .font(.caption2)
                .foregroundStyle(.tertiary)
        }
        .padding(.horizontal)
    }
}

As the AI processes a document, you see events like:

  • 📄 "Extracting text from document..."
  • 🔍 "Classifying document type..."
  • ✅ "Identified as: Commercial Lease"
  • 👥 "Found 2 parties: Acme Corp, Smith Holdings"
  • 📅 "Extracted 4 dates"
  • 💰 "Extracted 3 amounts"
  • ✨ "Extraction complete (confidence: 0.94)"

It's satisfying to watch—and useful for understanding what the AI is doing.

Building with Claude Opus 4.5

The entire DocIQ codebase was generated with Claude Opus 4.5 in Cursor. The extraction from Capvera and generalization to multiple industries was a fascinating exercise in AI-assisted refactoring.

The Extraction Process

I started with Capvera's document extraction code, which was tightly coupled to real estate concepts. Rather than adding industries one at a time, I took a two-step approach:

Step 1: Generate the Industry Taxonomy

Me: "I want to extract the document extraction capabilities from Capvera into a standalone Swift Package that can handle documents from any industry, not just real estate. First, generate a comprehensive list of industries that would benefit from document extraction."

Claude: Generated a taxonomy of 15+ industries—Healthcare, Legal, Finance, Insurance, Manufacturing, Government, Education, Transportation, Agriculture, Oil & Gas, Hospitality, Retail, Pharmaceutical, Construction, and Nonprofit—each with descriptions of their document processing needs.

Step 2: Generate Attributes and Prompts for Each Industry

Me: "Now for each industry, generate the document types they commonly process, the key fields that need to be extracted, and the AI prompts that would reliably extract that information."

Claude: Produced the complete Industry enum, the 200+ DocumentType cases organized by category, extraction schemas for each document type, and the prompt templates in ExtractionPromptBuilder. All in a single comprehensive generation.

This two-step approach was remarkably efficient. Instead of iterating industry-by-industry, Claude understood the pattern after seeing the real estate extraction code and could generalize it across all domains simultaneously.

What the AI Got Right

  • Architectural decisions: Clean separation between the Swift Package (engine) and the app (UI)
  • Protocol design: Abstractions that made adding new document types trivial
  • SwiftUI patterns: Modern, idiomatic views with proper state management
  • Prompt engineering: Effective prompts that produce reliable JSON extraction
  • Error handling: Graceful degradation when extraction confidence is low

What Required Human Guidance

  • Domain expertise: Knowing which fields actually matter for each industry
  • Edge cases: Handling multi-page documents, scanned PDFs, handwritten annotations
  • UX polish: Deciding what information to surface vs. hide in the interface
  • Performance tuning: Optimizing for large document batches

The Real Work Ahead

Here's the honest truth: generating 200+ document types and 15 industries was the easy part. Claude produced comprehensive extraction schemas in hours. But this is a beta—a starting point, not a finished product.

The bulk of the work for a production-ready document intelligence application isn't the code. It's validating and refining each extraction template against real-world documents:

  • Do the extracted fields match what practitioners actually need?
  • Are the prompts reliable across different document formats and layouts?
  • What edge cases break the extraction logic?
  • Which industries need more granular document type distinctions?
  • What compliance or regulatory requirements affect how data should be extracted?

Each industry has its own quirks. A healthcare EOB from Blue Cross looks different than one from Aetna. A commercial lease in Texas has different standard clauses than one in New York. An oil & gas division order varies by basin and operator.

AI can generate the scaffolding, but domain experts need to validate the details. That's the roadmap for DocIQ: systematically working through each industry with practitioners who process these documents daily, refining the extraction schemas until they're production-grade.

Design System

DocIQ uses a centralized design token system for consistency:

struct DesignTokens {
    // Spacing
    static let spacing4: CGFloat = 4
    static let spacing8: CGFloat = 8
    static let spacing12: CGFloat = 12
    static let spacing16: CGFloat = 16
    static let spacing24: CGFloat = 24
    
    // Colors
    static let primaryAccent = Color.blue
    static let successColor = Color.green
    static let warningColor = Color.orange
    static let errorColor = Color.red
    
    // Card styling
    static let cardCornerRadius: CGFloat = 12
    static let cardShadowRadius: CGFloat = 4
    
    // Typography
    static let headingFont = Font.system(.title2, design: .rounded, weight: .semibold)
    static let bodyFont = Font.system(.body)
    static let captionFont = Font.system(.caption)
}

This makes theming changes trivial and ensures visual consistency across all views.

Current State

DocIQ is functional and useful, but there's room for improvement:

What Works Well:

  • PDF and Word document text extraction
  • AI-powered classification with high accuracy
  • Structured data extraction for common document types
  • Dashboard analytics and visualization
  • Document management and search

Future Enhancements:

  • OCR for scanned documents (currently requires text-based PDFs)
  • Batch processing with progress tracking
  • Export to spreadsheet and database formats
  • Custom extraction templates for organization-specific documents
  • Integration with cloud storage (iCloud, Dropbox, Google Drive)

The Journey from Capvera

Building DocIQ reinforced something I've learned repeatedly: general solutions often emerge from specific ones. Capvera's document extraction was built for real estate, but the patterns—text extraction, AI analysis, structured output—apply everywhere.

The refactoring process was surprisingly smooth with AI assistance. Claude understood the architectural goal and helped identify the abstractions needed to generalize the code. What might have taken weeks of manual refactoring happened in hours of conversation.

Try It Yourself

Download the macOS demo and try processing some documents. The classification is surprisingly accurate, and the extraction captures the fields that actually matter for each document type.

Whether you're processing invoices for accounting, contracts for legal review, or medical forms for healthcare administration, DocIQ turns unstructured documents into structured, actionable data.

DocIQ
DocIQ
Document Intelligence

Extract structured data from PDFs across 15 industries. AI-powered classification and extraction for invoices, contracts, medical forms, and 200+ document types.

⬇️ Download (4.5 MB)

Free download • Requires macOS 26+


Daniel Wanja is a developer and founder of Nouvelles Solutions, Inc. He builds macOS and iOS applications with AI assistance, exploring the intersection of document intelligence and modern Swift development.