Types

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Types

This page draws heavily from these swift.org references:

Protocol: Nominal or Non-Nominal?

If a type is either nominal or non-nominal, where do protocols fit? You certainly name a protocol, but can you create an instance using MyProtocol()? Well, according to Nominal Types, protocols are non-nominal:

protocol MyProtocol {
  var number: Int { get set }
  init(number: Int)
}

let mp = MyProtocol(number: 42)
Type 'any MyProtocol' cannot be instantiated

Boxed Protocol (Existential) & Opaque Types

Boxed protocol and opaque types are two ways to hide details about a value’s concrete type. A boxed protocol type is also called an existential type, which comes from the phrase “there exists a type T such that T conforms to the protocol”.

func max<T>(_ x: T, _ y: T) -> T where T: Comparable { ... }
func makeTrapezoid() -> some Shape { ... }

Self & Any Types

Simulating Abstract Classes

This example is from John Sundell’s Abstract types and methods in Swift. It discusses how to simulate an abstract class, with trade-offs when using a protocol with associated type vs. composing a class and a protocol.

class Loadable<Model> {
  func load(from url: URL) async throws -> Model {
    fatalError("load(from:) has not been implemented")
  }
}
protocol Loadable {
  associatedtype Model
  func load(from url: URL) async throws -> Model
}

class UserLoader: Loadable {
  func load(from url: URL) async throws -> User {
    ...
  }
}
protocol LoadableProtocol {
  associatedtype Model
  func load(from url: URL) async throws -> Model
}

class LoadableBase<Model> {
  let networking: Networking
  let cache: Cache<URL, Model>

  init(networking: Networking, cache: Cache<URL, Model>) {
    self.networking = networking
    self.cache = cache
  }
}
class UserLoader: LoadableBase<User>, LoadableProtocol {
  ...
}
typealias Loadable<Model> = LoadableBase<Model> & LoadableProtocol
protocol LoadableProtocol {
  associatedtype Model
  
  var networking: Networking { get }
  var cache: Cache<URL, Model> { get }
  
  func load(from url: URL) async throws -> Model
}

extension LoadableProtocol {
  func loadWithCaching(from url: URL) async throws -> Model {
    if let cachedModel = cache.value(forKey: url) {
      return cachedModel
    }
    
    let model = try await load(from: url)
    cache.insert(model, forKey: url)
    return model
  }
}
See forum comments
Download course materials from Github
Previous: Type Erasure Next: Protocols - Conclusion