Search code examples
iosswiftmultithreadingmbprogresshuddispatch-async

'MBProgressHUD needs to be accessed on the main thread.' - Swift 3


I am getting 'MBProgressHUD needs to be accessed on the main thread.' error on hiding MBProgressHUD once page is loaded with data from web service. Please help me to solve this. Stuck on this issue since hours now. My code is :

                DispatchQueue.main.async {
                    self.spinnerActivity.hide(animated: true)
                    self.tableView.reloadData()
                    self.refreshControl.endRefreshing()
                }

I started the spinner in viewDidLoad as follows :

    spinnerActivity = MBProgressHUD.showAdded(to: self.view, animated: true);
    spinnerActivity.label.text = "Loading";
    spinnerActivity.detailsLabel.text = "Please Wait!!";

Please help.

Edit:

This is my whole code of that view controller :

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

@IBOutlet weak var tableView: UITableView!

var refreshControl: UIRefreshControl!
var spinnerActivity: MBProgressHUD! = nil

override func viewDidLoad() {
    super.viewDidLoad()

    refreshControl = UIRefreshControl()
    refreshControl.tintColor = UIColor.green
    refreshControl.attributedTitle = NSAttributedString(string: "Refreshing", attributes: [NSForegroundColorAttributeName: UIColor.white])
    refreshControl.addTarget(self, action: #selector(ViewController.refresh(sender:)), for: UIControlEvents.valueChanged)
    tableView.addSubview(refreshControl)

    spinnerActivity = MBProgressHUD.showAdded(to: self.view, animated: true);
    spinnerActivity.label.text = "Loading";
    spinnerActivity.detailsLabel.text = "Please Wait!!";

    self.navigationItem.hidesBackButton = true
    let newBackButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.plain, target: self, action: #selector(ViewController.back(sender:)))
    self.navigationItem.leftBarButtonItem = newBackButton

    navigationItem.title = city.uppercased()

    self.runWebService()
}

func runWebService(){


    let url = URL(string: "")!// have removed my url

    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in // URLSession.shared().dataTask(with: url) { (data, response, error) is now URLSession.shared.dataTask(with: url) { (data, response, error)

        if error != nil {
            self.spinnerActivity.hide(animated: true)
            print(error)
        } else {
            if let urlContent = data {
                do {
                    guard let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as? [AnyObject] else {
                        self.spinnerActivity.hide(animated: true)
                        return
                    }
                    //processing web service
                    //DispatchQueue.global(qos: .userInitiated).async {
                    DispatchQueue.main.async {
                        self.spinnerActivity.hide(animated: true)
                        self.tableView.reloadData()
                        self.refreshControl.endRefreshing()
                    }
                } catch {
                    self.spinnerActivity.hide(animated: true)
                    print("JSON Processing Failed")
                }
            }
        }
    }
    task.resume()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func back(sender: UIBarButtonItem) {
    _ = navigationController?.popToRootViewController(animated: true)
}

func refresh(sender:AnyObject) {
    //pull to refresh
    self.runWebService()
}

//remaining are table view data source and delegate methods   }

Solution

  • You're trying to hide the MBProgressHUD from the background in all of your error handling. Switch over to the main thread to fix:

    func runWebService(){
    
        let url = URL(string: "http://www.google.com")!// have removed my url
    
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in // URLSession.shared().dataTask(with: url) { (data, response, error) is now URLSession.shared.dataTask(with: url) { (data, response, error)
    
            if error != nil {
                DispatchQueue.main.async {
                    self.spinnerActivity.hide(animated: true)
                }
    
                print(error)
            } else {
                if let urlContent = data {
                    do {
                        guard let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as? [AnyObject] else {
                            DispatchQueue.main.async {
                                self.spinnerActivity.hide(animated: true)
                            }
                            return
                        }
                        //processing web service
                        //DispatchQueue.global(qos: .userInitiated).async {
                        DispatchQueue.main.async {
                            self.spinnerActivity.hide(animated: true)
                        }
                    } catch {
                        DispatchQueue.main.async {
                            self.spinnerActivity.hide(animated: true)
                        }
                        print("JSON Processing Failed")
                    }
                }
            }
        }
        task.resume()
    }
    

    ** Note that I removed some of your code to test.