diff --git a/ios/Classes/SwiftFlutterHtmlToPdfPlugin.swift b/ios/Classes/SwiftFlutterHtmlToPdfPlugin.swift index b9d51d2..2382db1 100644 --- a/ios/Classes/SwiftFlutterHtmlToPdfPlugin.swift +++ b/ios/Classes/SwiftFlutterHtmlToPdfPlugin.swift @@ -5,6 +5,7 @@ import WebKit public class SwiftFlutterHtmlToPdfPlugin: NSObject, FlutterPlugin{ var wkWebView : WKWebView! var urlObservation: NSKeyValueObservation? + private var didFinishOnce: Bool = false public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "flutter_html_to_pdf", binaryMessenger: registrar.messenger()) @@ -12,28 +13,72 @@ public class SwiftFlutterHtmlToPdfPlugin: NSObject, FlutterPlugin{ registrar.addMethodCallDelegate(instance, channel: channel) } + private func topViewController() -> UIViewController? { + let window: UIWindow? + if #available(iOS 13.0, *) { + window = UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .first(where: { $0.activationState == .foregroundActive })? + .windows + .first(where: { $0.isKeyWindow }) + } else { + window = UIApplication.shared.keyWindow ?? UIApplication.shared.delegate?.window ?? nil + } + + var vc = window?.rootViewController + while let presented = vc?.presentedViewController { + vc = presented + } + return vc + } + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "convertHtmlToPdf": - let args = call.arguments as? [String: Any] - let htmlFilePath = args!["htmlFilePath"] as? String + guard let args = call.arguments as? [String: Any], + let htmlFilePath = args["htmlFilePath"] as? String, + !htmlFilePath.isEmpty else { + result(FlutterError(code: "INVALID_ARGUMENTS", + message: "Missing htmlFilePath", + details: nil)) + return + } // !!! this is workaround for issue with rendering PDF images on iOS !!! - let viewControler = UIApplication.shared.delegate?.window?!.rootViewController - wkWebView = WKWebView.init(frame: viewControler!.view.bounds) + guard let viewControler = topViewController() else { + result(FlutterError(code: "NO_VIEW_CONTROLLER", + message: "Could not find a root view controller", + details: nil)) + return + } + + didFinishOnce = false + wkWebView = WKWebView.init(frame: viewControler.view.bounds) wkWebView.isHidden = true wkWebView.tag = 100 - viewControler?.view.addSubview(wkWebView) + viewControler.view.addSubview(wkWebView) - let htmlFileContent = FileHelper.getContent(from: htmlFilePath!) // get html content from file + let htmlFileContent = FileHelper.getContent(from: htmlFilePath) // get html content from file wkWebView.loadHTMLString(htmlFileContent, baseURL: Bundle.main.bundleURL) // load html into hidden webview - urlObservation = wkWebView.observe(\.isLoading, changeHandler: { (webView, change) in + urlObservation = wkWebView.observe(\.isLoading, changeHandler: { [weak self] (webView, change) in + guard let self = self else { return } + if webView.isLoading { return } + if self.didFinishOnce { return } + self.didFinishOnce = true + // this is workaround for issue with loading local images DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { - let convertedFileURL = PDFCreator.create(printFormatter: self.wkWebView.viewPrintFormatter()) + guard let wkWebView = self.wkWebView else { + result(FlutterError(code: "WEBVIEW_DISPOSED", + message: "WKWebView was disposed before PDF generation finished", + details: nil)) + return + } + + let convertedFileURL = PDFCreator.create(printFormatter: wkWebView.viewPrintFormatter()) let convertedFilePath = convertedFileURL.absoluteString.replacingOccurrences(of: "file://", with: "") // return generated pdf path - if let viewWithTag = viewControler?.view.viewWithTag(100) { + if let viewWithTag = viewControler.view.viewWithTag(100) { viewWithTag.removeFromSuperview() // remove hidden webview when pdf is generated // clear WKWebView cache diff --git a/pubspec.yaml b/pubspec.yaml index db314ef..7d9450b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.7.0 homepage: https://github.com/Afur/flutter_html_to_pdf environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.12.0 <4.0.0' flutter: ">=1.12.0" dependencies: