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);
Deep linking (fin
/ fins
link)
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.
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.
- Close the window you created earlier (the one filled with www.example.com Views), move your windows to different locations.
- Move the views in their platform windows to different locations.
- 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"
}
}
]
}
]
}
}
]
}
}