Convert SwiftUI View to PDF on iOS

After some thinking I came up with the idea of combining the UIKit to PDF method and SwiftUI.

At first you create your SwiftUI view, then you put into an UIHostingController. You render the HostingController on a window behind all other views and and draw its layer on a PDF. Sample code is listed below.

func exportToPDF() {

    let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let outputFileURL = documentDirectory.appendingPathComponent("SwiftUI.pdf")

    //Normal with
    let width: CGFloat = 8.5 * 72.0
    //Estimate the height of your view
    let height: CGFloat = 1000
    let charts = ChartsView()

    let pdfVC = UIHostingController(rootView: charts)
    pdfVC.view.frame = CGRect(x: 0, y: 0, width: width, height: height)

    //Render the view behind all other views
    let rootVC = UIApplication.shared.windows.first?.rootViewController
    rootVC?.addChild(pdfVC)
    rootVC?.view.insertSubview(pdfVC.view, at: 0)

    //Render the PDF
    let pdfRenderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: 8.5 * 72.0, height: height))

    do {
        try pdfRenderer.writePDF(to: outputFileURL, withActions: { (context) in
            context.beginPage()
            pdfVC.view.layer.render(in: context.cgContext)
        })

        self.exportURL = outputFileURL
        self.showExportSheet = true

    }catch {
        self.showError = true
        print("Could not create PDF file: \(error)")
    }

    pdfVC.removeFromParent()
    pdfVC.view.removeFromSuperview()
}

I loved this answer, but couldn't get it to work. I was getting an exception and the catch wasn't being executed.

After some head scratching, and writing up a SO Question asking how to debug it (which I never submitted), I realized that the solution, while not obvious, was simple: wrap the rendering phase in an async call to the main queue:

//Render the PDF

 let pdfRenderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: 8.5 * 72.0, height: height))
 DispatchQueue.main.async {
     do {
         try pdfRenderer.writePDF(to: outputFileURL, withActions: { (context) in
             context.beginPage()
             pdfVC.view.layer.render(in: context.cgContext)
         })
         print("wrote file to: \(outputFileURL.path)")
     } catch {
         print("Could not create PDF file: \(error.localizedDescription)")
     }
 }

Thanks, SnOwfreeze!