| name | swift-patterns |
| description | Core Swift language patterns, optionals, closures, protocols, generics, and modern Swift features. |
Swift Language Patterns
Modern Swift idioms for clean, safe code.
Optionals
Safe Unwrapping
func processUser(_ user: User?) {
guard let user = user else {
print("User is nil")
return
}
print(user.name)
}
if let email = user.email {
sendEmail(to: email)
}
let displayName = user.name ?? "Anonymous"
let street = user.address?.street
let email = user.email!
Optional Mapping
let uppercase = user.name?.map { $0.uppercased() }
let domain = user.email?.flatMap { $0.split(separator: "@").last }
Closures
Trailing Closures
viewModel.fetchUsers { users in
self.users = users
}
viewModel.fetchUsers { users in
self.users = users
}
viewModel.fetchUsers {
self.users = $0
}
Capture Lists
class UserViewController: UIViewController {
private let viewModel: UserViewModel
func setupRefresh() {
viewModel.onRefresh = { [weak self] in
guard let self = self else { return }
self.tableView.reloadData()
}
}
func setupTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [self] _ in
self.updateUI()
}
}
}
Higher-Order Functions
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
let evens = numbers.filter { $0 % 2 == 0 }
let sum = numbers.reduce(0) { $0 + $1 }
let names = users.compactMap { $0.name }
numbers.forEach { print($0) }
Properties
Computed Properties
struct User {
let firstName: String
let lastName: String
var fullName: String {
"\(firstName) \(lastName)"
}
var age: Int {
get { _age }
set {
guard newValue >= 0 else { return }
_age = newValue
}
}
private var _age: Int = 0
}
Property Observers
class Settings {
var theme: Theme = .light {
didSet {
saveSettings()
notifyThemeChanged()
}
}
var score: Int = 0 {
willSet {
print("Score will change from \(score) to \(newValue)")
}
didSet {
if oldValue > score {
print("Score decreased!")
}
}
}
}
Lazy Properties
struct DataProcessor {
private lazy var formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
func process(date: Date) -> String {
formatter.string(from: date)
}
}
Protocols
Protocol-Oriented Programming
protocol Repository {
associatedtype Item
func fetch(id: String) async throws -> Item
func save(_ item: Item) async throws
}
extension Repository {
func fetchAll(ids: [String]) async throws -> [Item] {
try await withThrowingTaskGroup(of: Item.self) { group in
for id in ids {
group.addTask { try await fetch(id: id) }
}
return try await group.reduce(into: [Item]()) { $0.append($1) }
}
}
}
Protocol Extensions
protocol Identifiable {
var id: String { get }
}
extension Identifiable {
func debugInfo() -> String {
"ID: \(id)"
}
}
struct User: Identifiable {
let id: String
let name: String
}
let user = User(id: "123", name: "John")
print(user.debugInfo())
Generics
Generic Functions
func first<T>(_ array: [T]) -> T? {
array.first
}
func sorted<T: Comparable>(_ array: [T]) -> [T] {
array.sorted()
}
func process<T: Sequence & Sendable>(_ items: T) where T.Element: Hashable {
}
Associated Types
protocol DataSource {
associatedtype Item
func getItems() async throws -> [Item]
}
class RemoteDataSource: DataSource {
typealias Item = User
func getItems() async throws -> [User] {
try await api.getUsers()
}
}
Result Type
func fetchUser(id: String) async -> Result<User, Error> {
do {
let user = try await api.getUser(id)
return .success(user)
} catch {
return .failure(error)
}
}
let result = await fetchUser(id: "123")
switch result {
case .success(let user):
displayUser(user)
case .failure(let error):
showError(error)
}
let name = await fetchUser(id: "123").map(\.name)
Async/Await
Structured Concurrency
func loadDashboard() async throws -> Dashboard {
async let user = getUser()
async let notifications = getNotifications()
async let stats = getStats()
return try await Dashboard(
user: user,
notifications: notifications,
stats: stats
)
}
func fetchAllImages(urls: [URL]) async throws -> [Image] {
try await withThrowingTaskGroup(of: Image.self) { group in
for url in urls {
group.addTask {
try await downloadImage(from: url)
}
}
var images: [Image] = []
for try await image in group {
images.append(image)
}
return images
}
}
MainActor
@MainActor
class HomeViewModel: Observable {
@Published var users: [User] = []
func loadUsers() async {
let users = try? await api.getUsers()
self.users = users ?? []
}
}
class NetworkService {
func fetch() async -> Data {
let data = ...
await MainActor.run {
}
return data
}
}
Enums
Associated Values
enum NetworkError: Error {
case invalidURL
case noConnection
case serverError(code: Int, message: String)
case decodingError(Error)
}
func handle(_ error: NetworkError) {
switch error {
case .invalidURL:
print("Invalid URL")
case .noConnection:
print("No connection")
case .serverError(let code, let message):
print("Server error \(code): \(message)")
case .decodingError(let error):
print("Decoding failed: \(error)")
}
}
Enum as State
enum LoadState<T> {
case idle
case loading
case loaded(T)
case error(Error)
var isLoading: Bool {
if case .loading = self { return true }
return false
}
var value: T? {
if case .loaded(let value) = self { return value }
return nil
}
}
@Published var state: LoadState<[User]> = .idle
switch state {
case .idle:
EmptyView()
case .loading:
ProgressView()
case .loaded(let users):
UserList(users: users)
case .error(let error):
ErrorView(error: error)
}
Access Control
public struct PublicAPI {
public var value: String
private init() {
value = "default"
}
private var helper: String {
"helper"
}
var internalValue: String = "internal"
fileprivate var fileOnly: String = "fileOnly"
}
Key Paths
struct User {
let name: String
let email: String
let age: Int
}
let nameKeyPath = \User.name
let users = [User(...)]
let names = users.map(\.name)
func sort<T>(_ users: [T], by keyPath: KeyPath<T, String>) -> [T] {
users.sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
}
let sorted = sort(users, by: \.name)
Remember: Swift is designed for safety. Use optionals, type inference, and value types to write robust code.