FDC3 Compliance
Overview
FDC3 aims at developing specific protocols and classifications in order to advance the ability of desktop apps in financial workflows to interoperate in a plug-and-play fashion without prior bilateral agreements.
FDC3 for io.Connect Desktop
io.Connect Desktop supports fully the FDC3 2.0 standards.
The following sections explain the specifics for running FDC3-compliant apps within io.Connect Desktop and using the @interopio/fdc3
and the io.Connect .NET FDC3 libraries which provide io.Connect implementations of the FDC3 standards. For more detailed information on the FDC3 standards and APIs, see the FDC3 documentation.
FDC3 Implementations
Implementations of the FDC3 API are available for:
- JavaScript -
@interopio/fdc3
package available on NPM; - .NET - packages for .NET Framework and .NET 5+ available on NuGet;
JavaScript
The @interopio/fdc3
library provides an io.Connect implementation of the FDC3 standards and can be either automatically injected in your app, or installed and imported manually. In either case, it's important to consider that the io.Connect FDC3 API implementation depends on the @interopio/desktop
library which must be initialized with the appropriate settings in order for the @interopio/fdc3
library to function properly.
Direct Usage
To manually install the @interopio/fdc3
library, execute the following command:
npm install @interopio/fdc3
Reference the library in your app:
import "@interopio/fdc3";
You can also reference it as a script from UNPKG:
<script src="https://unpkg.com/@interopio/fdc3@latest/dist/fdc3.umd.js"></script>
Initialize the @interopio/desktop
library with the following configuration:
import IODesktop from "@interopio/desktop";
import "@interopio/fdc3";
const config = {
// This is needed for the default Intents Resolver UI app to work properly.
appManager: "full",
// This is necessary in order to use the FDC3 User Channels.
channels: true
};
await IODesktop(config);
⚠️ Note that the
@interopio/desktop
library can also be auto injected and auto initialized through system configuration or app definition. For more details, see the How to... > Interop-Enable Your Apps > JavaScript section.
Auto Injection
io.Connect Desktop can auto inject the @interopio/fdc3
library in FDC3 apps retrieved from any supported app store: local, remote, in-memory, or io.Manager.
When auto injecting the @interopio/fdc3
library, it isn't necessary to install, auto inject or initialize the @interopio/desktop
library, on which it depends, as io.Connect Desktop does this automatically without the need for explicit configuration.
To specify global settings for auto injecting the @interopio/fdc3
library, use the "autoInjectFdc3"
property under the "windows"
top-level key in the system.json
system configuration of io.Connect Desktop located in %LocalAppData%/interop.io/io.Connect Desktop/Desktop/config
.
The following example demonstrates how to specify settings for injecting the @interopio/fdc3
library for all apps:
{
"windows": {
"autoInjectFdc3": {
"enabled": true,
"library": {
"source": "https://my-fdc3-library/umd.min.js",
"fallback": "2.*",
"timeout": 5000
}
}
}
}
The "autoInjectFdc3"
object has the following properties:
Property | Type | Description |
---|---|---|
"allowed" |
string[] |
List of io.Connect app names in which the @interopio/fdc3 library will be injected. |
"blocked" |
string[] |
List of io.Connect app names in which the @interopio/fdc3 library won't be injected. |
"enabled" |
boolean |
Required. If true , will enable auto injecting the @interopio/fdc3 library. |
"library" |
object |
Settings for the @interopio/fdc3 library to be injected. |
The "library"
object has the following properties:
Property | Type | Description |
---|---|---|
"fallback" |
string |
Fallback location of the @interopio/fdc3 library in case the one specified in the "source" property doesn't load. Accepts a URL pointing to a locally or remotely hosted @interopio/fdc3 library, or a string specifying the version of the @interopio/fdc3 library to be used from the locally available packages in the %LocalAppData%/interop.io/io.Connect Desktop/Desktop/assets folder. |
"source" |
string |
URL pointing to a locally or remotely hosted @interopio/fdc3 library, or a string specifying the version of the @interopio/fdc3 library to be used from the locally available packages in the %LocalAppData%/interop.io/io.Connect Desktop/Desktop/assets folder. |
"timeout" |
number |
Interval in milliseconds to wait for loading the @interopio/fdc3 library. |
You can override the settings for injecting the @interopio/fdc3
library per app from the io.Connect app definition by using the "autoInjectFdc3"
property of the "details"
top-level key.
The following example demonstrates how to disable injecting the @interopio/fdc3
for an app:
{
"title": "My Non-FDC3 App",
"type": "window",
"name": "my-non-fdc3-app",
"details": {
"url": "https://my-non-fdc3-app.com",
"autoInjectFdc3": {
"enabled": false
}
}
}
If you already have an FDC3 definition for your app, you can use the "ioConnect"
property of the "hostManifests"
top-level key to provide settings for auto injecting the library.
The following example demonstrates how to enable injecting the @interopio/fdc3
library using an FDC3 app definition:
{
"appId": "my-app",
"title": "My App",
"type": "web",
"details": {
"url": "https://example.com/my-app"
},
"hostManifests": {
"ioConnect": {
"autoInjectFdc3": {
"enabled": true,
"library": {
"source": "https://my-fdc3-library/umd.min.js",
"fallback": "2.*",
"timeout": 5000
}
}
}
}
}
After the library has been imported or injected, the FDC3 API will be available as an fdc3
object injected in the global window
object:
const contextType = "Contact";
const handler = context => console.log(`Context: ${JSON.stringify(context)}`);
await fdc3.addContextListener(contextType, handler);
.NET
Available since io.Connect Desktop 9.3
io.Connect .NET implementations of the FDC3 standards are available for .NET Standard and .NET Framework on NuGet.
Both .NET FDC3 libraries depend on the respective io.Connect .NET library, provide identical APIs that strictly follow the FDC3 standards, and are initialized in the same way.
⚠️ Note that the .NET FDC3 library for .NET Standard depends on the io.Connect .NET library for .NET Standard which is meant to provide cross-platform support (e.g., for Android, Linux, Blazor) and doesn't work with window handles. This means that if you want to register you .NET FDC3 apps as io.Connect Windows, you must also include the io.Connect .NET library for .NET 5+ as a dependency in your project.
Initialization
To initialize the .NET FDC3 libraries, invoke the DesktopAgent()
constructor which accepts as a required argument an initialized instance of the io.Connect .NET library. The following example demonstrates basic initialization of the .NET FDC3 library:
// Initialize the io.Connect .NET library or provide an already initialized instance.
Glue42 io = await Glue42.InitializeGlue();
// Creating a Desktop Agent.
DesktopAgent desktopAgent = new DesktopAgent(io);
⚠️ Note that both io.Connect .NET FDC3 implementations follow strictly the FDC3 API standards. For examples on using the FDC3 APIs, see the FDC3 official documentation.
Optional Configuration
When initializing the io.Connect .NET FDC3 libraries, you can provide an optional configuration for your Desktop Agent. This configuration is outside the scope of the FDC3 standards and its purpose is to enable finer tuning of the library behavior. You can specify a Dispatcher
to be used, whether your app will receive its own context updates, and you can also load custom contexts that aren't defined in the FDC3 standards:
// Defining a custom context that must inherit the necessary library classes.
public class NewCustomFDC3Context :
Finos.Fdc3.Context.Context<NewCustomFDC3Context.CustomID>,
IContext,
IContext<object>,
IIntentResult,
IDynamicContext
{
public class CustomID
{
public string? PROP1 { get; set; }
public string? PROP2 { get; set; }
public string? PROP3 { get; set; }
}
public NewCustomFDC3Context(CustomID? id = null, string? name = null)
: base("fdc3.new.custom", id, name)
{
}
object? IContext<object>.ID => (object)this.ID;
}
// Initialize the .NET io.Connect library or provide an already initialized instance.
Glue42 io = await Glue42.InitializeGlue();
// Optional Desktop Agent configuration.
var configuration = new DesktopAgent.Configuration
{
// Providing a `Dispatcher`.
Dispatcher = new GlueDispatcher(Dispatcher.CurrentDispatcher),
// Enabling your app to receive its own context updates.
HearOwnBroadcasts = true,
// Describe how your custom context is loaded.
ContextLoader = loader =>
{
loader.AddContextLoader<NewCustomFDC3Context>((prop, value) =>
{
// You can add custom logic based on the raw value.
// Creating an instance of the context using the property loader to avoid typos.
// The rest of the properties will be loaded automatically using reflection.
return new NewCustomFDC3Context(prop.Load(c => c.ID), prop.Load(c => c.Name));
// Your custom FDC3 context type.
}, "fdc3.new.custom");
}
};
DesktopAgent desktopAgent = new DesktopAgent(io, configuration);
Channel Mapping
All default io.Connect Channels are mapped to the default FDC3 User Channels. This allows you to instrument your interop-enabled .NET app to join or leave FDC3 User Channels when the io.Connect Channel is changed programmatically or manually from the Channel Selector. Once you have created a Desktop Agent, use the Channel changed event provided by the io.Connect .NET library to determine when to join or leave an FDC3 User Channel:
public IDisposable LinkChannelChanges(IGlueWindow window)
{
// Use the Channel changed event.
return window.ChannelContext.Subscribe(new LambdaGlueChannelEventHandler(null, channelChanged: async (context, channel) =>
{
if (channel == null)
{
// If there is no current io.Connect Channel, leave the current FDC3 User Channel.
await LeaveCurrentChannel();
}
else
{
// Join the respective FDC3 User Channel when the window joins an io.Connect Channel.
await JoinUserChannel(context.GetValue<string>("fdc3.id", ChannelLevel.Meta));
}
}));
}
App Definition
To use your FDC3-compliant app in io.Connect Desktop, you must either create an io.Connect app definition file for it, or place your already existing FDC3 app definition in any io.Connect Desktop app store (local, remote, in-memory, or io.Manager).
io.Connect Definitions
The following example demonstrates a minimal io.Connect definition for an interop-enabled app:
{
"name": "my-fdc3-app",
"type": "window",
"title": "My FDC3 App",
"details": {
"url": "https://my-fdc3-app.com"
}
}
The "name"
, "type"
and "url"
properties are required and "type"
must be set to "window"
. The "url"
property points to the location of the web app.
For more information on configuring your apps for io.Connect Desktop, see the Developers > Configuration > Application section.
FDC3 Definitions
io.Connect Desktop supports the FDC3 app definition standard (App Directory or AppD). FDC3 definitions are automatically converted to io.Connect ones, preserving the original FDC3 app definition in a top-level "fdc3"
property added to the resulting io.Connect app definition. You can add FDC3 definitions from any supported io.Connect Desktop app store: local, remote, in-memory, or io.Manager.
If you already have an FDC3 definition for your app, you can use the "ioConnect"
property of the "hostManifests"
top-level key to provide any app settings specific to the io.Connect framework that you want to employ:
{
"appId": "my-app",
"title": "My App",
"type": "web",
"details": {
"url": "https://example.com/my-app"
},
"hostManifests": {
"ioConnect": {
"name": "my-app",
"type": "window",
"details": {
"url": "https://example.com/my-app",
},
"customProperties": {
"includeInWorkspaces": true
}
}
}
}
For more details on the FDC3 app definition standards, see the FDC3 Application schema.
App Directory
The goal of the FDC3 App Directory (or AppD) REST service is to provide trusted identity for apps. App definitions are provided by one or more App Directory REST services where user entitlements and security can also be handled.
io.Connect Desktop supports the FDC3 app definition standard. FDC3 definitions are automatically converted to io.Connect ones, preserving the original FDC3 app definition in a top-level "fdc3"
property added to the resulting io.Connect app definition. You can add FDC3 definitions from any supported io.Connect Desktop app store: local, remote, in-memory, or io.Manager.
For more details on the FDC3 app definition standards, see the FDC3 Application schema.
The following example demonstrates how to configure io.Connect Desktop to retrieve FDC3 app definitions from a remote app store. Add a new entry to the "appStores"
top-level key in the system.json
system configuration file of io.Connect Desktop located in the %LocalAppData%/interop.io/io.Connect Desktop/Desktop/config
folder:
{
"appStores": [
{
"type": "rest",
"details": {
"url": "https://example.com/my-fdc3-app-store/",
"auth": "no-auth",
"pollInterval": 30000,
"enablePersistentCache": true,
"cacheFolder": "%LocalAppData%/interop.io/io.Connect Desktop/UserData/%GLUE-ENV%-%GLUE-REGION%/configCache/"
}
}
]
}
For all supported app stores, you can use the "appDefinitionOverrides"
property in the respective app store configuration object to override all definitions of apps of the specified type. This property may be useful if your app definitions (io.Connect or FDC3 ones) don't have settings for auto injecting the @interopio/fdc3
library. You can add these settings to all apps in the store without having to manually update all their definitions.
The following example demonstrates how to enable auto injecting the @interopio/fdc3
library for all apps of type "window"
fetched from a remote app store:
{
"appStores": [
{
"type": "rest",
"details": {
"url": "https://example.com/my-app-store/",
"auth": "no-auth",
"pollInterval": 30000,
"enablePersistentCache": true,
"cacheFolder": "%LocalAppData%/interop.io/io.Connect Desktop/UserData/%GLUE-ENV%-%GLUE-REGION%/configCache/"
},
"appDefinitionOverrides": {
// Overrides for the properties under the `"details"` top-level key for apps of type `"window"`.
"window": {
// Settings for auto injecting the `@interopio/fdc3` library.
"autoInjectFdc3": {
"enabled": true,
"library": {
"source": "https://my-fdc3-library/umd.js",
"fallback": "2.*",
"timeout": 5000
}
}
}
}
}
]
}
For more details on overriding app definitions and configuring all supported app stores, see Capabilities > App Management > Overview > App Stores.
According to the FDC3 App Directory specifications, the remote store must return app definitions in the following response shape:
{
"applications": [
// List of app definition objects.
{}, {}
]
}
For a reference implementation of the FDC3 App Directory, see the Node.js REST Config Example on GitHub.
For more details on using App Directory, see the FDC3 App Directory documentation.
Intents
The FDC3 Intents concept serves the purpose of enabling the creation of cross-app workflows on the desktop. An Intent specifies what action the app can execute and with what data structure it can work. An app declares itself as an Intent handler through configuration or at runtime. Other apps can raise this Intent and provide context data for handling the Intent to the Intent handler app.
Intents can be defined both in the "intents"
top-level key of an io.Connect app definition, or in the "intents"
property of the "interop"
top-level key in an FDC3 app definition.
The following example demonstrates how to define an Intent in an io.Connect app definition:
{
"intents": [
{
"name": "ShowChart",
"displayName": "Instrument Chart",
"contexts": ["Instrument"]
}
]
}
Property | Type | Description |
---|---|---|
"name" |
string |
Required. The name of the Intent. |
"displayName" |
string |
The display name of the Intent. Can be used in UI elements to visualize the Intent. |
"contexts" |
string[] |
The type of predefined data structures with which the app can work (see FDC3 Contexts). |
The Intents Resolver UI app allows users to choose an app for handling a raised Intent:
For more details on how to enable or disable the Intents Resolver UI, or on how to create your own custom Intents Resolver App, see the Capabilities > Intents > Overview > Intents Resolver section.
For more details on using Intents, see the FDC3 Intents API.
Channels
The io.Connect Channels correspond to the FDC3 User Channels. Channels are well-known named context objects that can be represented with colors and names in the UI of an app. They can be controlled programmatically or manually by end users directly from the app UI. This allows users to switch apps from one Channel to another, changing the context data with which they operate. All apps on the same Channel have access to the Channel context and can read or update the data in it as necessary.
All default system Channels in io.Connect Desktop are defined in the channels.json
file located in the %LocalAppData%/interop.io/io.Connect Desktop/Desktop/config
folder. There you can add as many custom Channels as you need. For instance, to add a black Channel to the existing list of system Channels, add the following configuration:
{
"name": "Black",
"meta": {
"color": "black"
}
}
The default io.Connect Channels are mapped to the default FDC3 User Channels. Any custom Channel you define, must be mapped to an FDC3 User Channel if you want to use it as such.
For more details on defining Channels, Channel mapping, and how to enable the Channel Selector for your apps, see the Developers > Configuration > Channels section.
To be able to use the FDC3 User Channels, the @interopio/desktop
library (on which the @interopio/fdc3
library depends) must be initialized with enabled io.Connect Channels.
For more details on using User Channels, see the FDC3 User Channels API.
Using FDC3 Contexts in Non-FDC3 Apps
Available since io.Connect Desktop 9.6
If you have non-FDC3 interop-enabled apps, they can still use the contexts of FDC3 User Channels via the io.Connect Channels API in order to retrieve, subscribe for, and publish FDC3 context data.
Retrieving FDC3 Context Data
The ChannelContext
object utilized by the Channels API methods for retrieving Channel contexts (such as get()
, getMy()
and list()
) will contain an fdc3
property in its data
object if FDC3 context data has been published to the Channel. The fdc3
property is an object with a required type
property holding the type of the FDC3 context:
const context = await io.channels.get("Red");
// If FDC3 context data has been published to the Channel, the Channel context will contain a `data.fdc3` property.
if (context.data.fdc3) {
// The `type` property of the `fdc3` object is required.
const contextType = context.data.fdc3.type;
console.log(contextType);
};
The get()
and getMy()
accept an FDC3Options
object as an optional argument. You can use this argument to specify the type of the FDC3 context in which you are interested. The methods will resolve with an empty object if FDC3 context data of the specified type isn't available in the Channel:
// Specifying the type of an FDC3 context.
const fdc3Options = { contextType: "fdc3.contact" };
// Retrieving an FDC3 context of a specific type.
const fdc3Context = await io.channels.getMy(fdc3Options);
Subscribing for FDC3 Context Data
The object argument received by the handler provided to the subscribe()
or subscribeFor()
methods of the Channels API will contain an fdc3
property if FDC3 context data has been published to the Channel to which you have subscribed. The fdc3
property is an object with a required type
property holding the type of the FDC3 context:
// The `data` argument received by the callback will have an `fdc3` property
// if FDC3 context data has been published to the Channel.
const handler = (data) => {
if (data.fdc3) {
// The `type` property of the `fdc3` object is required.
const contextType = data.fdc3.type;
console.log(contextType);
};
};
const unsubscribe = io.channels.subscribe(handler);
The subscribe()
and subscribeFor()
methods accept an FDC3Options
object as an optional argument. You can use this argument to specify the type of the FDC3 context in which you are interested. The callback passed to the subscribe()
and subscribeFor()
methods won't be invoked unless FDC3 context data of the specified type is published in the Channel:
// Specifying the type of an FDC3 context.
const fdc3Options = { contextType: "fdc3.contact" };
// Subscribing to an FDC3 context of a specific type.
const handler = (data) => {
// The `type` property of the `fdc3` object is required.
const contextType = data.fdc3.type;
console.log(contextType);
};
const unsubscribe = io.channels.subscribe(handler, fdc3Options);
Publishing FDC3 Context Data
The publish()
method of the Channels API accepts also a PublishOptions
object as a second optional argument. You can use it to specify the name of the Channel to update and whether the published data is an FDC3 context:
// FDC3 context data to publish.
const data = {
type: "fdc3.contact",
name: "John Doe",
id: {
email: "john.doe@example.com"
}
};
const options = {
name: "Red",
// Specify that the published data is FDC3 context data.
fdc3: true
};
await io.channels.publish(data, options);
Using Multiple Channels
Available since io.Connect Desktop 9.7
io.Connect Desktop provides experimental support for working with multiple Channels simultaneously.
For details on how to enable the multi Channel Selector and the considerations you should take into account when using multiple Channels, see the Capabilities > Channels > Overview section.
The FDC3 standard doesn't provide out-of-the-box support for multiple Channels. To enable FDC3 apps to work with multiple Channels, the io.Connect JavaScript FDC3 implementation has been extended with the following supplementary methods:
// Retrieving all currently joined Channels.
const currentChannels = await fdc3.getCurrentChannels();
// Leaving a Channel by specified Channel ID.
await fdc3.leave("Red");
// Leaving all currently joined Channels.
await fdc3.leaveCurrentChannels();
The handler passed to the addContextListener()
method receives as a third optional argument a Channel ID. You can use it to determine the Channel from which comes the context update:
const contextType = "Contact";
const handler = (context, metadata, channelId) => {
console.log(`Received updates from Channel ${channelId}.`);
};
await fdc3.addContextListener(contextType, handler);