Skip to main content

Platform API

The Platform API is a key component of Here™ Core Container. It enables developers to create complex interfaces that display multiple web apps or Here™ Core apps in views (tabs) within a window, and to save and restore arrangements of views and windows.

Elements of the Platform API

The following are high-level explanations of the elements of the Platform API.

Platform provider

A platform provider is the communication hub that coordinates among all the windows in a platform application. The provider runs in a hidden window and enables platform developers to extend or overwrite default platform behavior through platform overrides. This is where you can control how your platform applicatin will look and feel to create a custom branded experience.

Platform window

Platform windows act as a “frame” for platform views. They can display UI controllers such as minimize, maximize and close. These are child windows of the platform provider and may contain one or many platform views.

Platform view

Platform views provide the application's content and they reside within a platform window. The content (a web app or Here Core app) is loaded into a view and attached to a platform window. Views have their own JavaScript context that is distinct and unconnected to the window’s context; they have no DOM representation within the window. This allows views to be moved between windows without refreshing or otherwise destroying the context. Views can have tab headers (or not) and enable the end-user to arrange the layout of a platform window. For optimal performance, it is worth noting that views can be split into as many renderer processes as needed via the processAffinity option at the view level or the processAffinityStrategy option at the platform level.

Layout management

Layout management enables applications providers to programmatically embed multiple views or “web apps” in a single platform window. Refer to Organize views with layout.

Snapshot

A snapshot defines an arrangement (layout) of platform windows and views to be launched into a platform application. When a snapshot is added as a top-level option in a platform manifest, the platform launches with that snapshot by default. Platform developers can replenish or restore saved configurations while the platform application is running.

Platform manifest

This is the file that defines how a Here™ Core platform application works and tells the Here Core Runtime what the platform application looks like at launch.

Process affinity

By default, all child windows of a platform provider share a single renderer process, if they use content from the same origin. You can assign views into multiple processes by providing a processAffinity option; views with the same processAffinity value share a process. So, you can have two or more views share a process separate from other views. This can be quite useful in architecting your application for views that have a heavier memory profile.

You can also define whether a platform provider tries to keep views in the same process or different ones. The viewProcessAffinityStrategy option in the platform manifest can be set to "same" or "different". Setting this preference at the platform level can be simpler and more robust than handling process affinity at the view level.

How to use it

A platform application and its content can be launched programmatically or with a platform manifest. Read on to learn about the mechanisms that allow application developers to start a platform and launch content into that platform. As a best practice, we recommend leveraging a custom platform provider rather than a snapshot in the platform manifest.

Use the Platform API to:

  • Launch a platform, create a window, and add a view

  • Launch content in a view

  • Configure a layout

  • Save and restore a snapshot

Launch a platform

In order to use the API, its window must be a part of a manifest-launched platform. Let's launch one. Copy the following manifest and save as app.json locally:

{
"platform":
{
"uuid": "example_platform",
"applicationIcon": "https://here.io/favicon.ico",
"autoShow": false,
"defaultWindowOptions":
{
"cornerRounding":
{
"height": 10,
"width": 10
},
"contextMenu": true
}
},
"snapshot":
{
"windows":
[
{
"defaultWidth": 600,
"defaultHeight": 600,
"layout":
{
"content":
[
{
"type": "stack",
"content":
[
{
"type": "component",
"componentName": "view",
"componentState":
{
"name": "component_A1",
"processAffinity": "ps_1",
"url": "https://www.example.com"
}
},
{
"type": "component",
"componentName": "view",
"componentState":
{
"name": "component_A2",
"url": "https://cdn.openfin.co/embed-web/chart.html"
}
}
]
}
]
}
},
{
"defaultWidth": 600,
"defaultHeight": 600,
"defaultLeft": 200,
"defaultTop": 200,
"layout":
{
"content":
[
{
"type": "stack",
"content":
[
{
"type": "component",
"componentName": "view",
"componentState":
{
"name": "component_B1",
"url": "https://here.io"
}
}
]
}
]
}
}
]
},
"runtime":
{
"arguments": "--v=1 --inspect",
"version": "stable"
},
"shortcut":
{
"company": "Here™",
"description": "Here Core Platform Prototype",
"icon": "https://here.io/favicon.ico",
"name": "Here Core Platform Prototype"
}
}

Now let's launch it with the Here Core CLI:

openfin -l -c app.json

Create a platform window

Now that we have our platform, let's create a new platform window, along with a platform view, using Platform.createWindow. Open your developer console from a platform view and enter the following code:

let platform = await fin.Platform.getCurrent();
let myWinIdentity = await platform.createWindow({
contextMenu: true,
layout: {
content: [{
type: 'stack',
content:[{
type: 'component',
componentName: 'view',
componentState: {
name: 'my-new-test-view',
url: 'http://www.example.com' // The URL of the View
}
}]
}]
}
});

Add a view to an existing window

From the same view (and console), lets add an additional view to that window using Platform.createView():

// The `platform`and `myWinIdentity` here are the objects we captured in step above
platform.createView(
{ // View Configuration Options
name: 'my-new-test-view-2',
url:'http://www.example.com'
},
myWinIdentity // Target Identity
);

