API Overview (next)
The role of FDC3 API is to establish a baseline interface for interoperability between applications. Because FDC3 is largely an agreement between existing platforms and applications, standards should be optimized for ease of adoption rather than functional completeness. Functionality absent from a FDC3 specification is in no way a commentary on its importance.
The following sections examine the API's use-cases and core concepts. The API is fully defined in both subsequent pages of this Part and a full set of TypeScript definitions in the src directory of the FDC3 GitHub repository.
Components
Desktop Agent
A Desktop Agent is a desktop component (or aggregate of components) that serves as a launcher and message router (broker) for applications in its domain. A Desktop Agent can be connected to one or more App Directories and will use directories for application identity and discovery. Typically, a Desktop Agent will contain the proprietary logic of a given platform, handling functionality like explicit application interop workflows where security, consistency, and implementation requirements are proprietary.
Examples of Desktop Agents include:
- Autobahn
- io.Connect
- OpenFin
- Refinitiv Eikon
An FDC3-compliant Desktop Agent exposes an FDC3 Standard API to applications they have launched. When an App is launched by a Desktop Agent and is given access to the Agent's API to interoperate, it is running in that Desktop Agent's context.
Application
An application is any endpoint on the desktop that is:
- Registered with/known by a Desktop Agent
- Launchable by a Desktop Agent
- Addressable by a Desktop Agent
Examples of endpoints include:
- Native Applications
- Web Applications
- Headless “services” running on the desktop
Desktop Agent Implementation
The FDC3 API specification consists of interfaces. It is expected that each Desktop Agent will implement these interfaces. A Desktop Agent MUST provide implementations for the following interfaces:
Other interfaces defined in the spec are not critical to define as concrete types. Rather, the Desktop Agent should expect to have objects of the interface shape passed into or out of their library.
Implementation language
FDC3 and the Desktop Agent API it defines are intended to be independent of particular programming languages and platforms and hence the original definitions, produced in TypeScript, may be translated into other languages. However, this also places limitations on the API definitions as they need to be widely implementable in other languages.
Specifically, the use of 'unions' of primitive values in API type and metadata objects, or function parameters SHOULD be avoided as they often cannot be replicated in other languages. Unions of more complex types (such as specific Context Types) may be used where a suitable interface is available or can be created to allow the required polymorphism in languages other than TypeScript.
For implementation details relating to particular languages, and how to access the API in those languages, please see Supported Platforms.
Standards vs. Implementation
The surface area of FDC3 standardization (shown in white above) itself is quite small in comparison to the extent of a typical desktop agent implementation (in grey).
For example:
- workspace management
- user identity and SSO
- entitlements
- UX of application resolution
Are all areas of functionality that any feature-complete desktop agent would implement, but are not currently areas considered for standardization under FDC3.
Inter-Agent Communication
A goal of the FDC3 Standard is that applications running in different Desktop Agent contexts on the same desktop, or operated by the same user, would be able to interoperate and that one Desktop Agent context would be able to discover and launch an application in another Desktop Application context. As Desktop Agent interop is supported by common standards for APIs an app in one Desktop Agent context would not need to know a different syntax to launch or interact with an app in another Desktop Agent context.
Inter-agent communication at the API layer may be achieved via the Desktop Agent Bridging Part of the FDC3 Standard (@experimental), which defines an independent service that Desktop Agents may connect to, and a protocol for the exchange of messages relating to FDC3 API calls. Hence, by implementing support for Desktop Agent Bridging, a platform may extend interop across applications running in multiple Desktop Agent contexts.
Desktop Agent Bridging provides message exchanges and a workflow for performing intent resolution across multiple agents. Hence, app discovery is supported across the agents connected to the bridge for intent-based workflows. Further, as channels are also supported by bridging, context sharing also works across multiple agents.
There is currently no method of discovering all the apps supported by a Desktop Agent in the FDC3 API nor bridging. Hence, to support launching, via fdc3.open
across the connected Desktop Agents, application details must be known in advance. This may be achieved by connecting Desktop Agents to the same App Directories.
Desktop Agent API Standard Compliance
An FDC3 Standard compliant Desktop Agent implementation MUST:
- Provide the FDC3 API to web applications via a global accessible as
window.fdc3
. - Provide a global
fdc3Ready
event to web applications that is fired when the API is ready for use. - Provide a method of resolving ambiguous intents (i.e. those that might be resolved by multiple applications) or unspecified intents (calls to
raiseIntentForContext
that return multiple options), such as a resolver UI.- Intent resolution MUST take into account any specified input or return context types
- Requests for resolution to apps returning a channel MUST include any apps that are registered as returning a channel with a specific type.
- Return (JavaScript or platform appropriate) Error Objects with messages from the
ChannelError
,OpenError
,ResolveError
andResultError
enumerations as appropriate. - Accept as input and return as output data structures that are compatible with the interfaces defined in this Standard.
- Include implementations of the following Desktop Agent API functions, as defined in this Standard:
- Provide an ID for each
PrivateChannel
created viacreatePrivateChannel
and prevent them from being retrieved viagetOrCreateChannel
by ID. - Only require app directories that they connect to to have implemented only the minimum requirements specified in the App Directory API Part of this Standard.
- Provide details of whether they implement optional features of the Desktop Agent API in the
optionalFeatures
property of theImplementationMetadata
object returned by thefdc3.getInfo()
function. - Allow, by default, at least a 15 second timeout for an application, launched via
fdc3.open
,fdc3.raiseIntent
orfdc3.raiseIntentForContext
to add any context listener (viafdc3.addContextListener
) or intent listener (viafdc3.addIntentListener
) necessary to deliver context or intent and context to it on launch. This timeout only applies to listeners needed to receive context on launch; further intent and context listeners not required on launch MAY be added later.
An FDC3 Standard compliant Desktop Agent implementation SHOULD:
- Support connection to one or more App Directories meeting the FDC3 App Directory Standard.
- Qualify
appId
values received from an app directory with the hostname of the app directory server (e.g.myAppId@name.domain.com
) as defined in the app directory standard. - Allow applications to register an
IntentHandler
for particular Intent and Context type pairs by providinginterop.intents.listensFor
metadata in their AppD record. - Adopt the recommended set of User channel definitions.
- Ensure that context messages broadcast by an application on a channel are not delivered back to that same application if they are joined to the channel.
- Make metadata about each context message or intent and context message received (including the app that originated the message) available to the receiving application.
- Prevent external apps from listening or publishing on a
PrivateChannel
that they did not request or provide. - Enforce compliance with the expected behavior of intents (where Intents specify a contract that is enforceable by schema, for example, return object types) and return an error if the interface is not met.
An FDC3 Standard compliant Desktop Agent implementation MAY:
- Make the Desktop Agent API available through modules, imports, or other means.
- Support multiple routes for registration of an
IntentHandler
by an app to be considered during Intent resolution, including dynamic registration of apps at runtime. - Implement the following OPTIONAL Desktop Agent API functions:
- Implement the following deprecated API functions:
addContextListener
(without a contextType argument)getSystemChannels
(renamed getUserChannels)joinChannel
(renamed joinUserChannel)open
(deprecated version that addresses apps vianame
field)raiseIntent
(deprecated version that addresses apps vianame
field)raiseIntentForContext
(deprecated version that addresses apps vianame
field)
For more details on FDC3 Standards compliance (including the versioning, deprecation and experimental features policies) please see the FDC3 Compliance page.
Functional Use Cases
Open an Application
Linking from one application to another is a critical basic workflow that the web revolutionized via the hyperlink. Supporting semantic addressing of applications across different technologies and platform domains greatly reduces friction in linking different applications into a single workflow.
Requesting Functionality From Another App
Often, we want to link from one app to another to dynamically create a workflow. Enabling this without requiring prior knowledge between apps is a key goal of FDC3 and is implemented via the raising of intents, which represent a desired action, to be performed with a context supplied as input.
Intents provide a way for an app to request functionality from another app and defer the discovery and launching of the destination app to the Desktop Agent. There are multiple models for interop that intents can support.
- Chain: In this case the workflow is completely handed off from one app to another (similar to linking). Currently, this is the primary focus in FDC3.
- Client-Service: A Client invokes a Service via the Intent, the Service performs some function, then passes the workflow back to the Client. Typically, there is a data payload type associated with this intent that is published as the standard contract for the intent.
- Remote API: An app wants to remote an entire API that it owns to another App. In this case, the API for the App cannot be standardized. However, the FDC3 API can address how an App connects to another App in order to get access to a proprietary API.
Send/broadcast Context
On the financial desktop, applications often want to broadcast context to any number of applications. Context sharing needs to support different groupings of applications, which is supported via the concept of 'channels', over which context is broadcast and received by other applications listening to the channel.
In some cases, an application may want to communicate with a single application or service and to prevent other applications from participating in the communication. For single transactions, this can instead be implemented via a raised intent, which will be delivered to a single application that can, optionally, respond with data. Alternatively, it may instead respond with a Channel
or PrivateChannel
over which a stream of responses or a dialog can be supported.
Retrieve Metadata about the Desktop Agent implementation
An application may wish to retrieve information about the version of the FDC3 Standard supported by a Desktop Agent implementation and the name of the implementation provider.
Since version 1.2 of the FDC3 Standard it may do so via the fdc3.getInfo()
function. The metadata returned can be used, for example, to vary the behavior of an application based on the version supported by the Desktop Agent, e.g.:
- TypeScript/JavaScript
- .NET
import {compareVersionNumbers, versionIsAtLeast} from '@finos/fdc3';
if (fdc3.getInfo && versionIsAtLeast(await fdc3.getInfo(), '1.2')) {
await fdc3.raiseIntentForContext(context);
} else {
await fdc3.raiseIntent('ViewChart', context);
}
var version = (await _desktopAgent.GetInfo()).Fdc3Version;
The ImplementationMetadata
object returned also includes the metadata for the calling application, according to the Desktop Agent. This allows the application to retrieve its own appId
, instanceId
and other details, e.g.:
- TypeScript/JavaScript
- .NET
let implementationMetadata = await fdc3.getInfo();
let {appId, instanceId} = implementationMetadata.appMetadata;
var implementationMetadata = await _desktopAgent.GetInfo();
var appId = implementationMetadata.AppMetadata.AppId;
var instanceId = implementationMetadata.AppMetadata.InstanceId;
Reference apps or app instance(s) and retrieve their metadata
To construct workflows between applications, you need to be able to reference specific applications and instances of those applications.
From version 2.0 of the FDC3 Standard, Desktop Agent functions that reference or return information about other applications do so via an AppIdentifier
type. AppIdentifier
references specific applications via an appId
from an App Directory record and instances of that application via an instanceId
assigned by the Desktop Agent.
Additional metadata for an application can be retrieved via the fdc3.getAppMetadata(appIdentifier)
function, which returns an AppMetadata
object. The additional metadata may include a title, description, icons, etc., which may be used for display purposes.
Identifiers for instances of an application may be retrieved via the fdc3.findInstances(appIdentifier)
function.
Raising Intents
Raising an Intent is a method for an application to request functionality from another application and, if desired, defer the discovery and launching of the destination app to the Desktop Agent.
Intents and Context
When raising an intent a specific context is provided as input. The type of the provided context may determine which applications can resolve the intent.
A context type may also be associated with multiple intents. For example, an fdc3.instrument
could be associated with ViewChart
, ViewNews
, ViewAnalysis
or other intents.
To raise an intent without a context, use the fdc3.nothing
context type. This type exists so that applications can explicitly declare that they support raising an intent without a context (when registering an IntentHandler
or in an App Directory).
As an alternative to raising a specific intent, you may also raise an unspecified intent with a known context allowing the Desktop Agent or the user (if the intent is ambiguous) to select the appropriate intent and then to raise it with the specified context for resolution.
Intent Results
An optional IntentResult
may also be returned as output by an application handling an intent. Results may be a single Context
object, a Channel
that may be used to send a stream of responses, or void
(no result). The PrivateChannel
type is provided to support synchronization of data transmitted over returned channels, by allowing both parties to listen for events denoting subscription and unsubscription from the returned channel. PrivateChannels
are only retrievable via raising an intent.
For example, an application handling a CreateOrder
intent might return a context representing the order and including an ID, allowing the application that raised the intent to make further calls using that ID.
An optional result type is also supported when programmatically resolving an intent via findIntent
or findIntentByContext
.
Resolvers
Successful delivery of an intent depends first upon the Desktop Agent's ability to "resolve the intent" (i.e. map the intent to a specific App instance). Where the target application is ambiguous (because there is more than one application that could resolve the intent and context) Desktop Agents may resolve intents by any suitable methodology. A common method is to display a UI that allows the user to pick the desired App from a list of those that will accept the intent and context. Alternatively, the app issuing the intent may proactively handle resolution by calling findIntent
or findIntentByContext
and then raise the intent with a specific target application, e.g.:
- TypeScript/JavaScript
- .NET
// Find apps to resolve an intent to start a chat with a given contact
const appIntent = await fdc3.findIntent("StartChat", context);
// use the returned AppIntent object to target one of the returned
// chat apps or app instances using the AppMetadata object
await fdc3.raiseIntent("StartChat", context, appIntent.apps[0]);
//Find apps to resolve an intent and return a specified context type
const appIntent = await fdc3.findIntent("ViewContact", context, "fdc3.contact");
try {
const resolution = await fdc3.raiseIntent(appIntent.intent, context, appIntent.apps[0].name);
const result = await resolution.getResult();
console.log(`${resolution.source} returned ${JSON.stringify(result)}`);
} catch(error) {
console.error(`${resolution.source} returned a result error: ${error}`);
}
//Find apps to resolve an intent and return a channel
const appIntent = await fdc3.findIntent("QuoteStream", context, "channel");
try {
const resolution = await fdc3.raiseIntent(appIntent.intent, context, appIntent.apps[0].name);
const result = await resolution.getResult();
if (result && result.addContextListener) {
result.addContextListener(null, (context) => {
console.log(`received context: ${JSON.stringify(context)}`);
});
} else {
console.log(`${resolution.source} didn't return a channel! Result: ${JSON.stringify(result)}`);
}
} catch(error) {
console.error(`${resolution.source} returned a result error: ${error}`);
}
//Find apps that can perform any intent with the specified context
const appIntents = await fdc3.findIntentByContext(context);
//use the returned AppIntent array to target one of the returned apps
await fdc3.raiseIntent(appIntent[0].intent, context, appIntent[0].apps[0]);
// Find apps to resolve an intent to start a chat with a given contact
var appIntent = await _desktopAgent.FindIntent("StartChat", context);
// use the returned AppIntent object to target one of the returned
// chat apps or app instances using the AppMetadata object
await _desktopAgent.RaiseIntent("StartChat", context, appIntent.Apps.First());
//Find apps to resolve an intent and return a specified context type
var appIntent = await _desktopAgent.FindIntent("ViewContact", context, "fdc3.contact");
var resolution = await _desktopAgent.RaiseIntent(appIntent.Intent.Name, context, appIntent.Apps.First());
try
{
var result = await resolution.GetResult();
System.Diagnostics.Debug.WriteLine($"{resolution.Source} returned ${result}");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"{resolution.Source} returned an error");
}
//Find apps to resolve an intent and return a channel
var appIntent = await _desktopAgent.FindIntent("QuoteStream", context, "channel");
try
{
var resolution = await _desktopAgent.RaiseIntent(appIntent.Intent.Name, context, appIntent.Apps.First());
var result = await resolution.GetResult();
if (result is IChannel resolvedChannel)
{
await resolvedChannel.AddContextListener<IContext>(null, (context, metadata) => { });
}
else
{
System.Diagnostics.Debug.WriteLine("Did not return a channel");
}
}
catch (Exception ex)
{
}
//Find apps that can perform any intent with the specified context
var appIntents = await _desktopAgent.FindIntentsByContext(context);
//use the returned AppIntent array to target one of the returned apps
await _desktopAgent.RaiseIntent(appIntents.First().Intent.Name, context, appIntents.First().Apps.First());
Result context types requested are represented by their type name. A channel may be requested by passing the string "channel"
or a channel that returns a specific type via the syntax "channel<contextType>"
, e.g. "channel<fdc3.instrument>"
. Requesting intent resolution to an app returning a channel MUST include apps that are registered as returning a channel with a specific type.
Intent Resolution
Raising an intent will return a Promise-type object that will resolve/reject based on a number of factors.
Resolve
- Intent was resolved unambiguously and the receiving app was launched successfully (if necessary).
- Intent was ambiguous, a resolution was chosen by the end user, and the chosen application was launched successfully.
Reject
- No app matching the intent and context (if specified) was found.
- A match was found, but the receiving app failed to launch.
- The intent was ambiguous and the resolver experienced an error.
Resolution Object
If the raising of the intent resolves (or rejects), a standard IntentResolution
object will be passed into the resolver function with details of the application that resolved the intent and the means to access any results subsequently returned.
For example, to raise a specific intent:
- TypeScript/JavaScript
- .NET
try {
const resolution = await fdc3.raiseIntent('StageOrder', context);
}
catch (err){ ... }
try
{
var resolution = await _desktopAgent.RaiseIntent("StageOrder", context);
}
catch (Exception ex) { }
or to raise an unspecified intent for a specific context, where the user may select an intent from a resolver dialog:
- TypeScript/JavaScript
- .NET
try {
const resolution = await fdc3.raiseIntentForContext(context);
if (resolution.data) {
const orderId = resolution.data.id;
}
}
catch (err){ ... }
try
{
var resolution = await _desktopAgent.RaiseIntentForContext(context);
if (resolution is IContext resolvedContext)
{
var orderId = resolvedContext.ID;
}
}
catch (Exception ex) { }
Use metadata about the resolving app instance to target a further intent
- TypeScript/JavaScript
- .NET
try {
const resolution = await fdc3.raiseIntent('StageOrder', context);
...
//some time later
await agent.raiseIntent("UpdateOrder", context, resolution.source);
}
catch (err) { ... }
try
{
var resolution = await _desktopAgent.RaiseIntent("StageOrder", context);
//some time later
await _desktopAgent.RaiseIntent("UpdateOrder", context, resolution.Source);
}
catch (Exception ex) { }
Raise an intent and retrieve either data or a channel from the IntentResolution:
- TypeScript/JavaScript
- .NET
let resolution = await agent.raiseIntent("intentName", context);
try {
const result = await resolution.getResult();
/* Detect whether the result is Context or a Channel by checking for properties unique to Channels. */
if (result && result.broadcast) {
console.log(`${resolution.source} returned a channel with id ${result.id}`);
} else if (result){
console.log(`${resolution.source} returned data: ${JSON.stringify(result)}`);
} else {
console.error(`${resolution.source} didn't return anything`);
}
} catch(error) {
console.error(`${resolution.source} returned a data error: ${error}`);
}
var resolution = await _desktopAgent.RaiseIntent("QuoteStream", new Instrument(new InstrumentID() { Ticker = "AAPL" }));
try
{
var result = await resolution.GetResult();
//check that we got a result and that it's a channel
if (result is IChannel channel)
{
System.Diagnostics.Debug.WriteLine($"{resolution.Source} returned a channel with id {channel.Id}");
}
else if (result is IContext context)
{
System.Diagnostics.Debug.WriteLine($"{resolution.Source} returned data {context}");
}
else
{
System.Diagnostics.Debug.WriteLine($"{resolution.Source} didn't return anything");
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"{resolution.Source} returned an error");
}
Register an Intent Handler
Applications need to let the system know the intents they can support. Typically, this SHOULD be done via registration with an App Directory by providing interop.intents.listensFor
metadata. However, Desktop Agent implementations MAY support dynamic registration of an IntentHandler
by an app at runtime (for example, when they add an IntentListener
via the API) to allow for ad-hoc registration which may be helpful at development time. Although dynamic registration is not part of this specification, a Desktop Agent agent MAY choose to support any number of registration paths.
When an instance of an application is launched, it is expected to add an IntentHandler
function to the Desktop Agent for each intent it has registered by calling the fdc3.addIntentListener
function of the Desktop Agent. Doing so allows the Desktop Agent to pass incoming intents and contexts to that instance of the application. Hence, if the application instance was spawned in response to the raised intent, then the Desktop Agent must wait for the relevant intent listener to be added by that instance before it can deliver the intent and context to it. In order to facilitate accurate error responses, calls to fdc3.raiseIntent
should not return an IntentResolution
until the intent handler has been added and the intent delivered to the target app.
Intent handlers SHOULD be registered via fdc3.addIntentListener
within 15 seconds of the application launch (the minimum timeout Desktop Agents are required to provide) in order to be widely compatible with Desktop Agent implementations. Individual Desktop Agent implementations MAY support longer timeouts or configuration to control or extend timeouts.
Originating App Metadata
Optional metadata about each intent & context message received, including the app that originated the message, SHOULD be provided by the desktop agent implementation to registered intent handlers. As this metadata is optional, apps making use of it MUST handle cases where it is not provided.
Compliance with Intent Standards
Intents represent a contract with expected behavior if an app asserts that it supports the intent. Where this contract is enforceable by schema (for example, return object types), the FDC3 API implementation SHOULD enforce compliance and return an error if the interface is not met.
It is expected that App Directories SHOULD also curate listed apps and ensure that they are complying with declared intents.
Context Channels
Context channels allows a set of apps to share a stateful piece of data between them, and be alerted when it changes. Use cases for channels include color linking between applications to automate the sharing of context and topic based pub/sub such as theme.
Types of Channel
There are three types of channels, which have different visibility and discoverability semantics:
User channels, which:
- facilitate the creation of user-controlled context links between applications (often via the selection of a color channel),
- are created and named by the desktop agent,
- are discoverable (via the
getUserChannels()
API call), - can be 'joined' (via the
joinUserChannel()
API call).
notePrior to FDC3 2.0, 'user' channels were known as 'system' channels. They were renamed in FDC3 2.0 to reflect their intended usage, rather than the fact that they are created by system (which could also create 'app' channels).
noteEarlier versions of FDC3 included the concept of a 'global' system channel which was deprecated in FDC3 1.2 and removed in FDC3 2.0.
App channels, which:
- facilitate developer controlled messaging between applications,
- are created and named by applications (via the
getOrCreateChannel()
API call), - are not discoverable,
- are interacted with via the Channel API (accessed via the desktop agent
getOrCreateChannel
API call)
Private channels, which:
- facilitate private communication between two parties,
- have an auto-generated identity and can only be retrieved via a raised intent.
Channels are interacted with via broadcast
and addContextListener
functions, allowing an application to send and receive Context objects via the channel. For User channels, these functions are provided on the Desktop Agent, e.g. fdc3.broadcast(context)
, and apply to channels joined via fdc3.joinUserChannel
. For App channels, a channel object must be retrieved, via fdc3.getOrCreateChannel(channelName)
, which provides the functions, i.e. myChannel.broadcast(context)
and myChannel.addContextListener(context)
. For PrivateChannels
, a channel object must also be retrieved, but via an intent raised with fdc3.raiseIntent(intent, context)
and returned as an IntentResult
.
Channel implementations SHOULD ensure that context messages broadcast by an application on a channel are not delivered back to that same application if they are also listening on the channel.
Joining User Channels
Apps can join User channels. An app can only be joined to one User channel at a time.
When an app is joined to a User channel, calls to fdc3.broadcast
will be routed to that channel and listeners added through fdc3.addContextListener
will receive context broadcasts from other apps also joined to that channel. If an app is not joined to a User channel fdc3.broadcast
will be a no-op and handler functions added with fdc3.addContextListener
will not receive any broadcasts. However, apps can still choose to listen and broadcast to specific channels (both User and App channels) via the methods on the Channel
class.
When an app joins a User channel, or adds a context listener when already joined to a channel, it will automatically receive the current context for that channel.
It is possible that a call to join a User channel could be rejected. If for example, the desktop agent wanted to implement controls around what data apps can access.
Joining channels in FDC3 is intended to be a behavior initiated by the end user. For example: by color linking or apps being grouped in the same workspace. Most of the time, it is expected that apps will be joined to a channel by mechanisms outside of the app. To support programmatic management of joined channels and the implementation of channel selector UIs other than those provided outside of the app, Desktop Agent implementations MAY provide fdc3.joinChannel()
, fdc3.getCurrentChannel()
and fdc3.leaveCurrentChannel()
functions and if they do, MUST do so as defined in the Desktop Agent API reference.
There SHOULD always be a clear UX indicator of what channel an app is joined to.
Examples
To find a User channel, one calls:
- TypeScript/JavaScript
- .NET
// returns an array of channels
const allChannels = await fdc3.getUserChannels();
const redChannel = allChannels.find(c => c.id === 'red');
var allChannels = await _desktopAgent.GetUserChannels();
var redChannel = allChannels.Single(c => c.Id == "red");
To join a User channel, one calls:
- TypeScript/JavaScript
- .NET
fdc3.joinUserChannel(redChannel.id);
await _desktopAgent.JoinUserChannel(redChannel.Id);
Calling fdc3.broadcast
will now route context to the joined channel.
Channel implementations SHOULD ensure that context messages broadcast by an application on a channel are not delivered back to that same application if they are joined to the channel.
Prior to FDC3 2.0, 'user' channels were known as 'system' channels. They were renamed in FDC3 2.0 to reflect their intended usage, rather than the fact that they are created by system (which could also create 'app' channels). The
joinChannel
function was also renamed tojoinUserChannel
to clarify that it is only intended to be used to join 'user', rather than 'app', channels.
Recommended User Channel Set
Desktop Agent implementations SHOULD use the following set of channels, to enable a consistent user experience across different implementations. Desktop Agent implementation MAY support configuration of the user channels.
Future versions of the FDC3 Standard may support connections between desktop agents, where differing user channel sets may cause user experience issues.
const recommendedChannels = [
{
id: 'fdc3.channel.1',
type: 'user',
displayMetadata: {
name: 'Channel 1',
color: 'red',
glyph: '1',
},
},
{
id: 'fdc3.channel.2',
type: 'user',
displayMetadata: {
name: 'Channel 2',
color: 'orange',
glyph: '2',
},
},
{
id: 'fdc3.channel.3',
type: 'user',
displayMetadata: {
name: 'Channel 3',
color: 'yellow',
glyph: '3',
},
},
{
id: 'fdc3.channel.4',
type: 'user',
displayMetadata: {
name: 'Channel 4',
color: 'green',
glyph: '4',
},
},
{
id: 'fdc3.channel.5',
type: 'user',
displayMetadata: {
name: 'Channel 5',
color: 'cyan',
glyph: '5',
},
},
{
id: 'fdc3.channel.6',
type: 'user',
displayMetadata: {
name: 'Channel 6',
color: 'blue',
glyph: '6',
},
},
{
id: 'fdc3.channel.7',
type: 'user',
displayMetadata: {
name: 'Channel 7',
color: 'magenta',
glyph: '7',
},
},
{
id: 'fdc3.channel.8',
type: 'user',
displayMetadata: {
name: 'Channel 8',
color: 'purple',
glyph: '8',
},
},
];
Direct Listening and Broadcast on Channels
While joining User channels (using fdc3.joinUserChannel
) automates a lot of the channel behavior for an app, it has the limitation that an app can only be 'joined' to one channel at a time. However, an app may instead retrieve an App Channel
Object via the fdc3.getOrCreateChannel
API, create a PrivateChannel
via the fdc3.createPrivateChannel
API, or by raising an intent that returns a channel created by another app. The Channel
object may then be used to listen to and broadcast on that channel directly using the Channel.addContextListener
and the Channel.broadcast
APIs. FDC3 imposes no restriction on adding context listeners or broadcasting to multiple channels.
App Channels
App Channels are topics dynamically created by applications connected via FDC3. For example, an app may create a named App Channel to broadcast data or status that is specific to that app to other apps that know to connect to the channel with that name.
To get (or create) a Channel
reference, then interact with it:
- TypeScript/JavaScript
- .NET
const appChannel = await fdc3.getOrCreateChannel('my_custom_channel');
// get the current context of the channel
const current = await appChannel.getCurrentContext();
// add a listener
await appChannel.addContextListener(null, context => {...});
// broadcast to the channel
await appChannel.broadcast(context);
var appChannel = await _desktopAgent.GetOrCreateChannel("my_custom_channel");
// get the current context of the channel
var current = await appChannel.GetCurrentContext(null);
// add a listener
await appChannel.AddContextListener<IContext>(null, (context, metadata) => { });
// broadcast to the channel
await appChannel.Broadcast(context);
An app can still explicitly receive context events on any Channel
, regardless of the channel it is currently joined to.
- TypeScript/JavaScript
- .NET
// check for current fdc3 channel
let joinedChannel = await fdc3.getCurrentChannel()
//current channel is null, as the app is not currently joined to a channel
//add a context listener for channels we join
const listener = await fdc3.addContextListener(null, context => { ... });
//retrieve an App channel and add a listener that is specific to that channel
const myChannel = await fdc3.getOrCreateChannel('my_custom_channel');
const myChannelListener = await myChannel.addContextListener(null, context => { ... });
fdc3.joinChannel('blue')
joinedChannel = await fdc3.getCurrentChannel()
//current channel is now the 'blue' channel
var joinedChannel = await _desktopAgent.GetCurrentChannel();
// current channel is null, as the app is not currently joined to a channel
// add a context listener for channels we join
var listener = await _desktopAgent.AddContextListener<IContext>(null, (context, metadata) => { });
// retrieve an App Channel and add a listener that is specific to that channel
var myChannel = await _desktopAgent.GetOrCreateChannel("my_custom_channel");
var myChannelListener = await myChannel.AddContextListener<IContext>(null, (context, metadata) => { });
await _desktopAgent.JoinUserChannel("blue");
joinedChannel = await _desktopAgent.GetCurrentChannel();
// current channel is now the "blue" channel
if another application broadcasts to "my_custom_channel" (by retrieving it and broadcasting to it via myChannel.broadcast()
) then the broadcast will be received by the specific listener (myChannelListener
) but NOT by the listener for joined channels (listener
).
Private Channels
A PrivateChannel
is created to support the return of a stream of responses from a raised intent, or private dialog between two applications.
It is intended that Desktop Agent implementations:
- SHOULD restrict external apps from listening or publishing on this channel.
- MUST prevent
PrivateChannels
from being retrieved viafdc3.getOrCreateChannel
. - MUST provide the
id
value for the channel as required by theChannel
interface.
The PrivateChannel
type also supports synchronization of data transmitted over returned channels. They do so by extending the Channel
interface with event handlers which provide information on the connection state of both parties, ensuring that desktop agents do not need to queue or retain messages that are broadcast before a context listener is added and that applications are able to stop broadcasting messages when the other party has disconnected.
Broadcasting and listening for multiple context types
The Context specification recommends that complex context objects are defined using simpler context types for particular fields. For example, a Position
is composed of an Instrument
and a holding amount. This leads to situations where an application may be able to receive or respond to context objects that are embedded in a more complex type, but not the more complex type itself. For example, a pricing chart might respond to an Instrument
but doesn't know how to handle a Position
.
To facilitate context linking in such situations it is recommended that applications broadcast
each context type that other apps (listening on a User Channel or App Channel) may wish to process, starting with the simpler types, followed by the complex type. Doing so allows applications to filter the context types they receive by adding listeners for specific context types - but requires that the application broadcasting context make multiple broadcast calls in quick succession when sharing its context.
Originating App Metadata
Optional metadata about each context message received, including the app that originated the message, SHOULD be provided by the desktop agent implementation to registered context handlers on all types of channel. As this metadata is optional, apps making use of it MUST handle cases where it is not provided.