Convert view to PDF

I'm trying to convert a view (ScrollView) to PDF, on my physical device iPhone 7 with iOS 15.8 it works perfectly, but on my friends' iPhone 11 and iPhone 13 both with iOS 17+ the application crashes at the time of conversion, it also crashes in the xCode preview and in the Simulators without showing errors.

I call the function like this:

Button(action: { if included && !saving { if signatureImage != nil { exportPDF(name: "ToR-(patient.name)-(patient.id)") { self } completion: { status, url in if let url = url, status { self.PDFUrl = url self.showActionSheet.toggle() print("URL => (url) - Status (status)") } else { print("Failed") } } } else { self.scrollToBottom = true } } }) { if saving { ProgressView() .tint(Color("MainColor")) .scaleEffect(1.5) } else { Text(NSLocalizedString("Save", comment: "")) } }

And the function is this:

import SwiftUI

import UIKit

extension View { func convertToScrollView<Content: View>(@ViewBuilder content: @escaping () -> Content) -> UIScrollView { let scrollView = UIScrollView() let hostingController = UIHostingController(rootView: content()).view! hostingController.translatesAutoresizingMaskIntoConstraints = false

    let constraints = [
        hostingController.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
        hostingController.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
        hostingController.topAnchor.constraint(equalTo: scrollView.topAnchor),
        hostingController.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
        hostingController.widthAnchor.constraint(equalToConstant: screenBounds().width)
    ]
    scrollView.addSubview(hostingController)
    scrollView.addConstraints(constraints)
    scrollView.layoutIfNeeded()

    return scrollView
}

func exportPDF<Content: View>(name: String, @ViewBuilder content: @escaping () -> Content, completion: @escaping (Bool, URL?) -> ()) {
    let documentDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
    let outputFileURL = documentDirectory.appendingPathComponent("\(name).pdf")

    let pdfView = convertToScrollView {
        content()
    }
    pdfView.tag = 1009
    let size = pdfView.contentSize
    pdfView.frame = CGRect(x: 0, y: getSafeArea().top, width: size.width, height: size.height)
    
    getRootController().view.insertSubview(pdfView, at: 0)
    
    let renderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: size.width, height: size.height))

    do {
        try renderer.writePDF(to: outputFileURL, withActions: { context in
            context.beginPage()
            pdfView.layer.render(in: context.cgContext)
        })
        
        completion(true, outputFileURL)
    }
    catch {
        completion(false, nil)
        print(error.localizedDescription)
    }
    
    getRootController().view.subviews.forEach { view in
        if view.tag == 1009 {
            view.removeFromSuperview()
        }
    }
    //pdfView.removeFromSuperview()
}

func screenBounds() -> CGRect {
    return UIScreen.main.bounds
}

func getRootController() -> UIViewController {
    guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
        return .init()
    }

    guard let root = screen.windows.first?.rootViewController else {
        return .init()
    }

    return root
}

func getSafeArea() -> UIEdgeInsets {
    guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
        return .zero
    }

    guard let safeArea = screen.windows.first?.safeAreaInsets else {
        return .zero
    }

    return safeArea
}

}

Please reformat your code. The top part isn't inside the code block, so it's difficult to read.

Where in code does it crash ?

What is the error message if any (crashlog) ?

Button code reformatted:

Button(action: {
    if included && !saving {
        if signatureImage != nil {
            exportPDF(name: "ToR-\(patient.name)-\(patient.id)") {
                self
            } completion: { status, url in
                if let url = url, status {
                    self.PDFUrl = url
                    self.showActionSheet.toggle()
                    print("URL => \(url) - Status \(status)")
                } else {
                    print("Failed")
                }
            }
        } else {
            self.scrollToBottom = true
        }
    }
}) {
    if saving {
        ProgressView()
            .tint(Color("MainColor"))
            .scaleEffect(1.5)
    } else {
        Text(NSLocalizedString("Save", comment: ""))
    }
}

pdfGenerator code reformatted:

import SwiftUI
import UIKit

extension View {
    func convertToScrollView<Content: View>(@ViewBuilder content: @escaping () -> Content) -> UIScrollView {
        let scrollView = UIScrollView()
        let hostingController = UIHostingController(rootView: content()).view!
        hostingController.translatesAutoresizingMaskIntoConstraints = false

        let constraints = [
            hostingController.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            hostingController.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            hostingController.topAnchor.constraint(equalTo: scrollView.topAnchor),
            hostingController.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            hostingController.widthAnchor.constraint(equalToConstant: screenBounds().width)
        ]
        scrollView.addSubview(hostingController)
        scrollView.addConstraints(constraints)
        scrollView.layoutIfNeeded()

        return scrollView
    }

    func exportPDF<Content: View>(name: String, @ViewBuilder content: @escaping () -> Content, completion: @escaping (Bool, URL?) -> ()) {
        let documentDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
        let outputFileURL = documentDirectory.appendingPathComponent("\(name).pdf")

        let pdfView = convertToScrollView {
            content()
        }
        pdfView.tag = 1009
        let size = pdfView.contentSize
        pdfView.frame = CGRect(x: 0, y: getSafeArea().top, width: size.width, height: size.height)
        
        getRootController().view.insertSubview(pdfView, at: 0)
        
        let renderer = UIGraphicsPDFRenderer(bounds: CGRect(x: 0, y: 0, width: size.width, height: size.height))

        do {
            try renderer.writePDF(to: outputFileURL, withActions: { context in
                context.beginPage()
                pdfView.layer.render(in: context.cgContext)
            })
            
            completion(true, outputFileURL)
        }
        catch {
            completion(false, nil)
            print(error.localizedDescription)
        }
        
        getRootController().view.subviews.forEach { view in
            if view.tag == 1009 {
                view.removeFromSuperview()
            }
        }
        //pdfView.removeFromSuperview()
    }

    func screenBounds() -> CGRect {
        return UIScreen.main.bounds
    }

    func getRootController() -> UIViewController {
        guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
            return .init()
        }

        guard let root = screen.windows.first?.rootViewController else {
            return .init()
        }

        return root
    }

    func getSafeArea() -> UIEdgeInsets {
        guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
            return .zero
        }

        guard let safeArea = screen.windows.first?.safeAreaInsets else {
            return .zero
        }

        return safeArea
    }
}
Convert view to PDF
 
 
Q