You need to add a field to a public struct in a binary SPM library used by five apps. What do you consider?

1.10.6 · L8 · Interview

Mental model. You are changing the shape of a shipping container while five trucks are already on the road. Either the trucks do not care, or they must all come back.

First, is the type resilient or @frozen? Resilient (library evolution on, non-frozen) hides layout, so adding a stored property can be ABI-compatible. Frozen means no — you have promised the layout.

Then consider: default values for existing initializers, whether the struct crosses @inlinable boundaries, Codable compatibility (old payloads lack the key), and whether identity really wanted a class.

Sometimes the right move is a new type plus deprecation, not a silent layout change. Ship a migration guide and a minor version if source breaks; never a patch.

Common traps

  • Adding a non-optional field with no default. Every client initializer call breaks at compile time — and every existing payload breaks at decode time.
  • Assuming “it compiles here” means it is safe. Binary compatibility is about clients you cannot see, built with older compilers.
  • Changing the field order for a frozen struct. That is an ABI break even though the source looks identical.

They might follow up with

  • How do you add a field without breaking Codable? Make it optional, give it a default in a custom init(from:), or version the payload.
  • How do you test ABI compatibility? Compare the generated .swiftinterface and symbol graph between releases; run client smoke tests against the old binary.

Related questions

  • 1.10.4 What is ABI stability vs source compatibility vs module stability?
  • 1.10.5 What does @frozen on an enum mean for you as an API author?
  • 1.10.1 How does Codable synthesis work? When do you write CodingKeys or a custom init(from:)?

← What does @frozen on an enum mean for you as an API author?   How do you set a language bar for a large iOS org (Swift 6… →