Swift
Modern Swift development with protocol-oriented programming and async/await.
When to Use
- Apple Ecosystem Native Applications: iOS, iPadOS, macOS, watchOS, and visionOS applications using SwiftUI.
- Modern Concurrent Systems: Utilizing Swift 6 complete concurrency checking, actors, and structured
Tasktrees. - Cross-Platform Server-Side Swift: High-performance backends and microservices built with Hummingbird or Vapor.
- Embedded & Real-Time Edge Processing: Utilizing Embedded Swift for microcontrollers and real-time audio engines.
Quick Start
struct User: Identifiable, Codable {
let id: UUID
var name: String
var email: String
var displayName: String {
name.capitalized
}
}
Core Concepts
Modern Swift 6 Concurrency: Actors & Structured Tasks
Data-race safety guaranteed at compile time with Sendable enforcement and actors:
import Foundation
// Thread-safe state isolation via actor
actor AccountManager {
private var balances: [UUID: Decimal] = [:]
func deposit(to accountId: UUID, amount: Decimal) {
guard amount > 0 else { return }
balances[accountId, default: 0] += amount
}
func balance(for accountId: UUID) -> Decimal {
balances[accountId, default: 0]
}
}
// Structured concurrency with TaskGroup
func fetchUserMetrics(userIds: [UUID]) async throws -> [UUID: Decimal] {
let manager = AccountManager()
return try await withThrowingTaskGroup(of: (UUID, Decimal).self) { group in
for id in userIds {
group.addTask {
let bal = await manager.balance(for: id)
return (id, bal)
}
}
var results: [UUID: Decimal] = [:]
for try await (id, bal) in group {
results[id] = bal
}
return results
}
}
Protocol-Oriented Architecture & Primary Associated Types
Expressive type contracts using Swift 5.7+ some and any semantics:
// Protocol with primary associated type
protocol Repository<Entity>: Sendable {
associatedtype Entity: Identifiable, Sendable
func fetch(by id: Entity.ID) async throws -> Entity?
func save(_ entity: Entity) async throws
}
struct User: Identifiable, Sendable {
let id: UUID
let name: String
}
final class InMemoryUserRepository: Repository {
private var store: [UUID: User] = [:]
func fetch(by id: UUID) async throws -> User? {
store[id]
}
func save(_ entity: User) async throws {
store[entity.id] = entity
}
}
// Opaque return type using 'some' for static dispatch performance
func makeDefaultRepo() -> some Repository<User> {
InMemoryUserRepository()
}
Result Builders & Custom DSLs
Constructing declarative hierarchies modeled after SwiftUI:
@resultBuilder
struct HTMLBuilder {
static func buildBlock(_ components: String...) -> String {
components.joined(separator: "
")
}
static func buildOptional(_ component: String?) -> String {
component ?? ""
}
}
func htmlDoc(@HTMLBuilder content: () -> String) -> String {
"<html>
<body>
\(content())
</body>
</html>"
}
let page = htmlDoc {
"<h1>Welcome to Swift 6</h1>"
"<p>Safe, fast, and expressive systems programming.</p>"
}
Common Patterns
Modern Async/Await Network Request
Problem: Deeply nested completion handler callbacks and error propagation bugs in asynchronous code.
Solution:
func fetchUser(id: UUID) async throws -> User {
let url = URL(string: "https://api.example.com/users/\(id)")!
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw NetworkError.invalidResponse
}
return try JSONDecoder().decode(User.self, from: data)
}
// Parallel execution
async let user = fetchUser(id: userId)
async let orders = fetchOrders(userId: userId)
let (userResult, ordersResult) = try await (user, orders)
Actors
Problem: Preventing concurrent data race bugs when mutating shared mutable state across asynchronous tasks.
Solution:
actor UserCache {
private var cache: [UUID: User] = [:]
func get(_ id: UUID) -> User? { cache[id] }
func set(_ user: User) { cache[user.id] = user }
}
Best Practices
Do:
- Enable Swift 6 complete concurrency checking (
-strict-concurrency=complete) to catch data races at compile time. - Prefer
structand value types overclassunless reference identity or Objective-C runtime bridging is explicitly required. - Favor
some Protocol(opaque types) overany Protocol(existential types) to eliminate dynamic dispatch overhead. - Use
async/awaitand structuredwithTaskGrouprather than legacy completion handlers and Grand Central Dispatch (DispatchQueue).
Don't:
- Force unwrap optionals (
!) in production; useguard let,if let, or default coalescing (??). - Capture strong
selfreferences in escaping closures; use[weak self]to avoid retain cycles. - Bypass actor isolation with
@unchecked Sendablewithout verifying thread-safety invariants.
Troubleshooting
| Error | Cause | Solution |
| ------------------------- | ------------------------------------ | -------------------- |
| unexpectedly found nil | Force unwrap of nil | Use optional binding |
| Actor-isolated property | Accessing actor from sync context | Use await |
| Sendable closure | Non-sendable type across concurrency | Make type Sendable |
微信扫一扫