He creado una clase de utilidad en mi proyecto Swift que maneja todas las solicitudes y respuestas REST. He creado una API REST simple para poder probar mi código. He creado un método de clase que debe devolver un NSArray, pero debido a que la llamada a la API es asíncrona, necesito regresar desde el método dentro de la llamada asíncrona. El problema es que el async devuelve vacío. Si estuviera haciendo esto en Node, usaría las promesas de JS pero no puedo encontrar una solución que funcione en Swift.
import Foundation
class Bookshop {
class func getGenres() -> NSArray {
println("Hello inside getGenres")
let urlPath = "http://creative.coventry.ac.uk/~bookshop/v1.1/index.php/genre/list"
println(urlPath)
let url: NSURL = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
var resultsArray:NSArray!
let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
println("Task completed")
if(error) {
println(error.localizedDescription)
}
var err: NSError?
var options:NSJSONReadingOptions = NSJSONReadingOptions.MutableContainers
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: options, error: &err) as NSDictionary
if(err != nil) {
println("JSON Error \(err!.localizedDescription)")
}
//NSLog("jsonResults %@", jsonResult)
let results: NSArray = jsonResult["genres"] as NSArray
NSLog("jsonResults %@", results)
resultsArray = results
return resultsArray // error [anyObject] is not a subType of 'Void'
})
task.resume()
//return "Hello World!"
// I want to return the NSArray...
}
}
ios
rest
asynchronous
swift
Mark Tyers
fuente
fuente
Respuestas:
Puede pasar la devolución de llamada y la devolución de llamada dentro de la llamada asíncrona
algo como:
y luego llame a este método:
fuente
override func viewDidLoad() { super.viewDidLoad() var genres = Bookshop.getGenres() // Missing argument for parameter #1 in call //var genres:NSArray //Bookshop.getGenres(genres) NSLog("View Controller: %@", genres) }
Swiftz ya ofrece Future, que es el componente básico de una Promise. Un futuro es una promesa que no puede fallar (todos los términos aquí se basan en la interpretación de Scala, donde una promesa es una mónada ).
https://github.com/maxpow4h/swiftz/blob/master/swiftz/Future.swift
Con suerte, eventualmente se expandirá a una Promesa completa al estilo Scala (puedo escribirla yo mismo en algún momento; estoy seguro de que otros RP serían bienvenidos; no es tan difícil con Future ya implementado).
En su caso particular, probablemente crearía un
Result<[Book]>
(basado en la versión de Alexandros Salazar deResult
). Entonces la firma de su método sería:Notas
get
en Swift. Romperá ciertos tipos de interoperabilidad con ObjC.Book
objeto antes de devolver los resultados comoFuture
. Hay varias formas en que este sistema puede fallar, y es mucho más conveniente si verifica todas esas cosas antes de envolverlas en un archivoFuture
. Llegar[Book]
es mucho mejor para el resto de su código Swift que entregar unNSArray
.fuente
Future
. Pero eche un vistazo a github.com/mxcl/PromiseKit , ¡funciona muy bien con Swiftz!get
prefijo indica retorno por referencia en ObjC (como en-[UIColor getRed:green:blue:alpha:]
). Cuando escribí esto, me preocupaba que los importadores aprovecharan ese hecho (para devolver una tupla automáticamente, por ejemplo). Resultó que no lo han hecho. Cuando escribí esto, probablemente también había olvidado que KVC admite prefijos "get" para los accesos (es algo que he aprendido y olvidado varias veces). Así acordado; No me he encontrado con ningún caso en el que el líderget
rompa cosas. Es engañoso para aquellos que conocen el significado de ObjC "get".El patrón básico es utilizar el cierre de controladores de finalización.
Por ejemplo, en el próximo Swift 5, usaría
Result
:Y lo llamarías así:
Tenga en cuenta que, arriba, estoy enviando el controlador de finalización a la cola principal para simplificar las actualizaciones del modelo y la interfaz de usuario. Algunos desarrolladores hacen una excepción a esta práctica y usan cualquier cola
URLSession
utilizada o usan su propia cola (requiriendo que la persona que llama sincronice manualmente los resultados).Pero eso no es material aquí. El problema clave es el uso del controlador de finalización para especificar el bloque de código que se ejecutará cuando se realice la solicitud asincrónica.
El patrón más antiguo de Swift 4 es:
Y lo llamarías así:
Tenga en cuenta que, anteriormente, retiré el uso de
NSArray
(ya no usamos esos tipos de Objective-C puenteados ). Supongo que teníamos unGenre
tipo y presumiblemente lo usamosJSONDecoder
, en lugar deJSONSerialization
decodificarlo. Pero esta pregunta no tenía suficiente información sobre el JSON subyacente para entrar en detalles aquí, así que omití eso para evitar nublar el problema central, el uso de cierres como controladores de finalización.fuente
Result
en Swift 4 y versiones inferiores, pero debe declarar la enumeración usted mismo. Estoy usando este tipo de patrón durante años.Swift 4.0
Para solicitud-respuesta asíncrona, puede usar el controlador de finalización. Vea a continuación. He modificado la solución con el paradigma de control de finalización.
Puede llamar a esta función de la siguiente manera:
fuente
Versión Swift 3 de la respuesta de @Alexey Globchastyy:
fuente
Espero que no te quedes atascado en esto, pero la respuesta corta es que no puedes hacer esto en Swift.
Un enfoque alternativo sería devolver una devolución de llamada que proporcionará los datos que necesita tan pronto como esté listo.
fuente
callback
conclosure
s como usted señala o para usardelegation
como las API de cacao más antiguasHay 3 formas de crear funciones de devolución de llamada, a saber: 1. Controlador de finalización 2. Notificación 3. Delegados
Controlador de finalización El conjunto interior del bloque se ejecuta y se devuelve cuando la fuente está disponible, el controlador esperará hasta que llegue la respuesta para que la interfaz de usuario se pueda actualizar después.
Notificación Un montón de información se activa en toda la aplicación, Listner puede recuperar y hacer uso de esa información. Manera asincrónica de obtener información a través del proyecto.
Delegados El conjunto de métodos se activará cuando se llame al delegado, la fuente debe proporcionarse a través de los propios métodos
fuente
fuente
Hay principalmente 3 formas de lograr la devolución de llamada de forma rápida
Controlador de cierres / finalización
Delegados
Notificaciones
Los observadores también se pueden usar para recibir notificaciones una vez que se haya completado la tarea asíncrona.
fuente
Hay algunos requisitos muy genéricos que le gustaría que cumpliera todo buen administrador de API: implementará un cliente API orientado a protocolos.
Interfaz inicial de APIClient
Ahora compruebe la estructura completa de la API
fuente
Este es un pequeño caso de uso que podría ser útil: -
Mientras llama a la función: -
fuente