iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🐷

How to Wait for Asynchronous Tasks to Complete in Swift Playgrounds

に公開

I use the macOS version of Swift Playgrounds when I want to write some quick Swift code.
https://www.apple.com/jp/swift/playgrounds/

While it is reasonably easy to use, I was having trouble because the program would terminate before asynchronous processes could complete.

For example, if you run the following code using URLSession to fetch the number of GitHub repositories, the program ends after only displaying the final message:

<code>code completed.</code>

import Combine
import Foundation

struct SearchResponse: Decodable {
    var totalCount: Int
}

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

let url = URL(string: "https://api.github.com/search/repositories?q=swift")!
var cancellable = URLSession.shared.dataTaskPublisher(for: url)
    .tryMap() { element -> Data in
        guard let httpResponse = element.response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw URLError(.badServerResponse)
        }
        return element.data
    }
    .decode(type: SearchResponse.self, decoder: decoder)
    .sink(receiveCompletion: { print ("Received completion: \($0).") },
          receiveValue: { repositories in print ("Received: \(repositories.totalCount).")})

print("code completed.")

Upon investigation, I found that setting needsIndefiniteExecution of PlaygroundPage to true prevents the program from terminating and allows it to run indefinitely, effectively enabling asynchronous processing to complete.

https://developer.apple.com/documentation/playgroundsupport/playgroundpage/1964501-needsindefiniteexecution

Specifically, add the following code to Playgrounds:

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

Taking the previous code as an example, add the above code to the beginning and run it.

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

import Combine
import Foundation

struct SearchResponse: Decodable {
    var totalCount: Int
}

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

let url = URL(string: "https://api.github.com/search/repositories?q=swift")!
var cancellable = URLSession.shared.dataTaskPublisher(for: url)
    .tryMap() { element -> Data in
        guard let httpResponse = element.response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw URLError(.badServerResponse)
        }
        return element.data
    }
    .decode(type: SearchResponse.self, decoder: decoder)
    .sink(receiveCompletion: { print ("Received completion: \($0).") },
          receiveValue: { repositories in print ("Received: \(repositories.totalCount).")})

print("code completed.")

This time, the output was as follows, showing the results of the asynchronous process:

code completed.
Received: 209193.
Received completion: finished.

In this state, the program will not terminate until you press the "Stop" button or edit the code in the editor. To terminate it explicitly, execute finishExecution after the process is complete.
https://developer.apple.com/documentation/playgroundsupport/playgroundpage/1964505-finishexecution

Additionally, the environments where PlaygroundPage is available are as follows:

macOS 11.0+
Xcode 10.2+
Swift Playgrounds 2.0+

https://developer.apple.com/documentation/playgroundsupport/playgroundpage

Discussion