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)
}
CodePudding user response:
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 orasync/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()
}
}