The Platform.createView leverages two arguments:

  • View configuration options: the name, URL of the window and any other view options
  • Target identity: the identity of the window to which the view will be attached.

You do not need to set a name for a view. Views that are not passed a name get a randomly generated one.

Launch content in a view

Content can be launched via manifest by calling the launchContentManifest() method on a wrapped platform instance. If the manifest to be launched contains a snapshot object, that snapshot is launched.

API calls

Once a platform is launched and running, snapshots can be launched into the platform using applySnapshot. Content can also be launched into the platform by manifest using startFromManifest. If that manifest also contains a snapshot object, that snapshot is launched. Any view specified in the snapshot is assigned a randomly generated name to avoid collisions.

applySnapshot(): A snapshot creates any windows and views that are not running in the snapshot object.

// Get a wrapped layout platform instance
const platform = await fin.Platform.getCurrent();

const snapshot = {
windows: [
{
layout: {
content: [
{
type: 'stack',
content: [
{
type: 'component',
componentName: 'view',
componentState: {
name: 'component_X',
url: 'https://www.openfin.co'
}
},
{
type: 'component',
componentName: 'view',
componentState: {
name: 'component_Y',
url: 'https://cdn.openfin.co/embed-web/chart.html'
}
}
]
}
]
}
}
]
}

platform.applySnapshot(snapshot);

createWindow(): creates a new platform window with default window UI

const platform = fin.Platform.getCurrentSync();
platform.createWindow({
layout: {
content: [
{
type: 'stack',
content: [
{
type: 'component',
componentName: 'view',
componentState: {
name: 'test_view_1',
url: 'https://developer.openfin.co/docs/javascript/stable/classes/OpenFin.Platform.html'
}
},
{
type: 'component',
componentName: 'view',
componentState: {
name: 'test_view_2',
url: 'https://developer.openfin.co/docs/javascript/stable/classes/OpenFin.Platform.html'
}
}
]
}
]
}
}).then(console.log);

createView(): Creates a new platform view and attaches it to a specified target window.

let windowIdentity;
if (fin.me.isWindow) {
windowIdentity = fin.me.identity;
} else if (fin.me.isView) {
windowIdentity = (await fin.me.getCurrentWindow()).identity;
} else {
throw new Error('Not running in a platform View or Window');
}

const platform = fin.Platform.getCurrentSync();

platform.createView({
name: 'test_view',
url: 'https://developers.openfin.co/docs/platform-api'
}, windowIdentity).then(console.log);

The RVM permits deep linking to a Here Core application from anywhere that can invoke a “link” like a browser, email client, or another Here Core application. The RVM uses a custom protocol handler to invoke an application, if not already running, and pass context to a specific location within an Here Core application via a uniform resource identifier (URI).

You can use a fins link to both start a platform and to launch content into a platform. A link that targets a single platform manifest starts the platform, if the platform is not already running, and launches a snapshot into the platform, if present in the manifest.

You can also designate a separate content manifest to be launched into the platform by targeting the platform manifest and adding an $$appManifestUrl parameter. This starts the platform, if not already running, but launches the content in the second manifest at the $$appManifestUrl location. If an $$appManifestUrl parameter is included, any snapshot that exists on the platform manifest is ignored and only the content designated at the appManifestUrl is launched.

Like the launchContentManifest() method, if the manifest at the $$appManifestUrl location contains a snapshot object, that snapshot is launched. If instead it is a legacy manifest without a snapshot, the startup_app is launched as a single view in the layout of a new Here Core window.

fins://mydomain.com/path-to-platform-manifest/app.json?$$appManifestUrl=https://mydomain.com/path-to-content-manifest/

If both snapshot and startup_app object are present in the $$appManifestUrl location, the following hierarchy of which of these is used applies:

  • startup_app is always used if you are launching the manifest in a traditional way through the RVM.
  • snapshot is used only if the manifest is passed via the $$appManifestUrl.

Changes to $$appManifestUrl

As part of our commitment to provide a secure and more controlled development environment, starting in Here™ Core Runtime version 36, behavior of the $$appManifestUrl parameter changes from earlier versions.

Starting in version 36, using the $$appManifestUrl parameter causes warning messages to appear in the Platform provider window console unless you specifically opt-in or opt-out of the allowLaunchIntoPlatform manifest property.

To opt-in, set the allowLaunchIntoPlatform property value to true as shown below. To opt-out, set the allowLaunchIntoPlatform property value to false.

{
...
"platform": {
"allowLaunchIntoPlatform": true
}
}

If the allowLaunchIntoPlatform property does not exist in the manifest file, the warnings appear at each launch that uses $$appManifestUrl.

Starting in Runtime version 38, use of the $$appManifestUrl parameter is disabled by default (opt-out behavior). The warning messages will appear in the Platform provider window console. However, the platform does not launch the new content.

To enable the $$appManifestUrl parameter for Runtime versions 38 and later, set the allowLaunchIntoPlatform manifest property to true as in the example.

Configure a layout

