Implementing Ipc Interfaces
This article discusses IPC communication in iTwin.js. See also RPC vs IPC.
Overview
IPC (Inter-Process Communication)) is a direct-connect communication technique used in iTwin.js when the frontend and backend processes are paired one-to-one. IPC is most commonly used for native apps where both processes are always on the same computer, but may also be used by web apps with a "dedicated" backend.
IpcSocket Interface
The basis for communication between the frontend/backend pair is a platform-specific implementation of the IpcSocket interface. There are two specializations of IpcSocket, IpcSocketFrontend and IpcSocketBackend for the frontend and backend respectively. They both allow sending and receiving messages, but frontend has a method to invoke functions on the backend, and the backend interface has a method to handle those invocations.
For desktops, those interfaces are implemented by Electron's ipc layers, exposed through the @itwin/core-electron
package.
To use Ipc under Electron, you must call ElectronApp.startup
on the frontend and ElectronHost.startup
on the backend:
import { ElectronHost } from "@itwin/core-electron/lib/cjs/ElectronBackend";
export async function initializeForElectron(rpcInterfaces: RpcInterfaceDefinition[]) {
await ElectronHost.startup({ electronHost: { rpcInterfaces } });
}
and
export async function initializeElectron(rpcInterfaces: RpcInterfaceDefinition[]) {
await ElectronApp.startup({
iModelApp: {
rpcInterfaces,
localization: new EmptyLocalization(),
},
});
}
For mobile devices those interfaces are implemented over WebSockets.
TODO: mobile initialization
IpcApp and IpcHost
Generally, iTwin.js programmers won't need to work with the low-level IpcSocket
interface.
On the frontend, the class IpcApp must be initialized at startup with the platform-specific implementation of IpcSocketFrontend
and contains the method IpcApp.addListener to supply a handler for notification messages sent from the backend to the frontend.
On the backend, the class IpcHost must be initialized at startup with the platform-specific implementation of IpcSocketBackend
and contains the method IpcHost.send to send a notification message from the backend to the frontend.
Since Ipc is only enabled in situations where a dedicated backend is available, each of IpcApp
and IpcHost
have an isValid
method that may be tested from code designed to work with or without Ipc.
Creating Your own Ipc Interfaces and IpcHandlers
To enable type-safe cross-process method calls using IPC, there are three required pieces:
- Define the method signatures in an interface. This must be in a file that can be
import
ed from both your frontend code and backend code. In iTwin.js we use the convention of a folder namedcommon
for this purpose. Note that all methods in your interface must return aPromise
. In the same file, define a variable that has a string with a unique name for the ipc channel your interface will use. If you'd like, you can incorporate a version identifier in the channel name (e.g. append "-1").
const myChannel = "my-interface-v1.2";
interface MyInterface {
sayHello(arg1: string, arg2: number, arg3: boolean): Promise<string>;
}
- In your backend code, implement a class that extends IpcHandler and implements the interface you defined in step 1. In your startup code, call the static method
register
on your new class. Your class must implement the abstract methodget channelName()
. Return the channel name variable from your interface file.
class MyClassHandler extends IpcHandler implements MyInterface
public get channelName() { return myChannel; }
public async sayHello(arg1: string, arg2: number, arg3: boolean) {
return `hello: ${arg1} ${arg2} ${arg3}`
}
// ...in startup code
MyClass.register();
- In your frontend code, make an Proxy object using
IpcApp.makeIpcProxy
:
const myBackendIpc = IpcApp.makeIpcProxy<MyInterface>(myChannel);
This makes a Proxy object to call the methods of MyInterface
from the frontend.
const hello = await myBackendIpc.sayHello("abc", 10, true);
Note that all IPC methods return a
Promise
, so their return value must beawait
ed.
As you refine your interface, you may decide to change your channel name.
Your application can have as many Ipc Interfaces as you like (each must have a unique channel name), and each of your interfaces may have as many functions as you need.
Last Updated: 15 May, 2024