Swift Concurrency (await/async)
[Concurrency vs Parallelism]
[Sync vs Async]
Official doc
Swift has built-in support for writing asynchronous and parallel code in a structured way.
It is a kind of from Swift v5.5 iOS v15 and v13 backward compatibility
solves:
- callback hell
- error handling(using native
try/catch mechanism)
- natural, straight-line code which is simple to read, write and support
High-level structure:
await/async markers for recognising async block and run it. You are able to use withCheckedThrowingContinuation and withCheckedContinuation inside a callback to support await/async
- await async func
- await async let
- await
Actor)
Task - create a concurrent environment in which await/async is allowed, it is stared immediately after creation, can be prioritized duting creation and can be canceled, has it's own lifetime as a result - current state. Task.init(priority:operation:) creates unstructured concurrency(doesn't have a parent task)
Task Group - allows to run parallel(more dynamic than await async let) tasks and all off them done - task group is finished. When you use TaskGroup.addTask or await async let creates Structured concurrency because it has explicit parent-child relationship between task and taskGroup
- [
Actor] - share mutable referance_type data between tasks in a concurrent environment which prevents Data race[About]
Example:
func runImageTask() {
let imageSaver = ImageSaver()
Task.init {
do {
let url = URL(string: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_dark_color_272x92dp.png")!
guard let image = try await self.downloadImage(from: url) else { return }
try await imageSaver.storeImage(image)
guard let transformedImage = try await self.transformImage(image) else { return }
try await imageSaver.storeImage(transformedImage)
self.showImage(transformedImage)
} catch {
print("error:\(error)")
}
}
}
func downloadImage(from url: URL) async throws -> UIImage? {
let (data, response) = try await URLSession.shared.data(from: url)
return UIImage(data: data)
}
func transformImage(_ image: UIImage) async throws -> UIImage? {
return try await withCheckedThrowingContinuation { continuation in
DispatchQueue.global().async {
UIGraphicsBeginImageContext(image.size)
image.draw(at: CGPoint.zero)
guard let context = UIGraphicsGetCurrentContext() else {
continuation.resume(with: .failure(NSError(domain: "context is nil", code: 1)))
return
}
context.setStrokeColor(UIColor.green.cgColor)
context.setLineWidth(5)
context.addEllipse(in: CGRect(x: 50, y: 50, width: 50, height: 50))
context.drawPath(using: .stroke)
let transformedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
continuation.resume(with: .success(transformedImage))
}
}
}
func showImage(_ image: UIImage) {
self.imageView.image = image
}
class ImageSaver: NSObject {
var continuation: CheckedContinuation<Void, Error>? = nil
func storeImage(_ image: UIImage) async throws {
return try await withCheckedThrowingContinuation { continuation in
self.continuation = continuation
UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveCompleted), nil)
}
}
@objc func saveCompleted(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
let result: Result<(), Error>
if let error = error {
result = .failure(error)
} else {
result = .success(())
}
self.continuation?.resume(with: result)
}
}
[Kotlin Coroutine]