Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to set a variable that runs on background thread and needs to access ui

I am fetching a data source in background. There are 2 urls and I choose it with a tabBar. To know which url I need to access, I use navigationController?.tabBarItem.tag. But It throws an error of "navigationController must be used from main thread only". I’ve tried to wrap it with DispatchQueue.main.async but it didn’t work. Any fix or new approach appreciated.

override func viewDidLoad() {
    super.viewDidLoad()
    performSelector(inBackground: #selector(fetchJSON), with: nil)
}

@objc func fetchJSON() {
    let urlString: String
    
    if navigationController?.tabBarItem.tag == 0 {a
        urlString = "https://www.hackingwithswift.com/samples/petitions-1.json"
    } else {
        urlString = "https://www.hackingwithswift.com/samples/petitions-2.json"
    }
    
    if let url = URL(string: urlString) {
        if let data = try? Data(contentsOf: url) {
            parse(json: data)
            return
        }
    }
    performSelector(onMainThread: #selector(showError), with: nil, waitUntilDone: false)
}

>Solution :

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Move the logic that needs to access the UI to the main thread, then pass the result as an argument to your function on the background thread.

Here, there’s several issues:

  • The performSelector(…) methods are quite low-level and not a good solution with Swift. Avoid these, they have issues and make it cumbersome to pass arguments around. Use GCD or async/await instead.
  • Using the synchronous Data(contentsOf: …) is also not a good idea. If you would asynchronous solutions you wouldn’t run into the threading issue in the first place.

I really suggest you look into the second problem (e.g. using a DataTask), as it completely eliminates your threading issues, but here’s a simple way to refactor your existing code using GCD that should already work:

override func viewDidLoad() {
    super.viewDidLoad()

    let urlString: String
    
    if navigationController?.tabBarItem.tag == 0 {a
        urlString = "https://www.hackingwithswift.com/samples/petitions-1.json"
    } else {
        urlString = "https://www.hackingwithswift.com/samples/petitions-2.json"
    }

    DispatchQueue.global(qos: .utility).async {
        self.fetchJSON(urlString)
    }
}

func fetchJSON(_ urlString: String) {    
    if let url = URL(string: urlString) {
        if let data = try? Data(contentsOf: url) {
            parse(json: data)
            return
        }
    }

    DispatchQueue.main.async {
        self.showError()
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading