2025 Swift
Embracing Swift concurrency
Join us to learn the core Swift concurrency concepts. Concurrency helps you improve app responsiveness and performance, and Swift is designed to make asynchronous and concurrent code easier to write correctly. We’ll cover the steps you need to take an app through from single-threaded to concurrent. We’ll also help you determine how and when to make the best use of Swift concurrency features – whether it’s making your code more asynchronous, moving it to the background, or sharing data across concurrent tasks.
Watch at developer.apple.com ↗Chapters
- 0:00 — Introduction
- 3:17 — Single-threaded code
- 6:00 — Asynchronous tasks
- 7:24 — Interleaving
- 10:22 — Introducing concurrency
- 11:07 — Concurrent functions
- 13:10 — Nonisolated code
- 14:13 — Concurrent thread pool
- 14:58 — Sharing data
- 15:49 — Value types
- 17:16 — Actor-isolated types
- 18:30 — Classes
- 23:18 — Actors
- 26:12 — Wrap-up
Code shown on screen · 26 snippets
Single-threaded program
var greeting = "Hello, World!"
func readArguments() { }
func greet() {
print(greeting)
}
readArguments()
greet() Data types in a the app
struct Image {
}
final class ImageModel {
var imageCache: [URL: Image] = [:]
}
final class Library {
static let shared: Library = Library()
} Load and display a local image
import Foundation
class Image {
}
final class View {
func displayImage(_ image: Image) {
}
}
final class ImageModel {
var imageCache: [URL: Image] = [:]
let view = View()
func fetchAndDisplayImage(url: URL) throws {
let data = try Data(contentsOf: url)
let image = decodeImage(data)
view.displayImage(image)
}
func decodeImage(_ data: Data) -> Image {
Image()
}
}
final class Library {
static let shared: Library = Library()
} Fetch and display an image over the network
import Foundation
struct Image {
}
final class View {
func displayImage(_ image: Image) {
}
}
final class ImageModel {
var imageCache: [URL: Image] = [:]
let view = View()
func fetchAndDisplayImage(url: URL) throws {
let (data, _) = try URLSession.shared.data(from: url)
let image = decodeImage(data)
view.displayImage(image)
}
func decodeImage(_ data: Data) -> Image {
Image()
}
}
final class Library {
static let shared: Library = Library()
} Fetch and display image over the network asynchronously
import Foundation
class Image {
}
final class View {
func displayImage(_ image: Image) {
}
}
final class ImageModel {
var imageCache: [URL: Image] = [:]
let view = View()
func fetchAndDisplayImage(url: URL) async throws {
let (data, _) = try await URLSession.shared.data(from: url)
let image = decodeImage(data)
view.displayImage(image)
}
func decodeImage(_ data: Data) -> Image {
Image()
}
}
final class Library {
static let shared: Library = Library()
} Creating a task to perform asynchronous work
import Foundation
class Image {
}
final class View {
func displayImage(_ image: Image) {
}
}
final class ImageModel {
var imageCache: [URL: Image] = [:]
let view = View()
var url: URL = URL("https://swift.org")!
func onTapEvent() {
Task {
do {
try await fetchAndDisplayImage(url: url)
} catch let error {
displayError(error)
}
}
}
func displayError(_ error: any Error) {
}
func fetchAndDisplayImage(url: URL) async throws {
}
}
final class Library {
static let shared: Library = Library()
} Ordered operations in a task
import Foundation
class Image {
func applyImageEffect() async { }
}
final class ImageModel {
func displayImage(_ image: Image) {
}
func loadImage() async -> Image {
Image()
}
func onButtonTap() {
Task {
let image = await loadImage()
await image.applyImageEffect()
displayImage(image)
}
}
} Fetch and display image over the network asynchronously
import Foundation
class Image {
}
final class View {
func displayImage(_ image: Image) {
}
}
final class ImageModel {
var imageCache: [URL: Image] = [:]
let view = View()
func fetchAndDisplayImage(url: URL) async throws {
let (data, _) = try await URLSession.shared.data(from: url)
let image = decodeImage(data)
view.displayImage(image)
}
func decodeImage(_ data: Data) -> Image {
Image()
}
} Fetch and display image over the network asynchronously
import Foundation
class Image {
}
final class View {
func displayImage(_ image: Image) {
}
}
final class ImageModel {
var imageCache: [URL: Image] = [:]
let view = View()
func fetchAndDisplayImage(url: URL) async throws {
let (data, _) = try await URLSession.shared.data(from: url)
let image = decodeImage(data, at: url)
view.displayImage(image)
}
func decodeImage(_ data: Data, at url: URL) -> Image {
Image()
}
} Fetch over network asynchronously and decode concurrently
import Foundation
class Image {
}
final class View {
func displayImage(_ image: Image) {
}
}
final class ImageModel {
var imageCache: [URL: Image] = [:]
let view = View()
func fetchAndDisplayImage(url: URL) async throws {
let (data, _) = try await URLSession.shared.data(from: url)
let image = await decodeImage(data, at: url)
view.displayImage(image)
}
func decodeImage(_ data: Data, at url: URL) async -> Image {
Image()
}
} Implementation of decodeImage
final class View {
func displayImage(_ image: Image) {
}
}
final class ImageModel {
var cachedImage: [URL: Image] = [:]
let view = View()
func fetchAndDisplayImage(url: URL) async throws {
let (data, _) = try await URLSession.shared.data(from: url)
let image = await decodeImage(data, at: url)
view.displayImage(image)
}
func decodeImage(_ data: Data, at url: URL) async -> Image {
if let image = cachedImage[url] {
return image
}
// decode image
let image = Image()
cachedImage[url] = image
return image
}
} Correct implementation of fetchAndDisplayImage with caching and concurrency
import Foundation
class Image {
}
final class View {
func displayImage(_ image: Image) {
}
}
final class ImageModel {
var cachedImage: [URL: Image] = [:]
let view = View()
func fetchAndDisplayImage(url: URL) async throws {
if let image = cachedImage[url] {
view.displayImage(image)
return
}
let (data, _) = try await URLSession.shared.data(from: url)
let image = await decodeImage(data)
view.displayImage(image)
}
func decodeImage(_ data: Data) async -> Image {
// decode image
Image()
}
} JSONDecoder API should be non isolated
// Foundation
import Foundation
nonisolated
public class JSONDecoder {
public func decode<T: Decodable>(_ type: T.Type, from data: Data) -> T {
fatalError("not implemented")
}
} Fetch over network asynchronously and decode concurrently
import Foundation
class Image {
}
final class View {
func displayImage(_ image: Image) {
}
}
final class ImageModel {
var imageCache: [URL: Image] = [:]
let view = View()
func fetchAndDisplayImage(url: URL) async throws {
let (data, _) = try await URLSession.shared.data(from: url)
let image = await decodeImage(data, at: url)
view.displayImage(image)
}
func decodeImage(_ data: Data, at url: URL) async -> Image {
Image()
}
} Example of value types
// Value types are common in Swift
import Foundation
struct Post {
var author: String
var title: String
var date: Date
var categories: [String]
} Sendable value types
import Foundation
// Value types are Sendable
extension URL: Sendable {}
// Collections of Sendable elements
extension Array: Sendable where Element: Sendable {}
// Structs and enums with Sendable storage
struct ImageRequest: Sendable {
var url: URL
}
// Main-actor types are implicitly Sendable
class ImageModel {} Fetch over network asynchronously and decode concurrently
import Foundation
class Image {
}
final class View {
func displayImage(_ image: Image) {
}
}
final class ImageModel {
var imageCache: [URL: Image] = [:]
let view = View()
func fetchAndDisplayImage(url: URL) async throws {
let (data, _) = try await URLSession.shared.data(from: url)
let image = await self.decodeImage(data, at: url)
view.displayImage(image)
}
func decodeImage(_ data: Data, at url: URL) async -> Image {
Image()
}
} MyImage class with reference semantics
import Foundation
struct Color { }
nonisolated class MyImage {
var width: Int
var height: Int
var pixels: [Color]
var url: URL
init() {
width = 100
height = 100
pixels = []
url = URL("https://swift.org")!
}
func scale(by factor: Double) {
}
}
let image = MyImage()
let otherImage = image // refers to the same object as 'image'
image.scale(by: 0.5) // also changes otherImage! Concurrently scaling while displaying an image is a data race
import Foundation
struct Color { }
nonisolated class MyImage {
var width: Int
var height: Int
var pixels: [Color]
var url: URL
init() {
width = 100
height = 100
pixels = []
url = URL("https://swift.org")!
}
func scaleImage(by factor: Double) {
}
}
final class View {
func displayImage(_ image: MyImage) {
}
}
final class ImageModel {
var cachedImage: [URL: MyImage] = [:]
let view = View()
// Slide content start
func scaleAndDisplay(imageName: String) {
let image = loadImage(imageName)
Task { in
image.scaleImage(by: 0.5)
}
view.displayImage(image)
}
// Slide content end
func loadImage(_ imageName: String) -> MyImage {
// decode image
return MyImage()
}
} Scaling and then displaying an image eliminates the data race
import Foundation
struct Color { }
nonisolated class MyImage {
var width: Int
var height: Int
var pixels: [Color]
var url: URL
init() {
width = 100
height = 100
pixels = []
url = URL("https://swift.org")!
}
func scaleImage(by factor: Double) {
}
}
final class View {
func displayImage(_ image: MyImage) {
}
}
final class ImageModel {
var cachedImage: [URL: MyImage] = [:]
let view = View()
func scaleAndDisplay(imageName: String) {
Task { in
let image = loadImage(imageName)
image.scaleImage(by: 0.5)
await view.displayImage(image)
}
}
nonisolated
func loadImage(_ imageName: String) -> MyImage {
// decode image
return MyImage()
}
} Scaling and then displaying an image within a concurrent asynchronous function
import Foundation
struct Color { }
nonisolated class MyImage {
var width: Int
var height: Int
var pixels: [Color]
var url: URL
init() {
width = 100
height = 100
pixels = []
url = URL("https://swift.org")!
}
func scaleImage(by factor: Double) {
}
}
final class View {
func displayImage(_ image: MyImage) {
}
}
final class ImageModel {
var cachedImage: [URL: MyImage] = [:]
let view = View()
func scaleAndDisplay(imageName: String) async {
let image = loadImage(imageName)
image.scaleImage(by: 0.5)
await view.displayImage(image)
}
nonisolated
func loadImage(_ imageName: String) -> MyImage {
// decode image
return MyImage()
}
} Scaling, then displaying and concurrently modifying an image is a data race
import Foundation
struct Color { }
nonisolated class MyImage {
var width: Int
var height: Int
var pixels: [Color]
var url: URL
init() {
width = 100
height = 100
pixels = []
url = URL("https://swift.org")!
}
func scaleImage(by factor: Double) {
}
func applyAnotherEffect() {
}
}
final class View {
func displayImage(_ image: MyImage) {
}
}
final class ImageModel {
var cachedImage: [URL: MyImage] = [:]
let view = View()
// Slide content start
func scaleAndDisplay(imageName: String) async {
let image = loadImage(imageName)
image.scaleImage(by: 0.5)
await view.displayImage(image)
image.applyAnotherEffect()
}
// Slide content end
nonisolated
func loadImage(_ imageName: String) -> MyImage {
// decode image
return MyImage()
}
} Applying image transforms before sending to the main actor
import Foundation
struct Color { }
nonisolated class MyImage {
var width: Int
var height: Int
var pixels: [Color]
var url: URL
init() {
width = 100
height = 100
pixels = []
url = URL("https://swift.org")!
}
func scaleImage(by factor: Double) {
}
func applyAnotherEffect() {
}
}
final class View {
func displayImage(_ image: MyImage) {
}
}
final class ImageModel {
var cachedImage: [URL: MyImage] = [:]
let view = View()
// Slide content start
func scaleAndDisplay(imageName: String) async {
let image = loadImage(imageName)
image.scaleImage(by: 0.5)
image.applyAnotherEffect()
await view.displayImage(image)
}
// Slide content end
nonisolated
func loadImage(_ imageName: String) -> MyImage {
// decode image
return MyImage()
}
} Closures create shared state
import Foundation
struct Color { }
nonisolated class MyImage {
var width: Int
var height: Int
var pixels: [Color]
var url: URL
init() {
width = 100
height = 100
pixels = []
url = URL("https://swift.org")!
}
func scale(by factor: Double) {
}
func applyAnotherEffect() {
}
}
final class View {
func displayImage(_ image: MyImage) {
}
}
final class ImageModel {
var cachedImage: [URL: MyImage] = [:]
let view = View()
// Slide content start
func scaleAndDisplay(imageName: String) async throws {
let image = loadImage(imageName)
try await perform(afterDelay: 0.1) {
image.scale(by: 0.5)
}
await view.displayImage(image)
}
nonisolated
func perform(afterDelay delay: Double, body: () -> Void) async throws {
try await Task.sleep(for: .seconds(delay))
body()
}
// Slide content end
nonisolated
func loadImage(_ imageName: String) -> MyImage {
// decode image
return MyImage()
}
}pet. Network manager class
import Foundation
nonisolated class MyImage { }
struct Connection {
func data(from url: URL) async throws -> Data { Data() }
}
final class NetworkManager {
var openConnections: [URL: Connection] = [:]
func openConnection(for url: URL) async -> Connection {
if let connection = openConnections[url] {
return connection
}
let connection = Connection()
openConnections[url] = connection
return connection
}
func closeConnection(_ connection: Connection, for url: URL) async {
openConnections.removeValue(forKey: url)
}
}
final class View {
func displayImage(_ image: MyImage) {
}
}
final class ImageModel {
var cachedImage: [URL: MyImage] = [:]
let view = View()
let networkManager: NetworkManager = NetworkManager()
func fetchAndDisplayImage(url: URL) async throws {
if let image = cachedImage[url] {
view.displayImage(image)
return
}
let connection = await networkManager.openConnection(for: url)
let data = try await connection.data(from: url)
await networkManager.closeConnection(connection, for: url)
let image = await decodeImage(data)
view.displayImage(image)
}
func decodeImage(_ data: Data) async -> MyImage {
// decode image
return MyImage()
}
} Network manager as an actor
import Foundation
nonisolated class MyImage { }
struct Connection {
func data(from url: URL) async throws -> Data { Data() }
}
actor NetworkManager {
var openConnections: [URL: Connection] = [:]
func openConnection(for url: URL) async -> Connection {
if let connection = openConnections[url] {
return connection
}
let connection = Connection()
openConnections[url] = connection
return connection
}
func closeConnection(_ connection: Connection, for url: URL) async {
openConnections.removeValue(forKey: url)
}
}
final class View {
func displayImage(_ image: MyImage) {
}
}
final class ImageModel {
var cachedImage: [URL: MyImage] = [:]
let view = View()
let networkManager: NetworkManager = NetworkManager()
func fetchAndDisplayImage(url: URL) async throws {
if let image = cachedImage[url] {
view.displayImage(image)
return
}
let connection = await networkManager.openConnection(for: url)
let data = try await connection.data(from: url)
await networkManager.closeConnection(connection, for: url)
let image = await decodeImage(data)
view.displayImage(image)
}
func decodeImage(_ data: Data) async -> MyImage {
// decode image
return MyImage()
}
} Resources
Related sessions
-
33 min -
24 min