Generics, Existentials, and code size

I recently joined a company with a large app. Anything that can be tested, has an associated protocol (with a restriction on using PATs). As a result, initializers for virtually everything define their parameters as existentials rather than concrete types. implementation.

Example:

// ModuleA
public protocol ModelProtocol { }

public final class Model: ModelProtocol { }

// ModuleB
import ModuleA

public protocol ComplexModelProtocol {
  init(model: ModelProtocol)
}

public final class ComplexModel: ComplexModelProtocol {
  let model: ModelProtocol

  public init(model: ModelProtocol) {
    self.model = model
  }
}

// In test targets
final class MockModel: ModelProtocol { }

final class MockComplexModel: ComplexModelProtocol {
  let model: ModelProtocol

  public init(model: ModelProtocol) {
    self.model = model
  }
}

In production every protocol (with few exceptions) has only one concrete protocol. The company has a fair amount of internal documentation stating that using generics drastically bloats binary size compared to using existentials. I believe (from following these forums for some time), that there is definitely a performance hit in using existentials everywhere compared to taking advantage of generics, but I've encountered little discussion around binary size.

It feels "un-swifty" to be doing this, at the very least unfamiliar in my experience in Swift (although I have definitely encountered this pattern in other languages). My question is:
What is the guidance on binary size? It seems wrong to ignore generics altogether.
It seems that where we really only have one concrete implementation in most cases, that the compiler would generate the same code as if we just used the concrete implementations directly (if we used generics). Generics or using the boxing method we are is only necessary for testing, is there another approach altogether to enable testing?

6 Likes

If there's only one concrete type conforming to these protocols they shouldn't be paying much of a binary size cost at all. But using generics for this usage would also pay a high usability cost, as it may require extensive refactoring to make every use site generic, so I'm not sure any conversion would be worth it.

In the end, this terrible, terrible model is Swift's equivalent of "enterprise" development. Unless you can change the entire paradigm (and there are a lot of good reasons to do so), I'm not sure this particular approach can be improved in any significant way.

That said, it would be good to gather some concrete data about this, or have someone who actually knows what they're talking about weigh in, since I don't.

1 Like
Terms of Service

Privacy Policy

Cookie Policy