Dia Dia - 10 days ago 14
iOS Question

'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 }

Answer

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.