質問

多くのCocoaとCocoAtouchメソッドには、Objective-CのブロックとSwiftのクロージャとして実装された完了コールバックがあります。ただし、これらを遊び場で試してみると、完成は決して呼ばれません。例えば:

// 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を設定することです。このプロパティが設定されている場合、最上位プレイグラウンドソースが終了すると、そこでプレイグラウンドを停止するのではなく、メインランループを回転させ続けるので、非同期コードには実行する機会があります。デフォルトのタイムアウト後に遊び場を終了しますが、アシスタントエディタを開き、タイムラインアシスタントを表示すると設定できます。タイムアウトは右下にあります。

たとえば、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
.

またはSWIFT 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
.

他のヒント

このAPIはXcode 8で再び変更され、それはPlaygroundSupportに移動しました:

import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true
.

この変更は、

Xcode 7.1の時点では、XCPSetExecutionShouldContinueIndefinitely()は非推奨です。これを行う正しい方法は、最初に現在のページのプロパティとして不定実行を要求することです。

import XCPlayground

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
.

...その後、実行が終了したとき:

XCPlaygroundPage.currentPage.finishExecution()
.

例えば:

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()
.

コールバックが呼び出されていない理由は、ランループが遊び場で実行されていない(またはその問題のためにRelp Mode)からです。

幾分jankyですが、コールバックを動作させるための効果的な方法はフラグを持ち、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)
}
.

このパターンは、例えば、非同期コールバックをテストする必要がある単位テストでよく使用されています。メインキューを呼び出す非同期キューのためのパターン

Xcode 8、Swift 3、IOS 10は、

です。
// 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

ノート:

遊具ファイルが「不定実行」

を必要とするようにコンパイラに指示する

完了ハンドラ内のPlaygroundSupport.current.completeExecution()への呼び出しを介して手動で実行を終了します。

キャッシュディレクトリに問題が発生し、これを解決することができます。

例:

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()
.
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top