- Write the interface
- Write the service implementation
- Register the service in main
- Register the service in renderer
- Use the service
Tips: You can copy/paste the skeleton folder for a quick start.
An observer is an object for which all functions are notifications.
import { observer } from 'services/lib/helpers';
import { RPC } from 'services/lib/types';
import { ServiceBase } from 'services/lib/class';
@service('my-service')
export class MyService extends ServiceBase implements RPC.Interface<MyService> {
// @ts-ignore current class is an empty shell, so TS is not happy
// but we also uses it as an Interface.
// @ts-ignore
addObserver(obs: MyServiceObserver): Promise<RPC.Subscription> {}
}
@service('my-service', { observer: true }) // same namespace
export class MyServiceObserver extends ServiceBase implements RPC.Interface<MyServiceObserver> {
// @ts-ignore
onClick(): void {}
// @ts-ignore
onHover(): void {}
}
export class MyServiceImpl extends MyService implements RPC.Interface<MyService> {
addObserver(obs: RPC.ObserverNode<MyServiceObserver>) {
const subscriptions = [];
if (obs.onClick) {
const fn = () => {
obs.onClick();
}
someEventEmitter.on('click', fn);
subscriptions.push(() => {
someEventEmitter.removeEventListener('click', fn)
})
}
if (obs.onHover) {
const fn = () => {
obs.onHover();
}
someEventEmitter.on('hover', fn);
subscriptions.push(() => {
someEventEmitter.removeEventListener('hover', fn)
})
}
// The second parameter allows us to sync the destruction of the subscription and the observer:
// - if `unsubscribe()` is called, `obs.destroy()` is called
// - if `obs.destroy()` is called, `unsubscribe()` is called
return new ServiceSubscription(subscriptions, obs);
}
}
export class MyServiceImpl extends MyService implements RPC.Interface<MyService> {
addObserver(obs: RPC.ObserverNode<MyServiceObserver>) {
const subscriptions = [];
if (obs.onClick) {
subscriptions.push(fromEvent(someEventEmitter, 'click')
.subscribe(() => {
obs.onClick();
})
);
}
if (obs.onHover) {
subscriptions.push(fromEvent(someEventEmitter, 'click')
.subscribe(() => {
obs.onHover();
})
);
}
return new ServiceSubscription(subscriptions, obs);
}
}
- See services/main/index and services/renderer/index
- Add the new service type to
GlobalService
type in services/types
const myService: RPC.Node<MyService>;
// Here, the `observer` helper converts its parameter to an actual Service,
// which allows it to be serialized.
const subscription = myService.addObserver(observer({
onClick() {/* ... */},
onHover() {/* ... */},
}));
// ...later, you can manually unsubscribe
subscription.unsubscribe();
A provider allows us to inject dependencies into a Service implementation, and thus delegate some logic to another process.
import { observer } from 'services/lib/helpers';
import { RPC } from 'services/lib/types';
import { ServiceBase } from 'services/lib/class';
@service('my-service')
export class MyService extends ServiceBase implements RPC.Interface<MyService> {
// @ts-ignore
addProvider(provider: MyServiceProvider): Promise<RPC.Subscription> {}
}
// A Provider is just a normal Service
@service('my-service')
export class MyServiceProvider extends ServiceBase implements RPC.Interface<MyServiceProvider> {
// @ts-ignore
handleSomething(): Promise<string> {} // Contrary to observers, providers should return values
}
export class MyServiceImpl extends MyService implements RPC.Interface<MyService> {
async addProvider(provider: MyServiceProvider) {
// do things
// ...
// then delegate to provider
const value = await provider.handleSomething();
// Do something with `value`
// ...
// Unplug it here if necessary
return new ServiceSubscription(...);
}
}
export class MyServiceProviderImpl extends MyServiceProvider implements RPC.Interface<MyServiceProvider> {
async handleSomething() {
// Here for instance we are in the worker,
// We could want to fetch something from the store:
const value = myRandomSelector(this.store.getState());
// This is the value that the handler will use
return value;
}
}
const myService: RPC.Node<MyService>; // or it could come from servicesManager
const subscription = myService.addProvider(new MyServiceProviderImpl());
// ...later, you can manually unsubscribe
subscription.unsubscribe();