Guidelines for organizing backend protocols
BackendFeatures. If you're trying to write your own
backend by using the BackendFeatures set of protocols, refer to
doc:Custom-backends and the documentation for the appropriate protocols.We recently split up the monolithic AppBackend protocol into a set of
three dozen or so smaller protocols to better organize the huge set of backend
functionality and to allow for a more modular backend development approach. This
file contains some guidelines as to how to organize these protocols when adding
new backend methods, in order to keep our protocol definitions easily
maintainable.
If you're augmenting an existing SwiftCrossUI feature with new functionality,
it's usually best to add a new method to an existing protocol that deals with
that feature. For example, if you're adding functionality to WebView and
need a new backend method, you should put it inside of
BackendFeatures/WebViews.
Here are some guidelines for adding methods to existing protocols:
BaseAppBackend (including methods in
BackendFeatures/Core), add a default implementation to
BackendFeatures/BaseStubs. There's a private struct in that file called
BaseStubsTest that the compiler will probably error on if you add new
backend APIs without updating BaseStubs; see that type's doc comment for
further info.Don't add these implementations for protocols which aren't a part of
BaseAppBackend.
If you're adding a wholly new feature to SwiftCrossUI -- for example, a new control -- you should put its corresponding backend methods in a new protocol.
Here are some tips for new backend protocols:
@MainActor -- it doesn't make much sense for backends
to run outside the main actor, since most UI frameworks must be run on the
main thread. If you absolutely have to, you can mark individual requirements
nonisolated.BackendFeatures/Controls or BackendFeatures/Gestures), add it there
and don't add it anywhere else. It'll automatically bubble up the protocol
inheritance chain into BaseAppBackend or FullAppBackend.BackendFeatures/Core. You should have to do this very
rarely.BaseAppBackend
typealias.FullAppBackend. Make sure to dynamically cast your
backend instance in the feature's implementation and prepare some sort of
fallback if the backend doesn't support the feature. (The internal
CastBackend macro can help with casting backends in view implementations.)Core and nothing else; if you
need to inherit from a different backend protocol, you can do that too. The
only non-backend conformance should be Sendable, which is required by
Widgets and thus usually inherited.AppBackend folder with a
name modeled on BackendFeatures+Feature.