¿Cómo ejecuto devoluciones de llamada asincrónicas en Playground?
117
Muchos métodos Cocoa y CocoaTouch tienen devoluciones de llamada de finalización implementadas como bloques en Objective-C y Closures en Swift. Sin embargo, al probarlos en Playground, nunca se llama a la finalización. Por ejemplo:
// Playground - noun: a place where people can play
importCocoaimportXCPlaygroundlet url = NSURL(string:"http://stackoverflow.com")let request =NSURLRequest(URL: url)NSURLConnection.sendAsynchronousRequest(request, queue:NSOperationQueue.currentQueue(){
response, maybeData, error in// This block never gets called?
iflet data = maybeData {let contents =NSString(data:data, encoding:NSUTF8StringEncoding)
println(contents)}else{
println(error.localizedDescription)}}
Puedo ver la salida de la consola en mi línea de tiempo de Playground, pero printlnnunca se llaman en mi bloque de finalización ...
Si bien puede ejecutar un ciclo de ejecución manualmente (o, para el código asincrónico que no requiere un ciclo de ejecución, use otros métodos de espera como semáforos de envío), la forma "incorporada" que proporcionamos en los patios de recreo para esperar el trabajo asincrónico es importar el XCPlaygroundmarco y configurarXCPlaygroundPage.currentPage.needsIndefiniteExecution = true . Si se ha establecido esta propiedad, cuando finalice la fuente del patio de recreo de nivel superior, en lugar de detener el patio de recreo allí, continuaremos girando el ciclo de ejecución principal, por lo que el código asincrónico tiene la oportunidad de ejecutarse. Eventualmente terminaremos el patio de recreo después de un tiempo de espera predeterminado de 30 segundos, pero que se puede configurar si abre el editor asistente y muestra el asistente de línea de tiempo; el tiempo de espera está en la parte inferior derecha.
Por ejemplo, en Swift 3 (usando URLSession lugar de NSURLConnection):
No olvide llamar PlaygroundPage.current.finishExecution().
Glenn
36
A partir de XCode 7.1, XCPSetExecutionShouldContinueIndefinitely()está obsoleto. La forma correcta de hacer esto ahora es solicitar primero la ejecución indefinida como una propiedad de la página actual:
… Luego indique cuando la ejecución ha terminado con:
XCPlaygroundPage.currentPage.finishExecution()
Por ejemplo:
importFoundationimportXCPlaygroundXCPlaygroundPage.currentPage.needsIndefiniteExecution =trueNSURLSession.sharedSession().dataTaskWithURL(NSURL(string:"http://stackoverflow.com")!){
result in
print("Got result: \(result)")XCPlaygroundPage.currentPage.finishExecution()}.resume()
La razón por la que no se llaman las devoluciones de llamada es porque RunLoop no se está ejecutando en Playground (o en modo REPL para el caso).
Una forma algo burda, pero efectiva, de hacer que las devoluciones de llamada operen es con una bandera y luego iterando manualmente en el runloop:
// Playground - noun: a place where people can play
importCocoaimportXCPlaygroundlet url = NSURL(string:"http://stackoverflow.com")let request =NSURLRequest(URL: url)var waiting =trueNSURLConnection.sendAsynchronousRequest(request, queue:NSOperationQueue.currentQueue(){
response, maybeData, error in
waiting =falseiflet data = maybeData {let contents =NSString(data:data, encoding:NSUTF8StringEncoding)
println(contents)}else{
println(error.localizedDescription)}}while(waiting){NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate:NSDate())
usleep(10)}
Las nuevas API como para XCode8, Swift3 e iOS 10 son,
// import the module
importPlaygroundSupport// write this at the beginning
PlaygroundPage.current.needsIndefiniteExecution =true// To finish execution
PlaygroundPage.current.finishExecution()
XCPlayground
marco ahora también está disponible para iOS Playgrounds.XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
import PlaygroundSupport
yPlaygroundPage.current.needsIndefiniteExecution = true
Esta API cambió nuevamente en Xcode 8 y se movió a
PlaygroundSupport
:Este cambio se mencionó en la Sesión 213 en WWDC 2016 .
fuente
PlaygroundPage.current.finishExecution()
.A partir de XCode 7.1,
XCPSetExecutionShouldContinueIndefinitely()
está obsoleto. La forma correcta de hacer esto ahora es solicitar primero la ejecución indefinida como una propiedad de la página actual:… Luego indique cuando la ejecución ha terminado con:
Por ejemplo:
fuente
La razón por la que no se llaman las devoluciones de llamada es porque RunLoop no se está ejecutando en Playground (o en modo REPL para el caso).
Una forma algo burda, pero efectiva, de hacer que las devoluciones de llamada operen es con una bandera y luego iterando manualmente en el runloop:
Este patrón se ha utilizado a menudo en pruebas unitarias que necesitan probar devoluciones de llamada asíncronas, por ejemplo: Patrón para la cola asíncrona de prueba unitaria que llama a la cola principal al finalizar
fuente
Las nuevas API como para XCode8, Swift3 e iOS 10 son,
fuente
Swift 4, Xcode 9.0
fuente
Swift 3, xcode 8, iOS 10
Notas:
Dígale al compilador que el archivo de la zona de juegos requiere "ejecución indefinida"
Finalice manualmente la ejecución mediante una llamada a
PlaygroundSupport.current.completeExecution()
su controlador de finalización.Puede tener problemas con el directorio de caché y, para resolverlo, deberá volver a crear una instancia manual del singleton UICache.shared.
Ejemplo:
fuente
fuente