Dependency Injection
Hosanna creates one application IoC container during launch. Framework services are registered first, platform services can replace them, and your custom services are applied last. Resolving a registered class creates and caches its instance.
Register application services
Override getCustomIOCServices() in your BaseApp subclass:
import { IOCServiceMap } from '@hs-src/hosanna-bridge-core/IocContainer';
export class App extends BaseApp {
protected override getCustomIOCServices(): IOCServiceMap {
return [
{ key: 'catalogueService', clazz: CatalogueService },
{ key: 'session', clazz: Session },
];
}
}
A custom entry with the same key replaces the base or platform mapping. Keep keys stable and register dependencies before a view first accesses them.
For an instance created at runtime, use AppUtils.register():
const session = new Session();
AppUtils.register('session', session);
const resolved = AppUtils.resolve<Session>('session');
AppUtils.resolve<T>(key, isRequired = true) is the public lookup helper.
Passing false makes a missing service optional. There is no AppUtils.get()
or public static IoCContainer.resolve() API.
Choose an injection decorator
@inject
Use @inject() for ordinary services. With no argument, the property name is
the service key:
export class DetailsScreenView extends BaseView<DetailsScreenState> {
@inject() private catalogueService!: CatalogueService;
@inject('session')
private currentSession!: Session;
}
On TypeScript targets the property resolves lazily on first access and caches the result. Generated platform code may resolve at a different lifecycle point, so startup services should still be registered before views are instantiated.
@injectState
Use @injectState() on a BaseView when the injected value must be stored
through the view's state machinery:
@injectState('session')
session!: Session;
@injectObservable
Use @injectObservable() for a shared HsObservable. It resolves the object,
stores it in view state, and subscribes the view to the requested fields:
@injectObservable('playbackState', ['position', 'state'])
playbackState!: PlaybackState;
Omitting the field list uses ['__internalState'], which receives changes from
all of the observable object's decorated fields.
Use @inject for services, @injectState for a value managed as view state,
and @injectObservable when changes inside a shared observable should
invalidate the view.
Device layout is an injected service
The platform registers one deviceLayout object. It is updated in place when
screen metrics change:
import type { IHosannaDeviceLayoutInfo } from '@hs-src/hosanna-bridge-core/api';
export class DetailsScreenView extends BaseView<DetailsScreenState> {
@inject()
protected deviceLayout!: IHosannaDeviceLayoutInfo;
protected override getViews() {
return [
Group([
// Content can use contentX/contentY and contentWidth/contentHeight.
]),
];
}
}
See the safe-area guide for the available content and inset measurements.
Runtime registration of shared state
The samples register an existing observable before presenting a view that injects it:
AppUtils.register('myObservable', this.myObservable);
this.present(InjectObservableRig());
export class InjectObservableRigView extends BaseView {
@injectObservable()
myObservable!: SimpleObservable;
}
Registration replaces the value stored under that key. The public helper does not provide an unregister operation; choose application-lifetime keys or manage shorter-lived values explicitly.