Swift: usa el multihilo de la function en un file separado

Estoy intentando utilizar multihilo en Swift para recuperar y mostrar una image en un controller de vista. Sin embargo, la function que estoy usando para recuperar la image está en un model separado. Podría simplemente copyr y pegar la function en el controller de vista, pero volveré a utilizar esta function varias veces y preferiría mantenerla separada del controller de vista específico.

La function que tengo actualmente (de nuevo, en un file separado que he nombrado WikimediaAPI) es la siguiente:

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 } 

Lo anterior funciona, pero no es multiprocess y cuando lo comparo con el controller de vista desde un controller de vista de tabla, toma demasiado time. He tratado de implementar multihilo en algún lugar a lo largo de estas líneas, pero no puedo devolver nada dentro del bloque asíncrono

 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 } 

Tenía la idea de que sería posible devolver un cierre con el bloque de código asíncrono en lugar del UIImage, pero no estoy seguro de cómo implementar esto. Y, si lo hiciera, todavía necesitaría configurar la image en el controller de vista, que no estoy seguro de cómo hacer.

Aquí está la llamada a la function en el controller de vista:

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

Estoy usando un dictionary (wikimediaPageTitleDict) de un plist para asociar una cadena con la página derecha de wikipedia (allergy.0 es esa cadena)

Toda la ayuda apreciada. ¡Gracias!

Tanto AlamofireImage como SDWebImage hacen esto por ti y extienden UIImageView por lo que simplemente llamas a un método en la vista de la image y cuando la image se descarga (o se recupera del caching) la vista de la image se actualiza. Sugiero que uses una biblioteca para esto. Pero si quieres hacerlo tú mismo (nota a los guardias para eliminar la si pirámide de la fatalidad):

  typealias ImageCallback = (UIImage?) -> Void func getThumbnailImage(forPage page: String, completion: @escaping ImageCallback) -> void { DispatchQueue.global().async { var imageToReturn: UIImage? = nil defer { completion(imageToReturn) } guard let data = try? Data(contentsOf: URL(string: "https://en.wikipedia.org/w/api.php?action=query&titles=" + page + "&prop=pageimages&format=json&pithumbsize=1000")!) else { return } let json = JSON(data: data) guard let pageId = json["query"]["pages"].dictionary?.first?.key, let imageUrl = json["query"]["pages"][pageId]["thumbnail"]["source"].string, let url = URL(string: imageUrl) else { return } guard let imageData = try? Data(contentsOf: url), let image = UIImage(data: imageData) else { return } imageToReturn = image } } getThumbnailImage(forPage: "Escherichia_coli") { image in DispatchQueue.main.async { imageView.image = image } } 

Si está utilizando un código asynchronous, no puede devolver un resultado de su function. Esa no es la forma en que funciona el async. Una llamada de function asíncrona pone en queue el resultado a search en algún momento posterior. La function luego vuelve antes de que los resultados estén disponibles.

Lo que usualmente hace con las funciones asíncronas es pasar un bloque de finalización que se llama cuando los resultados están disponibles. Puede pasar un cierre que instale la image recién descargada en la vista de image apropiada en el controller de vista, por ejemplo.

Mira mi respuesta en este hilo.

Almacenamiento de valores en completionHandlers – Swift

Doy un ejemplo de funcionamiento de una function asíncrona que lleva un controller de finalización.

No puede devolver nada dentro de un cierre asíncrono de envío. Sin embargo, puede pasar la variable UIImageView a su getThumbnailImage y cuando reciba la image, puede configurarlo de inmediato.

 public func getThumbnailImage(forPage page: String, imageView : UIImageView) { ... ... if let imageData = try? Data(contentsOf: url) { DispatchQueue.main.async { if let image = UIImage(data: imageData) { imageView.image = image } } } }