Class
Component
The base class for dependency injection components in the napkin architecture.
class Component<DependencyType>
Mentioned In
Overview
A Component serves as the dependency-injection container for a napkin unit. It defines the dependencies that the napkin provides to its own internal pieces (Router, Interactable, Presenter, view) and to its child napkins.
Overview
In the napkin architecture, components form a tree that mirrors the router tree:
Each napkin owns a Component subclass.
The component receives the parent’s component as its dependency and conforms to its own children’s Dependency protocols, which lets it satisfy each child’s required services.
The component creates instances of services that this napkin owns, typically using shared(forCallerKey:_:) so that multiple consumers see the same instance.
Creating a Component
Subclass Component, parameterize it on the parent’s Dependency, and conform to the dependency protocols of any child napkins this napkin builds:
protocol HomeDependency: Dependency {
var userService: UserService { get }
}
final class HomeComponent: Component<HomeDependency>,
ProfileDependency,
SettingsDependency {
// Pass-through from the parent.
var userService: UserService { dependency.userService }
// Lazily created and cached for the lifetime of this component.
var homeAnalytics: HomeAnalytics {
shared { HomeAnalytics(userService: dependency.userService) }
}
// A non-shared dependency: a fresh instance each call.
var requestBuilder: RequestBuilder {
RequestBuilder(userService: dependency.userService)
}
}
Shared vs Non-Shared Dependencies
Use shared(forCallerKey:_:) for instances that should be reused across consumers — services, caches, anything stateful that you want to live as long as the napkin does:
var imageCache: ImageCache {
shared { ImageCache() }
}
Return a fresh instance from the computed property when each consumer should get its own object — request builders, mutable state holders, per-call helpers:
var requestBuilder: RequestBuilder {
RequestBuilder(userService: dependency.userService)
}
shared keys cached instances by #function, so each computed property in your component gets its own slot automatically — no manual keying required.
Concurrency
Component is Sendable. Shared instances created via shared(forCallerKey:_:) are stored under a Mutex from the Synchronization module, so they may be retrieved from any actor or thread safely. shared(forCallerKey:_:) is safe to call concurrently; the framework guarantees the factory runs at most once per call site.
Subclassing
Component declares @unchecked Sendable because it is open and generic. The @unchecked annotation inherits to subclasses without the compiler re-verifying their stored properties. If a subclass adds non-Sendable mutable state, that state is silently unsafe to share across actors. Either keep stored properties immutable and Sendable, or use a Mutex / Synchronization.OSAllocatedUnfairLock to guard them. The framework’s own state (dependency is let, sharedInstances is a Mutex) is genuinely safe; the subclass obligation is on you.
See Also
Dependency
See Also
EmptyComponent
See Also
Builder
Topics
Creating a Component
Sharing Instances
Relationships
Conforms To