Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save gohanlon/6aaeff970c955c9a39308c182c116f64 to your computer and use it in GitHub Desktop.
Save gohanlon/6aaeff970c955c9a39308c182c116f64 to your computer and use it in GitHub Desktop.

Swift’s memberwise initializer and the extension dance

Swift's automatically provided memberwise initializer is a powerful feature. However, when an explicit initializer is present, Swift omits its memberwise initializer:

struct Person: Decodable {
  let name: String

  enum CodingKeys: CodingKey {
    case name
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.name = try container.decode(String.self, forKey: .name)
  }
}

_ = Person.init(name: "Foo")  // 🛑 Compiler error; Swift won't add a memberwise init

A common workaround is the “extension dance,” which will retain Swift’s memberwise initializer:

struct Person {
  let name: String
}

extension Person: Decodable {
  enum CodingKeys: CodingKey {
    case name
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.name = try container.decode(String.self, forKey: .name)
  }
}

_ = Person.init(name: "Foo")  // 🎉 Still has memberwise init

Using extensions preserves the memberwise initializer, but dictates tradeoffs:

  1. Fragmentation: Using extensions can split up related code, potentially reducing readability by distributing a single concept over multiple locations.
  2. Boilerplate: Protocol conformances via extensions add more repetitive code, which can obscure the primary logic and functionality.
  3. Obscured Conformance: Distributing conformances across multiple extensions can make it harder to immediately identify all the protocols a type conforms to.
  4. Pattern Rigidity: The extension approach dictates a particular design pattern, which might not always be the most suitable for every use-case.

For the convenience of generated memberwise initializers without the tradeoffs of the extension approach (and more), see MemberwiseInit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment