App Management

Overview

Available since io.Connect Desktop 10.0

⚠️ Note that the Apps API is still an experimental feature. This means that it may have hidden issues and may be subject to changes in future releases.

The Apps API is designed to replace the legacy App Management API accessible via the io.appManager object. The legacy App Management API is still supported, but will be entirely removed in a future release. It's highly recommended to migrate to the new Apps API.

The Apps API is accessible via the io.apps object.

Configuration

The Apps API is disabled by default. To enable it, set the apps property of the optional Config object for initializing the @interopio/desktop library to true:

import IODesktop from "@interopio/desktop";

const config = {
    apps: true
};

const io = await IODesktop(config);

Apps

The AppRegistry object of the Apps API accessible via io.apps.registry provides methods for managing all available apps (both statically and dynamically defined) within the io.Connect environment.

Retrieving Apps

To retrieve all available apps, use the getMany() method. The getMany() method resolves with an array of Application objects, each describing an app within the io.Connect framework:

const apps = await io.apps.registry.getMany();

apps.forEach(app => console.log(`App name: "${app.name}"`));

The getMany() method accepts an AppFilter object as an optional argument which you can use to filter the result of the operation:

// Filtering the result by app type.
const filter = {
    types: ["window", "node"]
};

const apps = await io.apps.registry.getMany(filter);

apps.forEach(app => console.log(`App name: "${app.name}"`));

To retrieve a specific app, use the get() method and provide a GetAppOptions object as a required argument. It's required to provide the name of the app to retrieve:

const options = {
    // It's required to provide the app name.
    name: "my-app"
};

const myApp = await io.apps.registry.get(options);

console.log(myApp.definition);

Checking for Existing Apps

To check if an app exists within the io.Connect environment, use the has() method and provide the name of the app as a required argument:

const appName = "my-app";
const myAppExists = await io.apps.registry.has(appName);

console.log(`App "${appName}" ${myAppExists ? "is" : "isn't"} available.`);

App Object

The Application object contains basic information about an app defined within the io.Connect framework (e.g., name, type, title, and more). It also provides access to all currently running app instances, the entire app definition, including any custom properties you may have specified for the app.

To retrieve all currently running app instances, use the instances property of the Application object:

const options = { name: "my-app" };
const myApp = await io.apps.registry.get(options);

if (myApp.instances.length > 0) {
    myApp.instances.forEach(instance => console.log(`Instance ID: ${instance.id}`));
} else {
    console.log("No available instances.");
}

The complete app definition is available via the definition property of the Application object. However, by default, when you retrieve multiple apps or a specific app via the getMany() or the get() methods, the Application objects won't include the complete app definitions.

To instruct the platform to include the complete app definitions when retrieving apps, use the includeDefinition property of the AppFilter or the GetAppOptions object respectively.

Including the app definitions when retrieving multiple apps:

const filter = {
    // This will add the respective app definition to each `Application` object.
    includeDefinition: true
};

const apps = await io.apps.registry.getMany(filter);

apps.forEach(app => console.log(app.definition));

Including the app definition when retrieving a specific app:

const options = {
    name: "my-app",
    // This will add the app definition to the `Application` object.
    includeDefinition: true
};

const myApp = await io.apps.registry.get(options);

console.log(myApp.definition);

If you have specified any custom properties for your app, they will be accessible via the customProperties property of the retrieved app definition object:

const options = {
    name: "my-app",
    // This will add the app definition to the `Application` object.
    includeDefinition: true
};

const myApp = await io.apps.registry.get(options);
const { customProperties } = myApp.definition;

console.log(customProperties);

Events

The io.apps.registry object provides methods for handling events related to the apps registered within the io.Connect environment. All methods resolve with an unsubscribe function which you can use to unsubscribe from the respective events.

App Added

To get notified when an app has been added to the environment, use the onAdded() method and provide a handler for the event. The handler will receive an AppAddedEvent object as an argument:

const handler = ({ app }) => console.log(`App "${app.name}" was added.`);

const unsubscribe = io.apps.registry.onAdded(handler);

⚠️ Note that the onAdded() method will be invoked immediately for all already existing apps, as well as for any newly added ones later. This behavior is designed to avoid potential race conditions that may cause your app to miss a notification about an added app.

App Removed

To get notified when an app has been removed from the environment, use the onRemoved() method and provide a handler for the event. The handler will receive an AppRemovedEvent object as an argument:

const handler = ({ appName }) => console.log(`App "${appName}" was removed.`);

const unsubscribe = io.apps.registry.onRemoved(handler);

App Updated

To get notified when an app has been updated, use the onUpdated() method and provide a handler for the event. The handler will receive an AppUpdatedEvent object as an argument:

const handler = ({ app, changes }) => console.log(`The following properties of app "${app.name}" were updated: ${changes}`);

const unsubscribe = io.apps.registry.onUpdated(handler);

Use the changes property of the AppUpdatedEvent object to check which app definition properties have been updated. If a nested property has been updated, the changes object will contain the full path to it. For instance, if you update the "hidden" top-level property, as well as the "width" property of the "details" top-level key in the app definition, the changes object will have the following shape: { hidden: true, details: { width: 200 } }.

In-Memory Store

The InMemoryStore object of the Apps API accessible via io.apps.registry.inMemory provides methods for managing app definitions dynamically.

⚠️ Note that all methods of the InMemoryStore operate only on in-memory app definitions - statically defined apps won't be affected. The in-memory store isn't persistent - if the platform is restarted or shutdown, the in-memory store will be cleared.

Import

To import one or more app definitions dynamically, use the import() method and provide an array of app definition objects as a required argument.

Optionally, specify as a second argument a mode for the import operation. Use "replace" (default) to replace all existing in-memory app definitions with the imported ones, or "merge" to merge the existing definitions with the imported ones, replacing any definitions with the same app name:

const definitions = [
    {
        name: "my-app",
        type: "window",
        title: "My App",
        details: { url: "https://my-domain.com/my-app" }
    },
    {
        name: "my-other-app",
        type: "window",
        title: "My Other App",
        details: { url: "https://my-domain.com/my-other-app" }
    }
];

// The `"merge"` mode will merge the already existing app definitions with the imported ones,
// replacing any definitions with the same app name.
const importResult = await io.apps.registry.inMemory.import(definitions, "merge");

The import() method resolves with an ImportResult object. Use this object to see a list of the names of the successfully imported apps and a list of any errors from the import operation.

Remove

To remove an app definition from the in-memory store, use the remove() method and provide a RemoveAppOptions object as a required argument:

const options = {
    // It's required to provide the app name.
    name: "my-app",
    // Use this to instruct the platform to stop all running instances of the removed app.
    stopInstances: true
};

await io.apps.registry.inMemory.remove(options);

Clear

To clear all app definitions from the in-memory store, use the clear() method. Optionally, provide a ClearAppsOptions object as an argument:

const options = {
    // Use this to instruct the platform to stop all running instances of the removed apps.
    stopAllInstances: true
};

await io.apps.registry.inMemory.clear(options);

Checking for Existing Apps

To check if an app exists within the in-memory store, use the has() method and provide the name of the app as a required argument:

const appName = "my-app";
const myAppExists = await io.apps.registry.inMemory.has(appName);

console.log(`App "${appName}" ${myAppExists ? "is" : "isn't"} available.`);

App Instances

The InstanceManager object of the Apps API accessible via io.apps.instances provides methods for managing all running app instances within the io.Connect environment.

Retrieving App Instances

To retrieve all available app instances, use the getMany() method. The getMany() method resolves with an array of ApplicationInstance objects, each describing an app instance within the io.Connect framework:

const instances = await io.apps.instances.getMany();

instances.forEach(instance => console.log(`Instance ID: ${instance.id}`));

The getMany() method accepts an InstanceFilter object as an optional argument which you can use to filter the result of the operation:

// Filtering the result by app name.
const filter = {
    appNames: ["my-app", "my-other-app"]
};

const instances = await io.apps.instances.getMany(filter);

instances.forEach(instance => console.log(`Instance ID: ${instance.id}`));

To retrieve a specific app instance, use the get() method and provide an InstanceSelect object as a required argument. It's required to provide the ID of the app instance to retrieve:

const options = {
    // It's required to provide the ID of the app instance.
    id: "my-instance-id"
};

const myInstance = await io.apps.instances.get(options);

Start

