I'm trying to use multithreading in Swift to retrieve and display an image in a view controller. However, the function I'm using to retrieve the image is in a separate model. I could just copy and paste the function into the view controller, but I will be reusing this function multiple times and would prefer to keep it separate from the specific view controller.
The function I currently have (again, in a separate file I've named WikimediaAPI) is the following:
public func getThumbnailImage(forPage page: String) -> UIImage? {
if let data = try? Data(contentsOf: URL(string: "https://en.wikipedia.org/w/api.php?action=query&titles=" + page + "&prop=pageimages&format=json&pithumbsize=1000")!) {
let json = JSON(data: data)
if let pageId = json["query"]["pages"].dictionary?.first?.key {
if let imageUrl = json["query"]["pages"][pageId]["thumbnail"]["source"].string {
if let url = URL(string: imageUrl) {
if let imageData = try? Data(contentsOf: url) {
if let image = UIImage(data: imageData) {
return image
}
}
}
}
}
}
return nil
}
The above works, but is not multithreaded and when I segue to the view controller from a table view controller it takes too long. I've tried to implement multithreading somewhere along these lines, but I am not allowed to return anything within the asynchronous block
public func getThumbnailImage(forPage page: String) -> UIImage? {
DispatchQueue.global().async {
if let data = try? Data(contentsOf: URL(string: "https://en.wikipedia.org/w/api.php?action=query&titles=" + page + "&prop=pageimages&format=json&pithumbsize=1000")!) {
DispatchQueue.main.async {
let json = JSON(data: data)
if let pageId = json["query"]["pages"].dictionary?.first?.key {
if let imageUrl = json["query"]["pages"][pageId]["thumbnail"]["source"].string {
if let url = URL(string: imageUrl) {
DispatchQueue.global().async {
if let imageData = try? Data(contentsOf: url) {
DispatchQueue.main.async {
if let image = UIImage(data: imageData) {
return image //error: "Unexpected non-void return value in void function"
}
}
}
}
}
}
}
}
}
}
return nil
}
I had the thought that it might be possible to return a closure with the asynchronous code block instead of the UIImage, but am not sure how to implement this. And, if I did that, I would still need set the image in the view controller, which I'm not sure how to do.
Here is the call to the function in the view controller:
let wikiQuery = WikimediaAPI() // the class from above
fileprivate func setImage() {
if let pageTitle = wikimediaPageTitleDict[(allergy.0).lowercased()] {
if let image = wikiQuery.getThumbnailImage(forPage: pageTitle) {
wikipediaImage.image = image
wikipediaImage.contentMode = .scaleAspectFit
}
}
}
I'm using a dictionary (wikimediaPageTitleDict) from a plist to associate a string with the right wikipedia page (allergy.0 is that string)
All help appreciated. Thank you!