Playground에서 비동기 콜백을 실행하는 방법
많은 Cocoa 및 CocoaTouch 메소드에는 Objective-C의 블록 및 Swift의 Closures로 완료 콜백이 구현되어 있습니다. 그러나 Playground에서 이러한 작업을 시도 할 때 완료가 호출되지 않습니다. 예를 들면 다음과 같습니다.
// Playground - noun: a place where people can play
import Cocoa
import XCPlayground
let 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?
if let data = maybeData {
let contents = NSString(data:data, encoding:NSUTF8StringEncoding)
println(contents)
} else {
println(error.localizedDescription)
}
}
내 놀이터 타임 라인에서 콘솔 출력을 볼 수 있지만 println
완료 블록 의 콘솔 출력은 호출되지 않습니다 ...
런 루프를 수동으로 (또는 런 루프가 필요없는 비동기 코드의 경우 디스패치 세마포어와 같은 다른 대기 방법 사용) 수동으로 실행할 수 있지만 놀이터에서 비동기 작업을 대기하기 위해 제공하는 "내장"방법은 다음과 같습니다. XCPlayground
프레임 워크를 가져 와서 설정 XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
합니다. 이 속성을 설정 한 경우 최상위 수준 놀이터 소스가 완료되면 놀이터를 중지하는 대신 기본 실행 루프를 계속 돌리므로 비동기 코드가 실행될 수 있습니다. 타임 아웃 후 기본적으로 놀이터는 30 초로 종료되지만, 보조 편집기를 열고 타임 라인 보조를 표시하면 구성 할 수 있습니다. 타임 아웃은 오른쪽 아래에 있습니다.
예를 들어 Swift 3에서 ( URLSession
대신 사용 NSURLConnection
) :
import UIKit
import PlaygroundSupport
let url = URL(string: "http://stackoverflow.com")!
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print(error ?? "Unknown error")
return
}
let contents = String(data: data, encoding: .utf8)
print(contents!)
}.resume()
PlaygroundPage.current.needsIndefiniteExecution = true
또는 스위프트 2에서 :
import UIKit
import XCPlayground
let url = NSURL(string: "http://stackoverflow.com")
let request = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.currentQueue()) { response, maybeData, error in
if let data = maybeData {
let contents = NSString(data:data, encoding:NSUTF8StringEncoding)
println(contents)
} else {
println(error.localizedDescription)
}
}
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
This API changed again in Xcode 8 and it was moved to the PlaygroundSupport
:
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
This change was mentioned in Session 213 at WWDC 2016.
As of XCode 7.1, XCPSetExecutionShouldContinueIndefinitely()
is deprecated. The correct way to do this now is to first request indefinite execution as a property of the current page:
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
…then indicate when execution has finished with:
XCPlaygroundPage.currentPage.finishExecution()
For example:
import Foundation
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://stackoverflow.com")!) {
result in
print("Got result: \(result)")
XCPlaygroundPage.currentPage.finishExecution()
}.resume()
The reason the callbacks are not called is because the RunLoop isn't running in Playground (or in REPL mode for that matter).
A somewhat janky, but effective, way to make the callbacks operate is with a flag and then manually iterating on the runloop:
// Playground - noun: a place where people can play
import Cocoa
import XCPlayground
let url = NSURL(string: "http://stackoverflow.com")
let request = NSURLRequest(URL: url)
var waiting = true
NSURLConnection.sendAsynchronousRequest(request, queue:NSOperationQueue.currentQueue() {
response, maybeData, error in
waiting = false
if let 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)
}
This pattern has often been used in Unit Tests which need to test async callbacks, for example: Pattern for unit testing async queue that calls main queue on completion
The new APIs as for XCode8, Swift3 and iOS 10 are,
// import the module
import PlaygroundSupport
// write this at the beginning
PlaygroundPage.current.needsIndefiniteExecution = true
// To finish execution
PlaygroundPage.current.finishExecution()
Swift 4, Xcode 9.0
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard error == nil else {
print(error?.localizedDescription ?? "")
return
}
if let data = data, let contents = String(data: data, encoding: String.Encoding.utf8) {
print(contents)
}
}
task.resume()
Swift 3, xcode 8, iOS 10
Notes:
Tell the compiler that the playground file requires "indefinite execution"
Manually terminate execution via a call to PlaygroundSupport.current.completeExecution()
within your completion handler.
You may run into problems with the cache directory and to resolve this you will need to manually re-instantiate the UICache.shared singleton.
Example:
import UIKit
import Foundation
import PlaygroundSupport
// resolve path errors
URLCache.shared = URLCache(memoryCapacity: 0, diskCapacity: 0, diskPath: nil)
// identify that the current page requires "indefinite execution"
PlaygroundPage.current.needsIndefiniteExecution = true
// encapsulate execution completion
func completeExecution() {
PlaygroundPage.current.finishExecution()
}
let url = URL(string: "http://i.imgur.com/aWkpX3W.png")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
var image = UIImage(data: data!)
// complete execution
completeExecution()
}
task.resume()
NSURLConnection.sendAsynchronousRequest(...)
NSRunLoop.currentRunLoop().run()
참고URL : https://stackoverflow.com/questions/24058336/how-do-i-run-asynchronous-callbacks-in-playground
'Programing' 카테고리의 다른 글
그룹의 어떤 라디오 버튼이 점검됩니까? (0) | 2020.07.21 |
---|---|
$ _REQUEST []를 사용하는 데 무엇이 문제입니까? (0) | 2020.07.21 |
'for'루프를 사용하여 C ++ 벡터를 반복 (0) | 2020.07.21 |
방랑자 역 포트 포워딩? (0) | 2020.07.21 |
SQL 테이블에서 레코드를 어떻게 복사하지만 새 행의 고유 ID를 교체합니까? (0) | 2020.07.21 |