To start an app instance, use the start() method and provide a StartAppOptions object as a required argument. It's required to provide the name of the app:

const options = {
    // It's required to provide the app name.
    name: "my-app",
    // Context for the instance, which will override the context specified in the app definition.
    context: { io: 42 }
};

const myInstance = await io.apps.instances.start(options);

console.log(`Instance with ID ${myInstance.id} was started.`);

Stop

To stop an app instance, use the stop() method and provide a StopInstanceOptions object as a required argument. It's required to provide the ID of the app instance to stop:

const options = {
    // It's required to provide the ID of the app instance.
    id: "my-instance-id",
    // Specifying a reason for stopping the instance.
    reason: "Instance stopped by user."
};

await io.apps.instances.stop(options);

Restart

To restart an app instance, use the restart() method and provide a RestartInstanceOptions object as a required argument. It's required to provide the ID of the app instance to restart:

const options = {
    // It's required to provide the ID of the app instance.
    id: "my-instance-id",
    // Use this property to pass new context to the restarted instance or clear its existing context.
    // By default, the existing context will be preserved when restarting an instance.
    context: "clear"
};

await io.apps.instances.restart(options);

console.log(`Instance with ID ${myInstance.id} was restarted.`);

Context

App instances can be assigned context data. This can be any custom data that the instance may use at runtime. To specify context data for an app instance, use:

  • The "context" property of the "details" top-level key in the app definition. The context provided in the app definition is the default context for all started instances of that app and can be overridden dynamically.

  • The context property of the StartAppOptions or the RestartInstanceOptions objects when starting or restarting instances respectively. Providing context dynamically will override the default context specified in the app definition.

To retrieve the context of an app instance, use the getContext() method and provide an InstanceSelect object as a required argument. It's required to provide the ID of the app instance whose context to retrieve:

const options = {
    // It's required to provide the ID of the app instance.
    id: "my-instance-id"
};

const context = await io.apps.instances.getContext(options);

State

All app instances are assigned an InstanceState describing their current state - started, ready, failed, or stopped.

To retrieve the current instance state, use the getState() method and provide an InstanceSelect object as a required argument. It's required to provide the ID of the app instance whose state to retrieve:

const options = {
    // It's required to provide the ID of the app instance.
    id: "my-instance-id"
};

const state = await io.apps.instances.getState(options);

Events

The io.apps.instances object provides methods for handling events related to the app instances running within the io.Connect environment. All methods resolve with an unsubscribe function which you can use to unsubscribe from the respective events.

Instance Started

To get notified when an app instance has been started, use the onStarted() method and provide a handler for the event. The handler will receive an InstanceStartedEvent object as an argument:

const handler = ({ instance }) => console.log(`App instance with ID ${instance.id} was started.`);

const unsubscribe = io.apps.instances.onStarted(handler);

⚠️ Note that the onStarted() method will be invoked immediately for all already running app instances, as well as for any newly started ones later. This behavior is designed to avoid potential race conditions that may cause your app to miss a notification about a started app instance.

Instance Stopped

To get notified when an app instance has been stopped, use the onStopped() method and provide a handler for the event. The handler will receive an InstanceStoppedEvent object as an argument:

const handler = ({ instance, reason }) => console.log(`App instance with ID ${instance.id} was stopped. Reason: ${reason}`);

const unsubscribe = io.apps.instances.onStopped(handler);

Instance State Changed

To get notified when the state of an app instance has changed, use the onStateChanged() method and provide a handler for the event. The handler will receive an InstanceStateChangedEvent object as an argument:

const handler = ({ instance, state }) => console.log(`App instance with ID ${instance.id} has new state: ${state}`);

const unsubscribe = io.apps.instances.onStateChanged(handler);

Current App Instance

The My object of the Apps API accessible via io.apps.my provides access to the current app instance.

App Name

To retrieve the name of the app to which the instance belongs, use the appName property of the My object:

const { appName } = io.apps.my;

console.log(`Current instance belongs to app "${appName}".`);

App Instance Object

To retrieve the ApplicationInstance object describing the current app instance, use the instance property of the My object:

const { instance } = io.apps.my;

console.log(`Current instance ID: ${instance.id}`);

API Reference

For a complete list of the available Apps API methods and properties, see the Apps API reference documentation.