Swift Concurrency
Swift Concurrency by Example
Introduction
Concurrency in top-level code
玩了分手厨房,我理解了协程是什么
通过 Swift 的 async await 语法糖进行 URLSession
import Foundation
let apiURL = "https://my.api.mockaroo.com/movie_ticket.json?key=8795e870"
struct MovieTicket: Codable {
let movieTitle, movieGenre: String
let movieTicketPrice: Float
let movieDuration: Int
}
enum TicketError: Error {
case urlError
case invalidServerResponse
}
DataTask, closure
How to create continuations that can throw errors
func getDataFromClosure(completionHandler: @escaping (Result<[MovieTicket], Error>) -> Void) {
guard let url = URL(string: apiURL) else {
completionHandler(.failure(TicketError.urlError))
return
}
URLSession.shared.dataTask(with: URLRequest(url: url)) { data, response, error in
if let error = error {
completionHandler(.failure(error))
}
guard let response = response as? HTTPURLResponse,
response.statusCode == 200,
let data = data
else {
completionHandler(.failure(TicketError.invalidServerResponse))
return
}
do {
let tickets = try JSONDecoder().decode([MovieTicket].self, from: data)
completionHandler(.success(tickets))
} catch {
completionHandler(.failure(error))
}
}.resume()
}
getDataFromClosure { result in
switch result {
case .success(let tickets):
for (index, ticket) in tickets.enumerated() {
print("\(index): \(ticket.movieTitle) [genre] - \(ticket.movieGenre)")
}
case .failure(let failure):
print(failure.localizedDescription)
}
exit(0)
}
// https://developer.apple.com/forums/thread/713085
// The standard workaround is to add a call to dispatchMain() which ‘parks’ the main thread,
// preventing the tool from exiting. If you then want to exit after your network request is complete, call exit(_:) directly.
dispatchMain()
Concurrency
func getDataFromConcurrency() async throws -> [MovieTicket] {
guard let url = URL(string: apiURL) else { throw TicketError.urlError }
let (data, response) = try await URLSession.shared.data(from: url)
guard let response = response as? HTTPURLResponse,
response.statusCode == 200
else { throw TicketError.invalidServerResponse }
let tickets = try JSONDecoder().decode([MovieTicket].self, from: data)
return tickets
}
do {
let tickets = try await getDataFromConcurrency()
for (index, ticket) in tickets.enumerated() {
print("\(index): \(ticket.movieTitle) [genre] - \(ticket.movieGenre)")
}
} catch {
print(error.localizedDescription)
}
Async/await
Sequences and streams
Tasks and task groups
Actors
How to use @MainActor to run code on the main queue
Error: Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
Solutions