Author: saqibkhan

  • Swift Concurrency

    Swift Concurrency

    Write safe, structured async code with async/await, Tasks, actors, and cooperative cancellation.


    Basic GCD (Grand Central Dispatch)

    Use DispatchQueue to perform work asynchronously.

    The example below runs a task on a background queue and waits for it to complete.

    Syntax:

    • DispatchQueue.global().async { ... }
    • DispatchGroup.enter()
    • DispatchGroup.leave()
    • DispatchGroup.wait()

    Example

    import Dispatch
    
    print("Start")
    let group = DispatchGroup()
    group.enter()
    DispatchQueue.global().async {
      print("Background work")
      group.leave()
    }
    group.wait()
    print("Done")

    This example runs work on a background queue and waits for completion using a DispatchGroup.

    Tip: In modern Swift, prefer async/await and Task over GCD for structured concurrency.


    Async/Await with Task

    async/await lets you write asynchronous code that looks like synchronous code.

    Use Task to start concurrent work from synchronous contexts.

    Syntax:

    • func name() async -> T {}
    • await value
    • Task { ... }

    Example

    import Dispatch
    
    func fetchValue() async -> Int { 7 }
    
    print("Start")
    let sem = DispatchSemaphore(value: 0)
    Task {
      let v = await fetchValue()
      print("Got \(v)")
      sem.signal()
    }
    sem.wait()
    print("Done")

    This example starts asynchronous work with Task and awaits a value using async/await.



    async let (Parallel Child Tasks)

    Use async let to start multiple child tasks in parallel and await their results.

    Syntax: async let name = expression() starts a child task; use await when reading the value.

    Example

    import Dispatch
    
    func fetch(_ id: Int) async -> Int { id * 10 }
    
    print("Start")
    let sem = DispatchSemaphore(value: 0)
    Task {
      async let a = fetch(1)
      async let b = fetch(2)
      let total = await (a + b)
      print("Total \(total)")
      sem.signal()
    }
    sem.wait()
    print("Done")

    This example launches two child tasks in parallel and awaits both to compute a total.


    Async/Await with Errors

    Async functions can also throw.

    Combine try with await and handle failures with do/catch.

    Syntax:

    • func name() async throws -> T
    • try await
    • do { ... } catch { ... }

    Example

    import Dispatch
    
    enum FetchError: Error { case bad }
    
    func fetch(_ ok: Bool) async throws -> Int {
      if !ok { throw FetchError.bad }
      return 42
    }
    
    print("Start")
    let sem = DispatchSemaphore(value: 0)
    Task {
      do {
    
    let v = try await fetch(false)
    print("ok \(v)")
    } catch {
    print("error")
    } sem.signal() } sem.wait() print("Done")

    This example shows error handling with try/await and do/catch around an async function.


    Task Groups

    Use withTaskGroup to fan out concurrent child tasks and aggregate their results.

    Syntax: withTaskGroup(of: ReturnType.self) { group in ... } adds tasks and iterates results.

    Example

    import Dispatch
    
    func square(_ n: Int) async -> Int { n * n }
    
    print("Start")
    let sem = DispatchSemaphore(value: 0)
    Task {
      var results: [Int] = []
      await withTaskGroup(of: Int.self) { group in
    
    for n in [1,2,3] {
      group.addTask { await square(n) }
    }
    for await val in group {
      results.append(val)
    }
    } print(results.sorted().map(String.init).joined(separator: ",")) sem.signal() } sem.wait() print("Done")

    This example concurrently computes squares for a list of numbers and aggregates the results from the task group.


    Actors and MainActor

    Actors protect their mutable state from data races by serializing access.

    Use @MainActor to mark code that must run on the main thread (e.g., UI updates).

    Syntax: actor Name { ... } defines an actor; call methods/properties with await from outside.

    MainActor: Annotate types/functions with @MainActor to run on the main thread.

    Example

    import Dispatch
    
    actor SafeCounter { private var value = 0 func increment() { value += 1 } func get() -> Int { value } } let counter = SafeCounter() print("Start") let sem = DispatchSemaphore(value: 0) Task { await withTaskGroup(of: Void.self) { group in
    for _ in 0..<1000 {
      group.addTask { await counter.increment() }
    }
    } print("Final: \(await counter.get())") sem.signal() } sem.wait() print("Done")

    This example uses an actor to protect mutable state and aggregates increments via a task group.

    Tip: Annotate UI-facing APIs with @MainActor to ensure they execute on the main thread.


    Task Cancellation

    Cancel long-running work by calling task.cancel() and checking for cancellation with Task.isCancelled or try Task.checkCancellation().

    Syntax:

    • t.cancel()
    • Task.isCancelled
    • try Task.checkCancellation()

    Example

    import Dispatch
    
    func slowWork() async throws {
      for i in 1...5 {
    
    try await Task.sleep(nanoseconds: 300_000_000) // 0.3s
    try Task.checkCancellation()
    print("Step ", i)
    } } let sem = DispatchSemaphore(value: 0) let t = Task { do { try await slowWork() } catch { print("Cancelled") } sem.signal() } DispatchQueue.global().asyncAfter(deadline: .now() + 0.7) { t.cancel() } sem.wait()
  • Swift Error Handling

    Swift Error Handling

    Throw and catch errors explicitly, or convert failures to optionals with try? when appropriate.


    Throw, Try, Catch

    Functions can throw errors with throw.

    Callers must use try and handle failures with do/catch, or use try? to get an optional result.

    Syntax:

    • func f() throws -> T
    • try f()
    • try? f()
    • do { ... } catch { ... }

    Example

    enum InputError: Error { case negative }
    
    func validate(_ n: Int) throws -> String {
      if n < 0 { throw InputError.negative }
      return "ok: \(n)"
    }
    
    do {
      let result = try validate(-1)
      print(result)
    } catch {
      print("error")
    }
    
    let maybe = try? validate(1)
    print(maybe ?? "nil")

    Tip: Use defer to run cleanup code before exiting the current scope, even when errors are thrown.



    Defer (Cleanup)

    Use defer to ensure cleanup code always runs when a scope exits, even if an error is thrown.

    Example

    enum FileError: Error { case fail }
    
    func work(_ ok: Bool) throws {
      print("start")
      defer { print("cleanup") }
      if !ok { throw FileError.fail }
      print("done")
    }
    
    do { try work(false) } catch { print("error") }

  • Swift Equatable & Comparable

    Swift Equatable & Comparable

    Adopt Equatable and Comparable so your types support == and ordering operations.


    Derived vs Custom Conformance

    Derived types inherit conformance automatically, while custom conformance requires explicit implementation.

    Example

    struct Point: Equatable, Comparable {
      var x: Int, y: Int
      static func == (lhs: Point, rhs: Point) -> Bool { lhs.x == rhs.x && lhs.y == rhs.y }
      static func < (lhs: Point, rhs: Point) -> Bool { (lhs.x, lhs.y) < (rhs.x, rhs.y) }
    }
    
    let a = Point(x: 1, y: 2), b = Point(x: 1, y: 3)
    print(a == b) // false
    print(a < b)  // true


    Sorting with Comparable

    Conform to Comparable to sort custom types and use min()/max().

    Example

    struct Score: Comparable {
      let user: String
      let value: Int
      static func < (l: Score, r: Score) -> Bool { l.value < r.value }
    }
    
    let scores = [Score(user: "A", value: 10), Score(user: "B", value: 5), Score(user: "C", value: 7)]
    let sorted = scores.sorted()
    print(sorted.map { $0.value })
    print(sorted.last!.value)
  • Swift Value Semantics & COW

    Prefer value semantics for predictability.

    Swift collections use Copy-on-Write (COW) to keep copies cheap until mutation.


    Syntax

    var a = [1,2,3]

    Arrays, sets, and dictionaries use value semantics.


    Copy-on-Write

    Swift collections use Copy-on-Write (COW) to keep copies cheap until mutation.

    Example

    var a = [1,2,3]
    var b = a // shares storage
    b.append(4) // triggers copy for b only
    print(a) // [1,2,3]
    print(b) // [1,2,3,4]
  • Swift Deinitializers

    Swift Deinitializers

    Run cleanup code before a class instance is deallocated using deinit.


    deinit

    Deinitializers are called automatically when an instance is deallocated.

    Syntax: deinit { ... }

    Example

    class FileHandle {
      init() { print("open") }
      deinit { print("close") }
    }
    
    var h: FileHandle? = FileHandle()
    h = nil // prints "close"

  • Swift Initializers

    Swift Initializers

    Create and customize object setup with designated, convenience, and memberwise initializers.


    Memberwise and Custom Init

    Structs get a memberwise initializer by default.

    You can also define custom initializers.

    Syntax: init(params) { self.prop = ... }

    Example

    struct User { var name: String; var age: Int }
    let a = User(name: "Morgan", age: 30) // memberwise
    
    struct Point {
      var x: Int, y: Int
      init(_ x: Int, _ y: Int) { self.x = x; self.y = y }
    }
    let p = Point(1, 2)

    This example shows a memberwise initializer for a simple struct and a custom initializer with positional parameters.


    Class Designated vs Convenience

    Classes use designated initializers to fully initialize stored properties and convenience initializers to provide shortcuts.

    Syntax:

    • init(...) (designated)
    • convenience init(...) (must call self.init)

    Example

    class Person {
      let name: String
      let age: Int
      init(name: String, age: Int) {
    
    self.name = name; self.age = age
    } convenience init(name: String) { self.init(name: name, age: 0) } } let p1 = Person(name: "Robin", age: 30) let p2 = Person(name: "Elisabeth")

    The convenience initializer provides a default for age by delegating to the designated initializer.



    Failable and Throwing Init

    Use failable initializers to return nil on invalid input; use throwing initializers to signal errors.

    Syntax:

    • init?
    • init throws

    Example

    struct Email {
      let value: String
      init?(_ s: String) { if s.contains("@") { value = s } else { return nil } }
    }
    
    enum InitError: Error { case invalid }
    struct Port {
      let number: Int
      init(_ n: Int) throws { guard (1...65535).contains(n) else { throw InitError.invalid }; number = n }
    }
  • Swift Access Control

    Swift Access Control

    Restrict visibility of types and members with publicinternalfileprivate, and private.


    Levels

    Swift’s defaults are safe by design: internal is the default.

    Syntax:

    • public (visible to other modules)
    • internal (module-only, default)
    • fileprivate (this file)
    • private (this scope type/extension)

    Example

    public struct APIClient {
      public init() {}
      public func request() {}
    }
    
    struct Repository { // internal by default
      fileprivate var cache: [String: String] = [:]
      private func reset() { cache.removeAll() }
    }

    This example shows public APIs, a module-internal type, a file-scoped property, and a private helper.



    Types and Members

    A member cannot be more visible than its enclosing type.

    Syntax:

    • public struct S { internal var x: Int } (valid)
    • but not internal struct S { public var x: Int }

    Example

    internal struct Box { // whole type is internal
      public var value: Int // warning/error: member more visible than type
    }
  • Swift Extensions

    Swift Extensions

    Add functionality to existing types without subclassing, including methods, computed properties, and protocol conformances.


    Computed Properties and Methods

    Extend any type to add helpers that are scoped to that type.

    Syntax: extension Type { var computed: T { ... }; func util() { ... } }

    Example

    extension String {
      var isBlank: Bool { trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
      func repeated(_ n: Int) -> String { String(repeating: self, count: n) }
    }
    
    print("  ".isBlank)    // true
    print("Hi".repeated(3)) // HiHiHi

    This example adds a computed property and a helper method to String.


    Protocol Conformance in Extensions

    Adopt protocols outside the original type declaration to separate concerns.

    Syntax: extension Type: Protocol { ... }

    Example

    protocol Describable { func describe() -> String }
    
    struct User { let name: String }
    
    extension User: Describable {
      func describe() -> String { "User(\(name))" }
    }
    
    print(User(name: "Morgan").describe())

    This example adds Describable conformance to User via an extension.



    Nesting by Responsibility

    Organize large types using multiple extensions grouped by feature (e.g., networking, formatting).

    Syntax:

    • extension Type { /* Feature A */ }
    • extension Type { /* Feature B */ }

    Example

    struct Article { let title: String; let body: String }
    
    extension Article { // Formatting helpers
      var preview: String { String(body.prefix(40)) + "..." }
    }
    
    extension Article { // Networking stub
      static func fetchAll() -> [Article] { [] }
    }
  • Swift Generics

    Swift Generics

    Write reusable code with type parameters and constraints to ensure correctness without duplication.


    Generic Functions

    Generics let you write flexible, reusable functions and types.

    Use a placeholder type name like T and constrain when needed.

    Syntax:

    • func name<T>(_ a: T) -> T
    • struct Box<T> { ... }

    Constraints:

    • T: Comparable
    • where clauses for more complex bounds.

    Example

    func swapTwo<T>(_ a: inout T, _ b: inout T) {
      let tmp = a
      a = b
      b = tmp
    }
    
    var x = 1, y = 2
    swapTwo(&x, &y)
    print(x)
    print(y)

    This example defines a generic T-typed swap function and shows it working with integers.



    Generic Constraints (where)

    Constrain generic parameters to types that meet certain requirements, like Comparable, using where clauses.

    Example

    func minValue<T: Comparable>(_ a: T, _ b: T) -> T { a < b ? a : b }
    
    print(minValue(3, 7))        // 3
    print(minValue("b", "a"))  // a
  • Swift Protocols

    Swift Protocols

    Define behavior contracts that types adopt, and extend them to add default implementations.


    Defining and Conforming to Protocols

    protocol defines a blueprint of methods and properties.

    Types adopt a protocol by providing implementations.

    Syntax:

    • protocol P { var x: Int { get set }; func f() }
    • struct S: P { ... }

    Example

    protocol Greetable { func greet() -> String }
    
    struct Person: Greetable {
      var name: String
      func greet() -> String { "Hello, \(name)" }
    }
    
    let p = Person(name: "Swift")
    print(p.greet())

    This example demonstrates how a protocol is defined and adopted by a type, and how the type provides an implementation for the protocol’s method.

    Tip: Use protocol extensions to provide default method implementations.


    Protocol Extensions (Default Implementations)

    Provide default behavior for conforming types by adding implementations in a protocol extension.

    Example

    protocol Describable { func describe() -> String }
    
    extension Describable {
      func describe() -> String { "(no description)" }
    }
    
    struct User: Describable { let name: String }
    
    struct Car: Describable {
      let model: String
      func describe() -> String { "Car: \(model)" }
    }
    
    let u = User(name: "Morgan")
    let c = Car(model: "SwiftMobile")
    print(u.describe())
    print(c.describe())

    This example gives User a default describe() and overrides it in Car.



    Protocols with Associated Types

    Use associatedtype to make a protocol generic over a placeholder type.

    Conformers bind the placeholder to a concrete type.

    Example

    protocol Container {
      associatedtype Element
      mutating func append(_ item: Element)
      var count: Int { get }
      subscript(i: Int) -> Element { get }
    }
    
    struct IntStack: Container {
      private var items: [Int] = []
      mutating func append(_ item: Int) { items.append(item) }
      var count: Int { items.count }
      subscript(i: Int) -> Int { items[i] }
    }
    
    func allItemsMatch<C1: Container, C2: Container>(_ c1: C1, _ c2: C2) -> Bool
    where C1.Element == C2.Element, C1.Element: Equatable {
      guard c1.count == c2.count else { return false }
      for i in 0..<c1.count { if c1[i] != c2[i] { return false } }
      return true
    }