Portability

This article describes the technologies and best practices that make iModel.js apps portable. A well-written iModel.js app will run on many platforms without modification.

In addition, a well-written interactive app can be configured to run as a Web abb, a desktop app, and a mobile app, with a few simple runtime checks and no other code changes. Configurability does not mean that an iModel.js app must be the same in all configurations. In fact, the iModel.js architecture makes it easy to make the app fit the platform.

This degree of portability and configurability is possible because of the technologies used by iModel.js apps and the iModel.js app architecture.

Cross-Platform Technologies

The key technology that makes an app portable is JavaScript. JavaScript engines are available on many operating systems and Web browsers. TypeScript compiles to JavaScript.

Node.js is an execution environment for agents, services, and servers. Node.js runs JavaScript programs. Node.js itself runs on many platforms, including Windows and Linux. Node.js is also widely supported by cloud infrastructures and deployment mechanisms.

Web UI technology, including HTML and CSS, makes it possible to write a cross-platform user interface that looks and behaves the same everywhere, in every configuration. Web UI technology is supported by Web browsers and Web view renderers on many platforms. Web UI technology also simplifies the task of reconfiguring or even swapping out an app's GUI.

Electron combines a Web view client and a nodejs-based server into a single desktop product, without requiring any modification of the code or the UI. Electron runs on many desktop platforms.

Frontend Portability

Since an iModel.js app frontend is written using Web UI technologies, it is inherently portable.

Using Platform-specific Modules in the Frontend

In some cases, the Web view environment will provide platform-specific globals and modules. The frontend can use these modules in guarded code. To detect the platform of the frontend:

const isIos: bool = /iphone|ipod|ipad/.test(window.navigator.userAgent.toLowerCase());
// ... other mobile platforms ...

if (isIos) {
  // ... use iOS-specific modules, such as camera ...
}

Backend Portability

Services and Agents

True services and agents are easy to make portable, since they always run in Node.js and they always run on a server. Services always run a Web server and always use Web RPC configuration. Note that an iModel.js service or agent does not deal directly with issues such as deployment, routing, or scaling. Those are the concerns of the cloud infrastructure. As nodejs apps, iModel.js services and agents are cloud-neutral and run on many cloud infrastructures.

App Backends

App-specific backends do not always run in nodejs, and they do not always run on a server. This section describes how to make an app-specific backend portable and adaptable to multiple app configurations.

Using Platform-specific Modules in the Backend

A backend can use platform-specific globals and modules, but only in guarded code. See Platform for methods to detect the platform.

For example, a backend can use node builtins in guarded code, like this:

  import { Platform } from "@bentley/imodeljs-backend";

  if (Platform.isNodeJs()) {
    // access nodejs-specific modules and/or globals
    __dirname;
    process.env;
    require("fs");
    // ...
  }

Avoiding Node.js dependencies

A backend can use the following portable imodeljs-backend classes to avoid unnecessary node dependencies:

Node builtin imodeljs-backend portable substitute
fs IModelJsFs
os Platform
process Platform
__dirname KnownLocations
__filename KnownLocations
console Logger
path path

In most cases, the imodeljs-backend substitutes do not provide all of the properties of the node global. That is by design, as not all of the features offered by node are portable.

path

A partial implementation of path is provided on non-nodejs platforms. The methods that are available everywhere are: posix, win32, sep, delimiter, basename, dirname, extname, normalize, join, isAbsolute.

Use only these methods in unguarded code.

Unsupported

An app backend must never depend on node-specific packages, including any package that is based on native code.

For fs-extra, use IModelJsFs.

Last Updated: 23 April, 2020