concurrency
Swift concurrency patterns with async/await, TaskGroup, and Actor examples
concurrency
Swift concurrency patterns with async/await, TaskGroup, and Actor examples
Details
- Language: swift
- Environment: swift5.9
- Tags: swift, concurrency, async-await, taskgroup, actor, error-handling
Build Information
- Build Success: ✅ Yes
- Last Built: 2025-08-10T07:39:21.606109-07:00
- Total Duration: 17.140432417s
Build Output
Building for debugging...
[0/6] Write sources
[0/6] Write Concurrency-entitlement.plist
[2/6] Write swift-version--58304C5D6DBC2206.txt
[4/8] Compiling Concurrency Concurrency.swift
[5/8] Emitting module Concurrency
[5/8] Write Objects.LinkFileList
[6/8] Linking Concurrency
[7/8] Applying Concurrency
Build complete! (4.19s)
Execution Output
[0/1] Planning build
Building for debugging...
[0/3] Write swift-version--58304C5D6DBC2206.txt
Build of product 'Concurrency' complete! (0.19s)
=== Task Group Basic Demo ===
Received metrics: InterfaceMetrics(name: eth0, bytesSent: 1024, timestamp: 2025-08-10 14:39:27 +0000)
Received metrics: InterfaceMetrics(name: wlan0, bytesSent: 1024, timestamp: 2025-08-10 14:39:27 +0000)
=== Context Cancellation Demo ===
Result: Stats from http://api.example.com/stats
=== Error Handling Demo ===
Validation error: Error Domain=ValidationError Code=100 "Invalid interface" UserInfo={NSLocalizedDescription=Invalid interface}
=== Real-World Monitoring Demo ===
Monitor received: InterfaceMetrics(name: eth0, bytesSent: 1024, timestamp: 2025-08-10 14:39:28 +0000)
Monitor received: InterfaceMetrics(name: wlan0, bytesSent: 1024, timestamp: 2025-08-10 14:39:28 +0000)
Monitor received: InterfaceMetrics(name: wlan0, bytesSent: 1024, timestamp: 2025-08-10 14:39:28 +0000)
Monitor received: InterfaceMetrics(name: eth0, bytesSent: 1024, timestamp: 2025-08-10 14:39:28 +0000)
Monitor received: InterfaceMetrics(name: wlan0, bytesSent: 1024, timestamp: 2025-08-10 14:39:28 +0000)
Monitor received: InterfaceMetrics(name: eth0, bytesSent: 1024, timestamp: 2025-08-10 14:39:28 +0000)
Monitor received: InterfaceMetrics(name: wlan0, bytesSent: 1024, timestamp: 2025-08-10 14:39:29 +0000)
Monitor received: InterfaceMetrics(name: eth0, bytesSent: 1024, timestamp: 2025-08-10 14:39:29 +0000)
Testing Output
[0/1] Planning build
Building for debugging...
[0/8] Write sources
[1/8] /Users/jeff/sync_studio/shared/websites/jeff_new/code/concurrency/swift/.build/arm64-apple-macosx/debug/ConcurrencyPackageTests.derived/runner.swift
[2/8] Write sources
[3/8] Write swift-version--58304C5D6DBC2206.txt
[5/8] Compiling ConcurrencyTests ConcurrencyTests.swift
[6/8] Emitting module ConcurrencyTests
[7/10] Emitting module ConcurrencyPackageTests
[8/10] Compiling ConcurrencyPackageTests runner.swift
[8/10] Write Objects.LinkFileList
[9/10] Linking ConcurrencyPackageTests
Build complete! (6.44s)
error: Exited with unexpected signal code 6
Test Suite 'All tests' started at 2025-08-10 07:39:36.556.
Test Suite 'ConcurrencyPackageTests.xctest' started at 2025-08-10 07:39:36.557.
Test Suite 'ConcurrencyPatternsTests' started at 2025-08-10 07:39:36.557.
Test Case '-[ConcurrencyTests.ConcurrencyPatternsTests testFetchNetworkStats_Failure]' started.
Test Case '-[ConcurrencyTests.ConcurrencyPatternsTests testFetchNetworkStats_Failure]' passed (1.029 seconds).
Test Case '-[ConcurrencyTests.ConcurrencyPatternsTests testFetchNetworkStats_Success]' started.
Test Case '-[ConcurrencyTests.ConcurrencyPatternsTests testFetchNetworkStats_Success]' passed (1.009 seconds).
Test Case '-[ConcurrencyTests.ConcurrencyPatternsTests testInterfaceMonitor]' started.
â—‡ Test run started.
↳ Testing Library Version: 124.4
↳ Target Platform: arm64e-apple-macos14.0
✔ Test run with 0 tests passed after 0.001 seconds.
Source Code
Makefile
build:
swift build
run:
swift run
test:
swift test
clean:
swift package clean
Package.swift
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "Concurrency",
platforms: [
.macOS(.v13)
],
targets: [
.executableTarget(
name: "Concurrency",
dependencies: []
),
.testTarget(
name: "ConcurrencyTests",
dependencies: ["Concurrency"]
),
]
)
Sources/Concurrency/Concurrency.swift
// Sources/ConcurrencyPatterns/ConcurrencyPatterns.swift
import Foundation
// Mock types for demonstration purposes
struct InterfaceMetrics: CustomStringConvertible {
let name: String
let bytesSent: Int
let timestamp: Date
var description: String {
return "InterfaceMetrics(name: \(name), bytesSent: \(bytesSent), timestamp: \(timestamp))"
}
}
struct NetworkInterface {
let name: String
}
// --- BEGIN BLOG EXAMPLE: TASK GROUP BASIC ---
// Line 25-45
func monitorInterfacesBasic() async throws {
let interfaces = [
NetworkInterface(name: "eth0"),
NetworkInterface(name: "wlan0"),
]
try await withThrowingTaskGroup(of: InterfaceMetrics.self) { group in
for interface in interfaces {
group.addTask {
// Simulate work
try await Task.sleep(for: .milliseconds(100))
return InterfaceMetrics(
name: interface.name,
bytesSent: 1024,
timestamp: Date()
)
}
}
for try await metrics in group {
print("Received metrics: \(metrics)")
}
}
}
// --- END BLOG EXAMPLE ---
// --- BEGIN BLOG EXAMPLE: ASYNC/AWAIT WITH ERROR HANDLING ---
// Line 65-90
func fetchNetworkStats(from endpoint: String) async throws -> String {
// Simulate network request with potential delay
try await Task.sleep(for: .seconds(1))
guard !endpoint.contains("error") else {
throw NSError(
domain: "NetworkError", code: 404, userInfo: [NSLocalizedDescriptionKey: "Not Found"])
}
return "Stats from \(endpoint)"
}
func contextCancellationDemo() async {
do {
let result = try await fetchNetworkStats(from: "http://api.example.com/stats")
print("Result: \(result)")
} catch {
print("Request failed: \(error)")
}
}
// --- END BLOG EXAMPLE ---
// --- BEGIN BLOG EXAMPLE: ERROR HANDLING WITH TASK GROUP ---
// Line 105-135
func validateInterfaces(_ interfaces: [String]) async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
for interface in interfaces {
group.addTask {
// Simulate validation
try await Task.sleep(for: .milliseconds(50))
if interface == "bad0" {
throw NSError(
domain: "ValidationError", code: 100,
userInfo: [NSLocalizedDescriptionKey: "Invalid interface"])
}
}
}
// Fail fast on first error
try await group.waitForAll()
}
}
func errorHandlingDemo() async {
let interfaces = ["eth0", "wlan0", "bad0", "lo"]
do {
try await validateInterfaces(interfaces)
print("All interfaces validated successfully")
} catch {
print("Validation error: \(error)")
}
}
// --- END BLOG EXAMPLE ---
// --- BEGIN BLOG EXAMPLE: REAL-WORLD MONITORING STRUCTURE ---
// Line 150-200
actor InterfaceMonitor {
private var monitoringTask: Task<Void, Never>?
private let metricsHandler: (InterfaceMetrics) async -> Void
init(metricsHandler: @escaping (InterfaceMetrics) async -> Void) {
self.metricsHandler = metricsHandler
}
func start() {
monitoringTask = Task { [weak self] in
await withTaskGroup(of: Void.self) { group in
let interfaces = [
NetworkInterface(name: "eth0"),
NetworkInterface(name: "wlan0"),
]
for interface in interfaces {
group.addTask { [interface, weak self] in
await self?.monitorInterface(interface.name)
}
}
}
}
}
private func monitorInterface(_ name: String) async {
while !Task.isCancelled {
do {
// Simulate metric collection
try await Task.sleep(for: .milliseconds(100))
// Check cancellation after sleep
if Task.isCancelled { break }
let metrics = InterfaceMetrics(
name: name,
bytesSent: 1024,
timestamp: Date()
)
await metricsHandler(metrics)
} catch is CancellationError {
break
} catch {
print("Error collecting metrics for \(name): \(error)")
try? await Task.sleep(for: .milliseconds(100))
}
}
}
func stop() async {
monitoringTask?.cancel()
monitoringTask = nil
}
}
func realWorldDemo() async {
var receivedMetrics: [InterfaceMetrics] = []
let monitor = InterfaceMonitor { metrics in
receivedMetrics.append(metrics)
print("Monitor received: \(metrics)")
}
await monitor.start()
// Let it run for a bit
try? await Task.sleep(for: .milliseconds(500))
await monitor.stop()
// Give tasks time to respond to cancellation before function exits
try? await Task.sleep(for: .milliseconds(100))
}
// --- END BLOG EXAMPLE ---
// MARK: - Main Execution
@main
struct ConcurrencyPatternsDemo {
static func main() async {
print("=== Task Group Basic Demo ===")
try? await monitorInterfacesBasic()
print("\n=== Context Cancellation Demo ===")
await contextCancellationDemo()
print("\n=== Error Handling Demo ===")
await errorHandlingDemo()
print("\n=== Real-World Monitoring Demo ===")
await realWorldDemo()
}
}
Tests/ConcurrencyTests/ConcurrencyTests.swift
// Tests/ConcurrencyPatternsTests/ConcurrencyPatternsTests.swift
import XCTest
@testable import Concurrency
final class ConcurrencyPatternsTests: XCTestCase {
// --- BEGIN BLOG EXAMPLE: TASK GROUP TEST ---
// Line 15-35
func testMonitorInterfacesBasic() async throws {
let expectation = XCTestExpectation(description: "Should complete monitoring")
Task {
try? await monitorInterfacesBasic()
expectation.fulfill()
}
await fulfillment(of: [expectation], timeout: 1.0)
}
// --- END BLOG EXAMPLE ---
// --- BEGIN BLOG EXAMPLE: ASYNC/AWAIT TEST ---
// Line 50-70
func testFetchNetworkStats_Success() async throws {
let result = try await fetchNetworkStats(from: "http://api.example.com/stats")
XCTAssertTrue(result.contains("Stats from"))
}
func testFetchNetworkStats_Failure() async {
do {
_ = try await fetchNetworkStats(from: "http://api.example.com/error")
XCTFail("Expected error but got success")
} catch {
// Success - we expected an error
}
}
// --- END BLOG EXAMPLE ---
// --- BEGIN BLOG EXAMPLE: ERROR HANDLING TEST ---
// Line 85-105
func testValidateInterfaces_Success() async throws {
let interfaces = ["eth0", "wlan0", "lo"]
try await validateInterfaces(interfaces)
// If we get here without error, test passes
}
func testValidateInterfaces_Failure() async {
let interfaces = ["eth0", "wlan0", "bad0", "lo"]
do {
try await validateInterfaces(interfaces)
XCTFail("Expected validation error")
} catch {
// Success - we expected an error
}
}
// --- END BLOG EXAMPLE ---
// --- BEGIN BLOG EXAMPLE: REAL-WORLD MONITOR TEST ---
// Line 120-145
func testInterfaceMonitor() async throws {
var receivedMetrics: [InterfaceMetrics] = []
let expectation = XCTestExpectation(description: "Should receive metrics")
let monitor = InterfaceMonitor { metrics in
receivedMetrics.append(metrics)
if receivedMetrics.count >= 2 {
expectation.fulfill()
}
}
await monitor.start()
// Wait for metrics to be collected
await fulfillment(of: [expectation], timeout: 1.0)
// Clean up
await monitor.stop()
// Verify we received metrics
XCTAssertTrue(
receivedMetrics.count >= 2, "Expected at least 2 metrics, got \(receivedMetrics.count)")
}
// --- END BLOG EXAMPLE ---
}
autodoc.yaml
title: concurrency
description: Swift concurrency patterns with async/await, TaskGroup, and Actor examples
tags:
- swift
- concurrency
- async-await
- taskgroup
- actor
- error-handling
environment: swift5.9
created: 2025-08-09T17:30:40.609919-07:00
author: Jeff Shumate