🦅 Swift Cheat Sheet
Modern Swift — optionals, protocols, generics, Combine, async/await.
📦 Types & Variables
Variable declarations
var name = "Swift" // mutable let pi = 3.14159 // immutable var age: Int = 25 let city: String = "Bangkok" var score: Double = 9.5 var isActive: Bool = true
String interpolation & multi-line
let greeting = "Hello, \(name)!" let calc = "Result: \(2 + 2)" let multi = """ Multi-line string """
Enum
enum Direction { case north, south, east, west }
// Raw values
enum Status: String {
case active = "active"
case inactive = "inactive"
var isActive: Bool { self == .active }
}
// Associated values
enum Result<T> {
case success(T)
case failure(Error)
}
let result: Result<User> = .success(user)
switch result {
case .success(let u): print(u.name)
case .failure(let e): print(e)
}Type casting
let n = value as? Int // optional cast (nil if fails)
let n2 = value as! Int // force cast (crash if fails)
if value is String { ... } // type check❓ Optionals
Optional basics
var name: String? = nil
name = "Feem"
// Unwrapping
if let n = name { print(n) } // if let
guard let n = name else { return } // guard let — early exit
let n = name! // force unwrap (avoid!)
let n = name ?? "default" // nil-coalescingOptional chaining
let city = user?.address?.city // String?
user?.updateProfile() // call method safely
// Multiple bindings
if let name = user?.name, let age = user?.age, age >= 18 {
print("Adult: \(name)")
}Optional pattern in switch
switch optionalValue {
case .some(let v): print(v)
case .none: print("nil")
}
// or
switch optionalValue {
case let v?: print(v)
case nil: print("nil")
}📚 Collections
Array
var list: [String] = ["a", "b", "c"]
list.append("d")
list.insert("x", at: 0)
list.remove(at: 1)
list.count; list.isEmpty
list.first; list.last
list.contains("a")
list.sorted()
list.reversed()
// Functional
list.map { $0.uppercased() }
list.filter { $0.count > 1 }
list.reduce("") { $0 + $1 }
list.compactMap { Int($0) } // map + remove nil
list.flatMap { [$0, $0] } // flatten
list.first { $0.hasPrefix("a") }
list.enumerated().forEach { i, v in print(i, v) }Dictionary
var dict: [String: Int] = ["a": 1, "b": 2]
dict["c"] = 3
dict.removeValue(forKey: "a")
dict["missing"] // Optional (Int?)
dict["missing", default: 0] // non-optional with default
dict.keys.sorted()
dict.values.map { $0 * 2 }
dict.filter { $1 > 1 }
dict.mapValues { $0 * 10 }Set
var set: Set<Int> = [1, 2, 3, 3] // {1, 2, 3}
set.insert(4)
set.remove(2)
set.contains(3)
set.union([4, 5])
set.intersection([2, 3, 4])🔧 Functions
Function syntax
// Basic
func add(_ a: Int, _ b: Int) -> Int { a + b }
// Argument labels
func greet(name: String, age: Int = 0) { print("Hi \(name)") }
greet(name: "Feem", age: 30)
// Variadic
func sum(_ nums: Int...) -> Int { nums.reduce(0, +) }
// inout
func double(_ n: inout Int) { n *= 2 }
double(&myNum)
// Multiple returns via tuple
func minMax(_ arr: [Int]) -> (min: Int, max: Int) {
(arr.min()!, arr.max()!)
}
let result = minMax([1,2,3])
print(result.min, result.max)@discardableResult & throws
enum NetworkError: Error {
case notFound
case unauthorized(String)
}
func fetchData() throws -> Data {
guard isConnected else { throw NetworkError.notFound }
return Data()
}
// Call with do-catch
do {
let data = try fetchData()
} catch NetworkError.notFound {
print("Not found")
} catch NetworkError.unauthorized(let msg) {
print("Auth error: \(msg)")
} catch {
print("Unknown: \(error)")
}
// try? returns optional
let data = try? fetchData()
// try! force (crash on error)
let data2 = try! fetchData()🔒 Closures
Closure syntax
// Full syntax
let multiply: (Int, Int) -> Int = { (a, b) in return a * b }
// Shorthand
let multiply2: (Int, Int) -> Int = { $0 * $1 }
// Trailing closure
[1,2,3].sorted { $0 > $1 }
// Escaping closure (stored / async)
func fetchUser(completion: @escaping (User?) -> Void) {
DispatchQueue.global().async {
let user = User(name: "Feem")
DispatchQueue.main.async { completion(user) }
}
}
// Capture list (avoid retain cycles)
button.action = { [weak self] in
guard let self else { return }
self.handleTap()
}🏗️ OOP
Struct vs Class
// Struct — value type (prefer for data)
struct Point {
var x: Double
var y: Double
// Mutating method
mutating func move(dx: Double, dy: Double) {
x += dx; y += dy
}
}
// Class — reference type (use for shared mutable state)
class ViewModel: ObservableObject {
var items: [Item] = []
deinit { print("deallocated") } // cleanup
}Inheritance
class Animal {
var name: String
init(name: String) { self.name = name }
func speak() { print("\(name) makes a sound") }
}
class Dog: Animal {
override func speak() {
super.speak()
print("\(name) barks")
}
}
// Prevent subclassing
final class Singleton {
static let shared = Singleton()
private init() {}
}Computed properties & observers
struct Circle {
var radius: Double
var area: Double { .pi * radius * radius } // computed (read-only)
var diameter: Double { // computed (read-write)
get { radius * 2 }
set { radius = newValue / 2 }
}
}
class Model {
var count: Int = 0 {
willSet { print("will change to \(newValue)") }
didSet { print("changed from \(oldValue)") }
}
}📋 Protocols
Protocol basics
protocol Drawable {
var color: String { get }
func draw()
func resize(by factor: Double) mutating
}
// Default implementation (extension)
extension Drawable {
func draw() { print("Drawing in \(color)") }
}
// Protocol composition
typealias DrawableIdentifiable = Drawable & Identifiable
func render(_ item: some Drawable) { item.draw() }
// Protocol inheritance
protocol AnimatableDrawable: Drawable {
func animate(duration: Double)
}Codable protocol
struct User: Codable {
let id: Int
let name: String
let email: String?
// Custom keys
enum CodingKeys: String, CodingKey {
case id, name
case email = "user_email"
}
}🔣 Generics
Generic functions & types
// Generic function
func swap<T>(_ a: inout T, _ b: inout T) {
let temp = a; a = b; b = temp
}
// Generic struct with constraint
struct Stack<T> {
private var items: [T] = []
mutating func push(_ item: T) { items.append(item) }
mutating func pop() -> T? { items.popLast() }
var top: T? { items.last }
}
// Where clause
func printIfEqual<T: Equatable>(_ a: T, _ b: T) {
if a == b { print("Equal: \(a)") }
}
// some / any (Swift 5.7+)
func makeShape() -> some Shape { Circle() } // opaque type
func render(_ shape: any Shape) { shape.draw() } // existential⏳ Async / Await
Async functions
func fetchUser(id: String) async throws -> User {
let url = URL(string: "https://api.example.com/users/\(id)")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data)
}
// Call
Task {
do {
let user = try await fetchUser(id: "1")
print(user.name)
} catch { print(error) }
}Parallel async (async let)
// Run in parallel — both start immediately
async let user = fetchUser(id: "1")
async let posts = fetchPosts(userId: "1")
// Await both
let (u, p) = try await (user, posts)
// withTaskGroup
await withTaskGroup(of: User.self) { group in
for id in ids {
group.addTask { try await fetchUser(id: id) }
}
for await user in group { results.append(user) }
}Actor (thread safety)
actor UserCache {
private var cache: [String: User] = [:]
func get(_ id: String) -> User? { cache[id] }
func set(_ user: User, for id: String) {
cache[id] = user
}
}
let cache = UserCache()
await cache.set(user, for: user.id)
let cached = await cache.get("1")🔗 Combine
Publishers & subscribers
import Combine
// Just (single value)
let publisher = Just(42)
// Subject
let subject = PassthroughSubject<Int, Never>()
let currentVal = CurrentValueSubject<Int, Never>(0)
// Subscribe
var cancellables = Set<AnyCancellable>()
subject
.filter { $0 > 0 }
.map { "Value: \($0)" }
.receive(on: DispatchQueue.main)
.sink { print($0) }
.store(in: &cancellables)
subject.send(42)
// From Future (async bridge)
Future<User, Error> { promise in
Task { try await promise(.success(fetchUser(id: "1"))) }
}
.sink(
receiveCompletion: { _ in },
receiveValue: { print($0) }
)
.store(in: &cancellables)🔄 Codable / JSON
Encode & Decode JSON
let encoder = JSONEncoder() encoder.keyEncodingStrategy = .convertToSnakeCase encoder.dateEncodingStrategy = .iso8601 encoder.outputFormatting = [.prettyPrinted, .sortedKeys] let data = try encoder.encode(user) let json = String(data: data, encoding: .utf8)! let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase decoder.dateDecodingStrategy = .iso8601 let user = try decoder.decode(User.self, from: data)