Protocol
Interactable
The base protocol for all napkin interactors.
protocol Interactable : Actor, InteractorScope
Mentioned In
Overview
Business logic for a feature lives in a final actor that conforms to Interactable. Default implementations of every lifecycle method are provided in a protocol extension; the conforming actor only needs to:
Declare a stored lifecycle property: nonisolated let lifecycle = InteractorLifecycle()
Optionally override didBecomeActive() / willResignActive() to perform per-activation setup and teardown.
Overview
Interactable is composed from InteractorScope and Actor rather than inherited from a base class. Swift actors cannot be subclassed, so napkin uses protocol composition plus a default-implementation extension to distribute lifecycle behaviour across many final actor types.
All mutable lifecycle state — the active flag, the bag of in-flight tasks, and the AsyncStream continuations — lives in the InteractorLifecycle helper. The interactor itself only owns business state and the nonisolated let lifecycle reference. Because the lifecycle is nonisolated, its operations can be invoked from outside the actor (for example, from a router on @MainActor) without hopping into the actor’s executor first.
Usage
A typical feature interactor looks like this:
final actor HomeInteractor: PresentableInteractable, HomeInteractable {
nonisolated let lifecycle = InteractorLifecycle()
nonisolated let presenter: HomePresentable
weak var listener: HomeListener?
private let userService: UserService
init(presenter: HomePresentable, userService: UserService) {
self.presenter = presenter
self.userService = userService
}
func didBecomeActive() async {
task {
for await user in self.userService.userStream {
await self.presenter.presentUser(user)
}
}
}
func willResignActive() async {
// Release any references that should not outlive the active scope.
}
}
See Also
InteractorLifecycle
See Also
InteractorScope
See Also
PresentableInteractable
Topics
Lifecycle
State
Tasks
Relationships
Inherits From
Inherited By