← Back to Home

Working with PDFs in SwiftUI: Best Packages and Implementation

SwiftUI PDF SPM

Working with PDFs in SwiftUI can be enhanced using third-party packages. In this guide, we'll explore popular PDF packages and how to implement them in your SwiftUI applications.

1. Adding PDFKit Integration

While PDFKit is built into iOS, we can create a SwiftUI wrapper for better integration:

PDFKitView.swift
import SwiftUI
import PDFKit

struct PDFKitView: UIViewRepresentable {
    let pdfDocument: PDFDocument
    
    func makeUIView(context: Context) -> PDFView {
        let pdfView = PDFView()
        pdfView.document = pdfDocument
        pdfView.autoScales = true
        return pdfView
    }
    
    func updateUIView(_ uiView: PDFView, context: Context) {
        uiView.document = pdfDocument
    }
}

// Usage Example
struct PDFViewer: View {
    let pdfDoc = PDFDocument(url: Bundle.main.url(
        forResource: "sample",
        withExtension: "pdf"
    )!)!
    
    var body: some View {
        PDFKitView(pdfDocument: pdfDoc)
    }
}

2. Using PSPDFKit

PSPDFKit is a powerful commercial PDF library. First, add the package to your project:

Package.swift
let package = Package(
    dependencies: [
        .package(
            url: "https://github.com/PSPDFKit/PSPDFKit-SP.git",
            from: "12.0.0"
        )
    ]
)

Then implement the viewer:

PSPDFKitView.swift
import SwiftUI
import PSPDFKit

struct PSPDFKitView: UIViewControllerRepresentable {
    let document: Document
    
    func makeUIViewController(context: Context) -> PDFViewController {
        let pdfController = PDFViewController(document: document)
        pdfController.pageMode = .single
        return pdfController
    }
    
    func updateUIViewController(
        _ uiViewController: PDFViewController,
        context: Context
    ) {
        // Update if needed
    }
}

3. Using PDFGenerator

For PDF generation, we can use the PDFGenerator package. Add it to your project:

Package.swift
.package(
    url: "https://github.com/sgr-ksmt/PDFGenerator.git", 
    from: "3.0.0"
)

Implementation example:

PDFGenerator.swift
import SwiftUI
import PDFGenerator

class PDFCreator {
    static func generatePDF(from view: some View) -> URL? {
        // Create a hosting controller for the SwiftUI view
        let hostingController = UIHostingController(rootView: view)
        hostingController.view.frame = CGRect(x: 0, y: 0, width: 595, height: 842) // A4 size
        
        // Generate PDF
        let pdfData = try? PDFGenerator.generate(hostingController.view)
        
        // Save to temporary file
        let tempURL = FileManager.default.temporaryDirectory
            .appendingPathComponent(UUID().uuidString)
            .appendingPathExtension("pdf")
        
        try? pdfData?.write(to: tempURL)
        return tempURL
    }
}

// Usage Example
struct PDFContent: View {
    var body: some View {
        VStack(spacing: 20) {
            Text("PDF Content")
                .font(.largeTitle)
            
            Image("sample-image")
                .resizable()
                .scaledToFit()
                .frame(height: 200)
            
            Text("Description text here...")
                .padding()
        }
    }
}

struct ContentView: View {
    var body: some View {
        Button("Generate PDF") {
            if let pdfURL = PDFCreator.generatePDF(from: PDFContent()) {
                // Share or display the PDF
                let activityVC = UIActivityViewController(
                    activityItems: [pdfURL],
                    applicationActivities: nil
                )
                
                // Present the share sheet
                UIApplication.shared.windows.first?.rootViewController?
                    .present(activityVC, animated: true)
            }
        }
    }
}

4. Handling PDF Forms

For interactive PDF forms, we can create a custom implementation:

PDFFormHandler.swift
import PDFKit

class PDFFormHandler {
    let pdfDocument: PDFDocument
    
    init(pdfDocument: PDFDocument) {
        self.pdfDocument = pdfDocument
    }
    
    func fillForm(with data: [String: String]) {
        for i in 0..<self.pdfDocument.pageCount {
            guard let page = self.pdfDocument.page(at: i) else { continue }
            
            for annotation in page.annotations {
                guard let field = annotation as? PDFAnnotation,
                      let fieldName = field.fieldName,
                      let value = data[fieldName] else { continue }
                
                field.widgetStringValue = value
            }
        }
    }
    
    func saveForm() -> Data? {
        return self.pdfDocument.dataRepresentation()
    }
}

Best Practices

  • Always handle PDF loading errors gracefully
  • Consider memory usage when dealing with large PDFs
  • Implement proper caching for frequently accessed PDFs
  • Use appropriate compression for generated PDFs
  • Consider accessibility features for PDF viewing

Common Issues and Solutions

  • Memory issues with large PDFs: Implement page loading on demand
  • PDF rendering performance: Use appropriate zoom levels and caching
  • Form field handling: Properly validate input before saving
  • PDF generation errors: Implement proper error handling and logging

These implementations provide a solid foundation for working with PDFs in your SwiftUI applications. Choose the approach that best fits your specific requirements and performance needs.