Here Core platforms allow end-users the freedom to easily move views within a Here Core window to create their own customized workspace. Application providers may also wish to have presets that launch multiple views in a pre-configured window arrangement. In this section, we cover three basic layout configurations for tabs, columns and rows and a grid.

Image outlining a possible layout

Tabs

// A Window object within a snapshot that has one tabset (or “stack”) with three
// views represented as tabs.
{
"content": [
{
"type": "stack",
"content": [
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_E",
"url": "https://cdn.openfin.co/embed-web/chart.html"
}
},
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_F",
"url": "https://openfin.co"
}
},
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_G",
"url": "https://www.example.com"
}
}
]
}
]
}

Columns and rows

//A window object within a layout that has three views arranged as columns.
"content": [
{
"type": "row",
"content": [
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_C",
"url": "https://cdn.openfin.co/embed-web/chart.html"
}
},
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_D",
"url": "https://openfin.co"
}
},
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_E",
"url": "https://www.example.com"
}
}
]
}
]

Grid

//A window object within a snapshot that has four views arranged as a 2x2 grid.
{
"content": [
{
"type": "row",
"content": [
{
"type": "row",
"content": [
{
"type": "column",
"content": [
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_A",
"url": "https://cdn.openfin.co/embed-web/chart.html"
}
},
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_B",
"url": "https://cdn.openfin.co/embed-web/chart.html"
}
}
]
}
]
},
{
"type": "row",
"content": [
{
"type": "column",
"content": [
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_C",
"url": "https://www.example.com"
}
},
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_D",
"url": "https://www.openfin.co"
}
}
]
}
]
}
]
}
]
}

Snapshots

Earlier, we loaded a brand-new platform window with two views in it. Here we will retrieve getSnapshot a platform payout and apply applySnapshot a previously saved platform layout.

Get snapshot

Use the following code in the same console you were using in the Add a View to an Existing Window section. Platform.getSnapshot returns a snapshot object and can be used to return the desktop back to this current state. You can store this object wherever you'd like, and use it later to apply this Snapshot.

// The `platform` here is the object we captured
const mySnapshot = await platform.getSnapshot();
console.log(mySnapshot);

Apply snapshot

Now let’s use our snapshot object to recreate our desktop using applySnapshot. First, rearrange your platform windows on your desktop.

  1. Close the window you created earlier (the one filled with www.example.com Views), move your windows to different locations.
  2. Move the views in their platform windows to different locations.
  3. Execute the code below to get your desktop back to the way it was before.
// The `platform` here is the object we captured
platform.applySnapshot(mySnapshot, {closeExistingWindows: true});

How a window can opt out of snapshots

There are some occasions where you may not want a window to be included in snapshots.

To make a window opt out of snapshots, set the window.includeInSnapshots property to false. To include the window in snapshots again, set the window.includeInSnapshots property to true.

Platform manifest in depth

This section breaks down the various pieces of the Here Core manifest for a platform.

The platform object defines the platform as a targetable unique entity. Requires a top-level platform property and a UUID. If you are previously accustomed to Here Core manifest structure, platform acts in the same way as the startup_app field.

{
"runtime": {
...
},
"shortcut": {
...
},
"platform": {
"uuid": "PLATFORM_EXAMPLE",
"defaultWindowOptions": {
"stylesheetUrl": "FULL_URL_TO_CSS",
"cornerRounding": {
"height": 10,
"width": 10
}
}
}
}

The snapshot object defines the window and view configurations to be launched into a platform. If a snapshot is defined as a top-level option in the platform manifest, the platform launches with that snapshot by default. It has a windows property that contains an array of Here Core Window objects. Do not give names to windows, because they can be destroyed and re-created by the user at any time.

{
"snapshot": {
"windows": [
{
"defaultWidth": 600,
"defaultHeight": 600,
"layout": {
...
}
}
]
}
}

The layout object defines the layout configuration for a window. To position a Here Core view within a window, the layout must contain a content declaration with a componentName value of "view". Content items have a "type" property, which can have the following values: "row", "column", "stack", or "component". Arrays of content items can be nested within other content items as well.

{
"snapshot": {
"windows": [
{
"defaultWidth": 600,
"defaultHeight": 600,
"layout": {
"content": [
{
"type": "stack",
"content": [
{
"type": "component",
"componentName": "view",
"componentState": {
...
}
},
{
"type": "component",
"componentName": "view",
"componentState": {
...
}
}
]
}
]
}
}
]
}
}

The componentState object defines the componentState property to provide the view options. The URL of a manifest that contains View Options can be added as a manifestUrl property. Properties in the manifest take precedence if there is any collision.

{
"snapshot": {
"windows": [
{
"defaultWidth": 600,
"defaultHeight": 600,
"layout": {
"content": [
{
"type": "stack",
"content": [
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_A1",
"processAffinity": "ps_1",
"url": "https://www.example.com"
}
},
{
"type": "component",
"componentName": "view",
"componentState": {
"name": "component_A2",
"url": "https://cdn.openfin.co/embed-web/chart.html"
}
}
]
}
]
}
}
]
}
}