Opinions on `SomeTypeError` vs. `SomeType.Error`


I've been using for a long time pattern, where I declare error enums related to a specific type (i.e. Operation subclass, services, etc.) by creating a nested type:

final class MyOperation: Operation {
    enum Error: Swift.Error { /* ... */ }
    // ...

I've realized, I can't recall from top of my head anyone using the same pattern. My question is simply, would you frown upon such a pattern in your codebase? If so, why?


I’ve been using it, still do eventually (rarely, when it fits), and prefer either to decouple errors from the type, or give specific names to them (say, ValidationError), which is more descriptive in my opinion. It less confuses reader of a class, it simplifies search, it gives more meaning to the error. If there are several unrelated errors, I’d declare them as separate structs instead of enum. It simplifies naming and makes it clear that these are distinct errors, not a “family”.

1 Like
  • When I use an error defined within a type outside, even if the error is named "uniquely" like "TheOperationError", for some reason Xcode's autocomplete doesn't work and when I manually start typing the name Xcode fix-it suggests "This enum is defined on MyOperation, and may not be available in this context", which is a bogus suggestion as the name actually works:

  • Naming it "Error" makes it less discoverable.

Note that these two factors are separate – you could put it inside and name it "uniquely" to make it more discoverable.

Other than that I do not see any issues with your approach. Defining things "as local as possible" makes total sense. I wish we could soon do that with protocols as well.


For non-generic contexts, that is possible since Swift 5.10 via SE-0404.


I put most of my Error types inside the type that uses them - anytime that makes sense. Only if they naturally span multiple types do I push them up to global level.

IMO it's just good organisation, like nesting helper types in general. It makes them easier to find and reason about. And convenient to use inside the type (you can just use their name instead of the fully qualified name).

It's also annoying to pollute the global namespace.