1

I have a app that can download files from a webview. It was working fine in ios 12 but isnt working. I'm getting the error

Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.

and

This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.

This is my view.controller code:

func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebView.NavigationType) -> Bool {
    print(request.url as Any)
    if request.url!.absoluteString.range(of: "/download/") != nil {
        let extention = request.url!.absoluteString.slice(from: "&fileextension=", to: "&")?.lowercased()
        var name = request.url!.absoluteString.slice(from: "&name=", to: "&")?.lowercased()
        name =  name?.replacingOccurrences(of: "+", with: " ")
        DownlondFromUrl(request.url! as URL,name!, extention!)
        return false
    }
    return true
}
func DownlondFromUrl(_ url: URL,_ name:String,_ extensionfile:String){
    // Create destination URL
    let documentsUrl =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
    let destinationUrl = documentsUrl!.appendingPathComponent(name + ".\(extensionfile)")

    //Create URL to the source file you want to download
    let fileURL = url

    let sessionConfig = URLSessionConfiguration.default
    let session = URLSession(configuration: sessionConfig)

    let request = URLRequest(url:fileURL)

    let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
        if let tempLocalUrl = tempLocalUrl, error == nil {
            let dataFromURL = NSData(contentsOf: tempLocalUrl)
            dataFromURL?.write(to: destinationUrl, atomically: true)

            let alert = UIAlertController.init(title: "Download", message: "File download Successful. Do you want open file ", preferredStyle: .actionSheet)
            alert.addAction(UIAlertAction(title: "Open", style: .default , handler:{ (UIAlertAction)in
                let fileBrowser = FileBrowser()
                self.present(fileBrowser, animated: true, completion: nil)
            }))
            alert.addAction(UIAlertAction(title: "Share", style: .default , handler:{ (UIAlertAction)in

                let activityViewController = UIActivityViewController(activityItems: [destinationUrl], applicationActivities: nil)
                activityViewController.popoverPresentationController?.sourceView = self.view
                self.present(activityViewController, animated: true, completion: nil)
            }))
            alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel, handler:{ (UIAlertAction)in
            }))

            self.present(alert, animated: true, completion: {
                print("completion block")
            })
        } else {
            print("Error took place while downloading a file. Error description: %@", error?.localizedDescription as Any);
        }
    }
    task.resume()
}
override func viewWillAppear(_ animated: Bool) {
    self.navigationController?.isNavigationBarHidden = true;
}

}

I have found this post (Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread) but I'm very much a beginner and not sure how to implement this.

  • I have a similar requirement, but it's related to download of zip files using WKWebView. Your question is the solution to my requirement. Can you please edit and update this code for Swift 5 ? – Zahurafzal Mirza Oct 21 '20 at 07:11

1 Answers1

1

You have to run any code which accesses the UI on the main thread.

As URLSession tasks run on a background thread you have to add a DispatchQueue block

DispatchQueue.main.async {
    let alert = UIAlertController.init(title: "Download", message: "File download Successful. Do you want open file ", preferredStyle: .actionSheet)
    alert.addAction(UIAlertAction(title: "Open", style: .default , handler:{ action in
        let fileBrowser = FileBrowser()
        self.present(fileBrowser, animated: true, completion: nil)
    }))
    alert.addAction(UIAlertAction(title: "Share", style: .default , handler:{ action in

        let activityViewController = UIActivityViewController(activityItems: [destinationUrl], applicationActivities: nil)
        activityViewController.popoverPresentationController?.sourceView = self.view
        self.present(activityViewController, animated: true, completion: nil)
    }))
    alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel)

    self.present(alert, animated: true, completion: {
        print("completion block")
    })
}

Note:

The parameter in the UIAlertAction closure must be an instance not a type. If the parameter is not used you can replace it with an underscore character (for example handler:{ _ in)

vadian
  • 274,689
  • 30
  • 353
  • 361