2.0.0 Change Notes

This is release 2.0 of iModel.js.

With this annual major release comes many new features and some breaking API changes. Several breaking changes are the removal of previously deprecated APIs. In other cases, some APIs have changed in ways that may require calling code to be adjusted. This document describes these in detail to help you upgrade.

Table of Contents

What's New?

3D Globe Background Map Display

The background map can now be displayed as either a plane or a three-dimensional globe. This is controlled by the GlobeMode property of the DisplayStyleSettings.backgroundMap associated with a DisplayStyleState.

In Plane mode, or in 3d mode when sufficiently zoomed-in on the iModel, the iModel's geographic coordinate system is used to transform the map into the iModel's coordinate space.

Plane mode

Plane mode

Globe mode

Ellipsoid mode

Globe View Tools

The following are view tools that allow a user to navigate a plane or three-dimensional globe. All of these tools operate on the selected view.

  • ViewGlobeSatelliteTool views a location on the background map from a satellite's perspective; the viewed location is derived from the position of the current camera's eye above the map.
  • ViewGlobeBirdTool views a location on the background map from a bird's eye perspective; the viewed location is derived from the position of the current camera's eye above the globe.
  • ViewGlobeLocationTool views a location on the background map corresponding to a specified string. This will either look down at the location using a bird's eye height, or, if a range is available, the entire range corresponding to the location will be viewed.
  • ViewGlobeIModelTool views the current iModel on the background map so that the extent of the project is visible.

ViewGlobeSatelliteTool, ViewGlobeBirdTool, and ViewGlobeIModelTool run in the following manner:

  • The tool, once constructed, will execute when its onDataButtonDown or onPostInstall methods are called.
  • onDataButtonDown will execute the tool if its BeButtonEvent argument has a defined viewport property. It will use that viewport.
  • onPostInstall will use the viewport specified in the tool's constructor. If that does not exist, it will use IModelApp.viewManager.selectedView.

ViewGlobeLocationTool runs in the following manner:

  • The tool, once constructed, will execute when its parseAndRun method is called.
  • To navigate to a precise latitude/longitude location on the map, specify exactly two numeric arguments to parseAndRun. The first will be the latitude and the second will be the longitude. These are specified in degrees.
  • To search for and possibly navigate to a named location, specify any number of string arguments to parseAndRun. They will be joined with single spaces between them. If a location corresponding to the joined strings can be found, the tool will navigate there.

Customizable Scene Lighting

Previously, lighting of 3d scenes was entirely hard-coded with the exception of the sun direction, used only when shadows were enabled. Now, nearly all lighting parameters can be customized using the LightSettings associated with a DisplayStyle3dSettings. This includes new support for hemisphere lighting, greatly expanding the variety of display styles that can be achieved.

Example display styles

Clockwise from top-left: Default, Illustration, Sun-dappled, Moonlit, Glossy, Soft

Monochrome Mode

iModel.js now supports two monochrome display modes via DisplayStyleSettings.monochromeMode. The original mode, Scaled, preserves contrast and material textures. The new mode, Flat, applies the monochrome color uniformly to all surfaces.

Scaled (left) vs Flat (right) monochrome modes

Scaled (left) vs Flat (right) monochrome modes

Colorizing Clip Regions

Viewport now contains the following properties that control the color of pixels outside or inside a clip region. If either of these are defined, the corresponding pixels will be shown using the specified color; otherwise, no color override occurs and clipping proceeds normally for that area of the clip region. By default, these are both undefined.

  • outsideClipColor - Either a ColorDef or undefined. This setting controls the color override for pixels outside a clip region.
  • insideClipColor - Either a ColorDef or undefined. This setting controls the color override for pixels inside a clip region.

Clipped geometry drawn in yellow

Clipped geometry drawn in yellow - arrow indicates direction of clip plane

Incremental Precompilation of Shaders

Previously, shader programs used by the RenderSystem were never compiled until the first time they were used. This could produce noticeable delays when the user interacts with a Viewport. The RenderSystem can now precompile shader programs before any Viewport is opened.

  • To enable this functionality, set the doIdleWork property of the RenderSystem.Options object passed to IModelApp.startup to true.
  • Applications should consider enabling this feature if they do not open a Viewport immediately upon startup - for example, if the user is first expected to select an iModel and a view through the user interface.
  • Shader precompilation will cease once all shader programs have been compiled, or when a Viewport is opened (registered with the ViewManager).

Thematic Display

ViewFlags now contains a thematicDisplay property of type boolean; when set to true, this will enable thematic display for surfaces.

Note that the renderer currently does not allow surfaces which are thematically displayed to receive shadows.

  • The thematic display will be configured based on the thematic property of type ThematicDisplay on DisplayStyle3dSettings.
  • Within the gradientSettings property on ThematicDisplay, the display system currently supports a mode value of ThematicGradientMode.Smooth of type ThematicGradientMode. Using this mode, the color gradient will be smoothly interpolated based on the value specified for colorScheme on the gradientSettings property of ThematicDisplay. If the colorScheme property of gradientSettings is ThematicGradientColorScheme.Custom, then the customKeys property must be properly configured with values.
  • For the displayMode property of ThematicDisplay, the display system currently supports the following values:
    • ThematicDisplayMode.Height. Using this mode, the color gradient will be mapped to surface geometry based on world height in meters.
    • ThematicDisplayMode.InverseDistanceWeightedSensors. Using this mode, the color gradient will be mapped to surface geometry using inverse distance weighting based on world positions and corresponding values from a list of sensors.

See the following snippet for the JSON representation of a ThematicDisplay configuration object:

/** JSON representation of the thematic display setup of a [[DisplayStyle3d]].
 * @beta
 */
export interface ThematicDisplayProps {
  /** The thematic display mode. This determines how to apply the thematic color gradient to the geometry. Defaults to [[ThematicDisplayMode.Height]]. */
  displayMode?: ThematicDisplayMode;
  /** The settings used to create a color gradient applied to the geometry. The mode currently must be [[Gradient.ThematicMode.Smooth]]. Defaults to an instantiation using [[ThematicGradientSettings.fromJSON]] with no arguments. */
  gradientSettings?: ThematicGradientSettingsProps;
  /** The range in which to apply the thematic gradient. For [[ThematicDisplayMode.Height]], this is world space in meters. Defaults to a null range. */
  range?: Range1dProps;
  /** For [[ThematicDisplayMode.Height]], this is the axis along which to apply the thematic gradient in the scene. Defaults to {0,0,0}. */
  axis?: XYZProps;
  /** For [[ThematicDisplayMode.InverseDistanceWeightedSensors]], these are the settings that control the sensors. Defaults to an instantiation using [[ThematicDisplaySensorSettings.fromJSON]] with no arguments.
   * @alpha
   */
  sensorSettings?: ThematicDisplaySensorSettingsProps;
}

Consult the following code example demonstrating how to enable thematic display and configure the thematic display:

const _scratchViewFlags = new ViewFlags();

const isThematicDisplaySupported = (view: ViewState) => view.is3d();

function enableAndConfigureThematicDisplay(viewport: Viewport): boolean {
  const view = viewport.view;

  if (!isThematicDisplaySupported(view))
    return false; // Thematic display settings are only valid for 3d views

  // Clone and reconfigure the Viewport's viewFlags to have thematic display enabled
  const vf = viewport.viewFlags.clone(_scratchViewFlags);
  vf.thematicDisplay = true;
  viewport.viewFlags = vf;

  // Create a ThematicDisplayProps object with the desired thematic settings
  const thematicProps: ThematicDisplayProps = {
    displayMode: ThematicDisplayMode.Height, // The thematic display mode; could also be ThematicDisplayMode.InverseDistanceWeightedSensors
    gradientSettings: {
      mode: ThematicGradientMode.Smooth, // The only currently supported thematic gradient mode
      stepCount: 0, // Only relevant for ThematicGradientMode.Stepped, which is currently unsupported.
      marginColor: new ColorDef(ColorByName.blanchedAlmond), // The color used when outside the range to apply the gradient
      colorScheme: ThematicGradientColorScheme.BlueRed, // The color scheme used to construct the gradient; if using ThematicColorScheme.Custom, must also specify customKeys property.
    },
    range: { low: -900.0, high: 1000.0 }, // For ThematicDisplayMode.Height, the range in world meters to apply the gradient
    axis: [0.0, 0.0, 1.0], // For ThematicDisplayMode.Height, the axis (direction) along which to apply the gradient (Up along Z in this case)
    sensorSettings: { // For ThematicDisplayMode.InverseDistanceWeightedSensors, specify sensor settings here.
      sensors: [], // put any sensors in this array.
    },
  };

  // Create a ThematicDisplay object using the props created above
  const thematicDisplay = ThematicDisplay.fromJSON(thematicProps);

  // Change the thematic object on the 3d display style state to contain the new object
  (view as ViewState3d).getDisplayStyle3d().settings.thematic = thematicDisplay;

  // Sync the viewport with the new view state
  viewport.synchWithView();
}

Thematic height mode with a smooth "sea mountain" color gradient applied to surfaces with lighting enabled

Thematic height mode with a smooth "sea mountain" color gradient applied to surfaces with lighting enabled

Four thematic sensors applied using inverse distance weighting with a smooth "blue-red" color gradient applied to surfaces with lighting disabled

Four thematic sensors applied using inverse distance weighting with a smooth "blue-red" color gradient applied to surfaces with lighting disabled

Persistent Schedule Animation State

DisplayStyleSettings.timePoint now identifies the current point on the timeline of the style's RenderSchedule script. The time point is specified in Unix seconds.

To adjust the schedule script time point in the context of a viewport, use Viewport.timePoint.

Breaking API changes

Installation and Builds

Increase minimum Node version

The minimum version of Node required for iModel.js 2.0 applications is now 10.16.0. However, it is recommended to use the latest LTS version of Node 12 for the most recent security patches.

Update to Electron 8

iModel.js 2.0 has moved to the latest stable version of Electron 8. If you are building desktop applications, upgrade the version of electron in your package.json, e.g.:

 "dependencies": {
    ...
    "electron": "^8.2.1"
    ...
  },

Update to iModel.js Build System

The iModel.js 1.0 build system relied on a single package (@bentley/webpack-tools) to build iModel.js frontends and extensions. With the release of 2.0, there are significant improvements to the build system to help with both clarity and usability. As a result, the build system is now split into 2 separate components:

  • Webpack/bundling of an iModel.js Extension (formerly known as a Plugin) with @bentley/extension-webpack-tools
  • The iModel.js frontend build system is now based on Create-React-App. For more details visit the Build Migration Guide.

Deprecation Errors

If you were using or extending the tslint configuration supplied by iModel.js, please note that the default tslint configuration used to report usage of deprecated APIs as warnings. They now produce errors instead.

Changes in @bentley/imodeljs-clients

There are now separate packages

The former @bentley/imodeljs-clients package contained clients for various Bentley-hosted services. It has now been split into the following packages under the /clients/ directory:

  • @bentley/itwin-client
  • @bentley/context-registry-client
  • @bentley/forms-data-management
  • @bentley/frontend-authorization
  • @bentley/imodelhub-client
  • @bentley/product-settings-client
  • @bentley/projectshare-client
  • @bentley/rbac-client
  • @bentley/reality-data-client
  • @bentley/usage-logging-client

The @bentley/itwin-client package contains the base functionality that all other clients leverage. Update your imports to use specific packages as necessary.

The logger categories setup in @bentley/imodeljs-clients have now migrated to the individual packages, with names consistent with their new location: | Old code | New package name | New code | | ---------------------------------- | ------------------------------- | ---------------------------------------- | | ClientsLoggerCategory.Clients | @bentley/itwin-client | ITwinClientLoggerCategory.Clients | | ClientsLoggerCategory.ECJson | @bentley/itwin-client | ITwinClientLoggerCategory.ECJson | | ClientsLoggerCategory.Request | @bentley/itwin-client | ITwinClientLoggerCategory.Request | | ClientsLoggerCategory.IModelHub | @bentley/imodelhub-client | IModelHubClientLoggerCategory.IModelHub | | ClientsLoggerCategory.IModelBank | @bentley/imodelhub-client | IModelHubClientLoggerCategory.IModelBank | | ClientsLoggerCategory.ImsClients | Removed | Removed | | ClientsLoggerCategory.UlasClient | @bentley/usage-logging-client | UsageLoggingClientLoggerCategory.Client |

Removed support for SAML-based authorization

  • All authentication must now be done using OIDC. The iModel.js API includes wrappers around popular 3rd party utilities for OIDC based authentication/authorization for web, desktop and agent applications.
  • The previously deprecated SAML-based authentication utilities, ImsActiveSecureTokenClient and ImsDelegationSecureTokenClient, have now been removed.
  • AccessToken cannot hold SAML tokens anymore.
  • The deprecated OidcAgentClientV1 for SAML-based authentication of agents has been removed.

Removals and changes

  • The IAuthorizationClient interface has been renamed to AuthorizationClient
    • The hasExpired field in this interface has been removed.
    • The hasSignedIn field in this interface has been moved to FrontendAuthorizationClient
  • Config has been relocated to @bentley/bentleyjs-core from @bentley/imodeljs-clients. Update your imports:

    import {Config} from "@bentley/bentleyjs-core";

Changes in @bentley/imodeljs-clients-backend

Package rename

In addition to the @bentley/imodeljs-clients package changes described before, the @bentley/imodeljs-clients-backend package has been renamed to @bentley/backend-itwin-client.

Authorization for Agent Applications

The OidcAgentClient utility has been renamed to AgentAuthorizationClient and OidcAgentClientConfiguration is now renamed to AgentAuthorizationClientConfiguration for consistency.

The older names continue to exist as aliases, but are now marked as deprecated.

Changes in @bentley/imodeljs-common

RPC changes

  • The major versions of the RPC interfaces have been updated for iModel.js 2.0, and this makes them incompatible with older versions. As a result, 1.x frontends/backends cannot be paired with 2.0 frontends/backends
  • For applications that defined custom RPC interfaces, note that function signatures of RPC operations no longer need to include the IModelRpcProps (formerly IModelToken) argument, if not required.

Solar Calculation APIs

The solar calculation functions calculateSolarAngles, calculateSolarDirection, and calculateSunriseOrSunset have moved from the @bentley/imodeljs-frontend package to the @bentley/imodeljs-common package.

GeometryStream Iteration

The GeometryStreamIteratorEntry exposed by a GeometryStreamIterator has been simplified down to only four members. Access the geometric primitive associated with the entry by type-switching on its type property. For example, code that previously looked like:

function tryTransformGeometry(entry: GeometryStreamIteratorEntry, transform: Transform): void {
  if (undefined !== entry.geometryQuery)
    return entry.geometryQuery.tryTransformInPlace(transform);

  if (undefined !== entry.textString) {
    entry.textString.transformInPlace(transform);
    return true;
  } else if (undefined !== entry.image)
    entry.image.transformInPlace(transform);
    return true;
  }
  // etc...
}

Is now written as:

function tryTransformGeometry(entry: GeometryStreamIteratorEntry, transform: Transform): void {
  switch (entry.primitive.type) {
    case "geometryQuery":
      // The compiler knows that entry.primitive is of type AnyGeometryQuery
      return entry.primitive.geometryQuery.tryTransformInPlace(transform);
    case "textString":
    case "image":
      // The compiler knows that entry.primitive is a TextString or an ImageGraphic, both of which have a transformInPlace() method
      entry.primitive.transformInPlace(transform);
      return true;
    // etc...
  }
}

Immutable Color Types

ColorDef is now an immutable type. Naturally, mutating methods like setTransparency have been removed; they are replaced by methods like withTransparency which return a modified copy of the original ColorDef. The constructor is now private; replace new ColorDef(x) with ColorDef.create(x).

HSVColor and HSLColor are also now immutable.

Gradient API

The following have been removed from the Gradient namespace and renamed:

Gradient.Symb.createThematic now takes a ThematicGradientSettings argument.

Other changes

Changes in @bentley/imodeljs-backend

Async startup and shutdown

The following methods are now async and return Promise<void>:

Calling code should be updated to await these Promises.

Specifying cache locations

The existing method to specify the briefcase cache location by passing IModelHostConfiguration.briefcaseCacheDir to IModelHost.startup has been deprecated - i.e., this can still be used to match the existing behavior, even if use of it will cause deprecation warnings during compilation.

Starting with this version, a new IModelHostConfiguration.cacheDir has been introduced to specify a root location for all caches including the briefcase cache. The briefcases are organized under the "bc" sub-folder of this location.

If IModelHostConfiguration.cacheDir and the legacy IModelHostConfiguration.briefcaseCacheDir are both not specified, the existing default behavior was to pick a location underneath the temporary directory. This is no more the case.

Starting with version 2.0, the default behavior is to instead pick a location outside of the temporary directory, but still under the home directory of the logged in user. For more details on the new location, see IModelHostConfiguration.cacheDir.

Use IModelHost.cacheDir to get the resolved cache location after startup.

Briefcase iModels - Managing Briefcases at the Backend

The methods for working with Briefcase iModels (those that are synchronized with iModelHub) have been refactored. At the backend many of the methods have been moved from (the now abstract) IModelDb class to either the BriefcaseDb or BriefcaseManager class. At the frontend, the methods have been moved from (the now abstract) IModelConnection class to the RemoteBriefcaseConnection class. More details below -

  • Opening an iModel from iModelHub at the backend involves two steps - downloading a briefcase of the iModel, and then opening that briefcase. These two operations have been separated out now, and require two different API calls - await the asynchronous call to BriefcaseManager.download to complete the download, and open the briefcase with a synchronous call to BriefcaseDb.open.

    • Before change:

      const iModelDb = await IModelDb.open(requestContext, projectId, iModelId, OpenParams.fixedVersion(), IModelVersion.latest());
    • After change:

        const downloadOptions: DownloadOptions = {syncMode: FixedVersion};
        const briefcaseProps: BriefcaseProps = await BriefcaseManager.download(requestContext, contextId, iModelId, downloadOptions, version);
        requestContext.enter();
      
        const briefcaseDb = BriefcaseDb.open(requestContext, briefcaseProps.key);
  • The parameter OpenParams to the open call has been removed, and instead replaced with OpenBriefcaseOptions. The download call takes DownloadBriefcaseOptions. See above example. Also, the SyncMode option that is part of DownloadBriefcaseOptions has been moved from the @bentley/imodeljs-backend package to @bentley/imodeljs-common package. Update your imports like below:

    import {SyncMode} from "@bentley/imodeljs-common";
  • The API now allows downloading briefcases at the backend with a new SyncMode.PullOnly option. e.g.,

    const downloadOptions: DownloadOptions = {syncMode: FixedVersion};
    const briefcaseProps: BriefcaseProps = await BriefcaseManager.download(requestContext, projectId, iModelId, downloadOptions);
    requestContext.enter();
    
    const briefcaseDb = BriefcaseDb.open(requestContext, briefcaseProps.key);
    • Downloading iModels with this new option establishes a local briefcase that allows change sets to be pulled from iModelHub and merged in, but disallows pushes back to the iModelHub. e.g.,

      briefcaseDb.pullAndMergeChanges(requestContext, IModelVersion.latest());
    • Upon open, a new briefcase is acquired from iModelHub and is meant for exclusive use by that user.

    • The briefcase is opened read/write to allow merging of change sets even if no changes can be made to it.
  • BriefcaseDb.onOpen and BriefcaseDb.onOpened events pass a context that may or may not include an AccessToken. i.e., they take AuthorizedClientRequestContext | ClientRequestContext as a parameter instead of AuthorizedClientRequestContext

  • Removed the option to delete the briefcase on close (i.e., KeepBriefcase). Very similar to the open, it should be done in two separate steps, with the close being a synchronous operation now.

    • Before change:

      await iModelDb.close(requestContext, KeepBriefcase.No);
    • After change:

      briefcaseDb.close();
      await BriefcaseManager.delete(requestContext, briefcaseDb.key);
  • The following methods have been moved from (the now abstract) IModelDb class to the BriefcaseDb:

  • The following methods have been moved from IModelDb to BriefcaseManager

Snapshot iModels

The methods for working with snapshot iModels have been moved into a new SnapshotDb class, which is a breaking change. The following renames are required:

Other Changes

  • BriefcaseId / ReservedBriefcaseId: The former BriefcaseId class has been replaced by the BriefcaseId type (which is just number) and the ReservedBriefcaseId enumeration.
  • UlasUtilities has been renamed to UsageLoggingUtilities to better align with name of the newly created usage-logging-client package
  • ExportGraphicsProps and associated interfaces have been renamed to ExportGraphicsOptions to convey that they are not wire formats.
  • Entity.forEachProperty has moved from beta --> public. Please note that the default value of includeCustom has changed from false to true to better match typical use.

Changes in @bentley/imodeljs-frontend

Authorization in Browsers

OIDC functionality in the browser has been overhauled to better support iModel.js Extensions that may require the user to authenticate with other services.

Previously, signing in through OidcBrowserClient involved the following process:

const oidcConfiguration: BrowserAuthorizationClientConfiguration = {
  clientId: "imodeljs-spa-test",
  redirectUri: "http://localhost:3000/signin-callback",
  scope: "openid email profile organization imodelhub context-registry-service:read-only product-settings-service projectwise-share urlps-third-party",
  responseType: "code",
};
const browserClient = new OidcBrowserClient(oidcConfiguration);
await browserClient.initialize(new ClientRequestContext());
await browserClient.signIn();

The equivalent process for signing in via BrowserAuthorizationClient:

const oidcConfiguration: BrowserAuthorizationClientConfiguration = {
  clientId: "imodeljs-spa-test",
  redirectUri: "http://localhost:3000/signin-callback",
  scope: "openid email profile organization imodelhub context-registry-service:read-only product-settings-service projectwise-share urlps-third-party",
  responseType: "code",
};
await BrowserAuthorizationCallbackHandler.handleSigninCallback(oidcConfiguration.redirectUri);
const browserClient = new BrowserAuthorizationClient(oidcConfiguration);
await browserClient.signIn();

Notably, unlike OidcBrowserClient, BrowserAuthorizationClient does not require a call to initialize() before calling signIn(). Once the class instance has been constructed, signIn() may be called at any point. However, because OidcBrowserClient).initialize() was where the OIDC callback was being handled before, BrowserAuthorizationCallbackHandler must be used in conjunction with BrowserAuthorizationClient to now complete an OIDC signin.

Aside from the signIn() function supported by all FrontendAuthorizationClient implementations, there are also three new functions specific to BrowserAuthorizationClientsignInSilent(), signInPopup(), and signInRedirect() (which is an alias of signIn()) — allowing more flexibility in how the signin is performed.

For situations where a signin is delayed until after app startup, signInPopup() is encouraged as a way to direct the user towards a login page without redirecting them away from their current UI state.

Signin callbacks generated by any of the signIn methods can be handled easily using a single call to BrowserAuthorizationCallbackHandler.handleSigninCallback().

Authorization for Desktop Applications

DesktopAuthorizationClient is available for authorization in Desktop Applications. This is meant for use in the renderer process. A configured instance of this class can be used to setup IModelApp.authorizationClient.

const authConfiguration: DesktopAuthorizationClientConfiguration = {
  clientId: "imodeljs-electron-test",
  redirectUri: "http://localhost:3000/signin-callback",
  scope: "openid email profile organization imodelhub context-registry-service:read-only product-settings-service projectwise-share urlps-third-party",
  responseType: "code",
};
const desktopClient = new DesktopAuthorizationClient(authConfiguration);
await desktopClient.initialize(new ClientRequestContext());
await desktopClient.signIn();

IModel, IModelConnection, IModelDb

The properties formerly under IModel.iModelToken have been promoted to IModel. These renames affect IModelConnection and IModelDb:

And for RPC implementations, the following method has been added to replace other uses of IModel.iModelToken:

  • IModel.getRpcProps
    • This method returns an object of type IModelRpcProps that replaces IModelToken and IModelTokenProps but maintains the same property names as before.

Managing Briefcases at the Frontend

Similar to the backend, at the frontend, the following method has been moved from (the now abstract) IModelConnection class:

Snapshot iModels

Changes corresponding to the backend have been made to the frontend. The following methods have been moved from (the now abstract) IModelConnection class:

BlankConnection

A new BlankConnection subclass of of IModelConnection has been introduced for working with reality data services without requiring an iModel. The following renames are required:

ViewChangeOptions Interface

The ViewChangeOptions interface now has a new optional member, onExtentsError. It provides a way for applications to be notified when a viewing operation hits the view-specific limits for extents (either minimum or maximum size.) If possible, the viewing api will adjust the extents value to the limit, but if onExtentsError is provided, it is called with the error value so it can show a message to the user. If the error should be ignored (i.e. the adjusted value is acceptable), return ViewStatus.Success from onExtentsError.

The signatures of several ViewState methods have been modified to accept a ViewChangeOptions parameter:

Controlling Schedule Script Playback

The internal Viewport.scheduleScriptFraction property has been replaced by Viewport.timePoint, which specifies the current point in time along the viewport's RenderSchedule script timeline, in Unix seconds.

PropertyRecord classes moved to ui-abstract package

This includes the classes in the following files:

  • Description.ts
  • EditorParams.ts
  • PrimitiveTypes.ts
  • Record.ts
  • Value.ts

The deprecated ToolSettingsValue.ts has been removed.

React dependency changes

For the iModel.js Ui packages, react, react-dom, redux and react-redux dependencies were moved from dependencies to peerDependencies. The react-redux package was upgraded to "^7.2.0".

Applications with a dependency on the Ui packages must ensure their minimum versions are setup like below -

    "dependencies": {
      ...
      "react": "^16.8.0",
      "redux": "^4.0.3",
      "react-dom": "^16.8.0",
      "react-redux": "^7.2.0"
      ...
    },

Changes in @bentley/ui-core

Removal of Deprecated APIs

The following items that were marked as @deprecated in the 1.x time frame have been removed:

  • UiError (use UiError in @bentley/ui-abstract instead)
  • Position for Popup component (Use RelativePosition in @bentley/ui-abstract instead)

Changes in @bentley/ui-components

Hard Deprecations

A couple of already @deprecated APIs are now being hard-deprecated by adding a DEPRECATED_ prefix to increase awareness about future removal of the APIs:

  • Tree to DEPRECATED_Tree. Recommended replacement - ControlledTree.
  • withTreeDragDrop to DEPRECATED_withTreeDragDrop. We don't have a replacement for that yet.

As a short term solution, you can simply do a rename when importing the package, e.g.:

import { DEPRECATED_Tree as Tree } from "@bentley/ui-components";

Removals and Changes

  • Renamed ITreeNodeLoaderWithProvider.getDataProvider() to ITreeNodeLoaderWithProvider.dataProvider.
  • Renamed PagedTreeNodeLoader.getPageSize() to PagedTreeNodeLoader.pageSize.
  • Renamed TreeCheckboxStateChangeEvent to TreeCheckboxStateChangeEventArgs.
  • Renamed TreeNodeEvent to TreeNodeEventArgs.
  • Renamed TreeSelectionModificationEvent to TreeSelectionModificationEventArgs.
  • Renamed TreeSelectionReplacementEvent to TreeSelectionReplacementEventArgs.
  • Renamed useModelSource to useTreeModelSource.
  • Renamed useNodeLoader to useTreeNodeLoader.
  • Renamed usePagedNodeLoader to usePagedTreeNodeLoader.
  • Changed IPropertyValueRenderer.render to only be allowed to return ReactNode (do not allow Promise<ReactNode> anymore). This makes the calling code much simpler at the cost of a few more complex renderers. To help handle async rendering, a helper useAsyncValue hook has been added. Example usage:

    import { useAsyncValue } from "@bentley/ui-components";
    const MyComponent = (props: { asyncValue : Promise<string> }) => {
      const value = useAsyncValue(props.asyncValue);
      return value ?? "Loading...";
    };
  • Changed type of label attribute from string to PropertyRecord for these types:

    • BreadcrumbNodeProps
    • TreeModelNode
    • MutableTreeModelNode
    • TreeModelNodeInput

    Also removed labelDefinition attribute in PropertyData and TreeNodeItem in favor of label whose type changed from string to PropertyRecord.

    To render PropertyRecords we suggest using PropertyValueRendererManager API:

    import { PropertyValueRendererManager } from "@bentley/ui-components";
    const MyComponent = (props: { label: PropertyRecord }) => {
      return PropertyValueRendererManager.defaultManager.render(props.label);
    };
  • Removed HorizontalAlignment. Use the HorizontalAlignment in @bentley/ui-core instead.

Changes in @bentley/ui-framework

Renames

A couple of types were renamed to better match their intention:

  • VisibilityTree to ModelsTree
  • IModelConnectedVisibilityTree to IModelConnectedModelsTree

Removal of Deprecated APIs

The following items that were marked as @deprecated in the 1.x time frame have been removed:

  • FrontstageDef.inheritZoneStates (never implemented in iModel.js)
  • FrontstageDef.hubEnabled (never implemented in iModel.js)
  • FrontstageDef.contextToolbarEnabled (never implemented in iModel.js)
  • IconSpec (Use IconSpec in @bentley/ui-core instead)
  • IconProps (Use IconProps in @bentley/ui-core instead)
  • Icon (Use the Icon component in @bentley/ui-core instead)
  • ItemDefBase.betaBadge (use badgeType instead)
  • ItemProps.betaBadge (use badgeType instead)
  • WidgetDef.betaBadge (use badgeType instead)
  • WidgetProps.betaBadge (use badgeType instead)

Other changes

  • Added support for a StateManager class that can be instantiated by the host application. This manager provides centralized state management using Redux actions, reducers and store. This class monitors the ReducerRegistry and will automatically update the Redux store when a new reducer is registered. This allows the store to be incrementally constructed as modules and extensions are loaded.

  • Removed useControlledTree flag from the following Prop types:

    • CategoryTreeProps
    • ModelsTreeProps
    • SpatialContainmentTreeProps
    • VisibilityComponentProps

    The components now always use controlled tree as the internal tree implementation.

  • Removed UiFramework.getDefaultRulesetId() and UiFramework.setDefaultRulesetId(). Each component should decide what ruleset it wants to use.

  • Custom registered ToolUiProviders should now return a 'horizontalToolSettingNodes' property that contain an array of ToolSettingsEntry items. These items define the label and editor to use for each value when the Tool Settings container is in its default Horizontal orientation. The existing 'toolSettingsNode' property is still used to specify the UI if the Tool Settings are shown in a floating/rectangular container.

  • When using the DefaultToolSettingsProvider as specified in the ToolUiManager, the toolSettingsProperty argument to ToolUiManager.initializeToolSettingsData() has been changed from ToolSettingsPropertyRecord[] to DialogItem[]. DialogItem is an interface that you will find in the ui-abstract package in the file DialogItem.ts. The classes in the file ToolSettingsValue.ts have been deprecated and removed from the source tree.

Changes in @bentley/presentation-common

RPC Changes

The following endpoints have been either changed or removed:

  • PresentationRpcInterface.getDisplayLabel removed in favor of PresentationRpcInterface.getDisplayLabelDefinition.
  • PresentationRpcInterface.getDisplayLabels removed in favor of PresentationRpcInterface.getDisplayLabelDefinitions.
  • PresentationRpcInterface.getDisplayLabelsDefinitions renamed to PresentationRpcInterface.getDisplayLabelDefinitions.
  • RequestOptionsWithRuleset required either rulesetId or rulesetOrId (both optional). Now it only has a required attribute rulesetOrId.

In addition, support for stateful backed was removed.

Because of the above breaking PresentationRpcInterface changes its version was changed to 2.0.

Hard Deprecations

Some of already @deprecated APIs are now being hard-deprecated by adding a DEPRECATED_ prefix to increase awareness about future removal of the APIs:

  • AllInstanceNodesSpecification to DEPRECATED_AllInstanceNodesSpecification. Recommended replacement - InstanceNodesOfSpecificClassesSpecification.
  • AllRelatedInstanceNodesSpecification to DEPRECATED_AllRelatedInstanceNodesSpecification. Recommended replacement - RelatedInstanceNodesSpecification.
  • ChildNodeSpecificationTypes.AllInstanceNodes to ChildNodeSpecificationTypes.DEPRECATED_AllInstanceNodes. Recommended replacement - ChildNodeSpecificationTypes.InstanceNodesOfSpecificClasses.
  • ChildNodeSpecificationTypes.AllRelatedInstanceNodes to ChildNodeSpecificationTypes.DEPRECATED_AllRelatedInstanceNodes. Recommended replacement - ChildNodeSpecificationTypes.RelatedInstanceNodes.
  • PropertiesDisplaySpecification to DEPRECATED_PropertiesDisplaySpecification. Recommended replacement - using property overrides of PropertySpecification type with isDisplayed attribute.
  • PropertyEditorsSpecification to DEPRECATED_PropertyEditorsSpecification. Recommended replacement - using property overrides of PropertySpecification type with editor attribute.

As a short term solution, you can simply do a rename when importing the package, e.g.:

import { DEPRECATED_PropertiesDisplaySpecification as PropertiesDisplaySpecification } from "@bentley/presentation-common";

Removals and Changes

  • Removed @deprecated APIs: ECInstanceNodeKey, ECInstanceNodeKeyJSON, ECInstancesNodeKey.instanceKey, NodeKey.isInstanceNodeKey, StandardNodeTypes.ECInstanceNode. Instead, the multi-ECInstance type of node should be used, since ECInstance nodes may be created off of more than one ECInstance. Matching APIs: ECInstancesNodeKey, ECInstancesNodeKeyJSON, ECInstancesNodeKey.instanceKeys, NodeKey.isInstancesNodeKey, StandardNodeTypes.ECInstancesNode.
  • Removed Item.labelDefinition and Node.labelDefinition. Instead, Item.label and Node.label should be used, whose type has been changed from string to LabelDefinition. The LabelDefinition type has not only display value, but also raw value and type information which allows localizing and formatting raw value.
  • Removed PersistentKeysContainer. Instead, KeySetJSON should be used.
  • Changed RulesetsFactory.createSimilarInstancesRuleset to async. Removed RulesetsFactory.createSimilarInstancesRulesetAsync.

Changes in @bentley/presentation-backend

Removals and Changes

  • PresentationManager.getDisplayLabel was removed in favor of PresentationManager.getDisplayLabelDefinition
  • PresentationManager.getDisplayLabels was removed in favor of PresentationManager.getDisplayLabelDefinitions
  • PresentationManager.getDisplayLabelsDefinitions was renamed to PresentationManager.getDisplayLabelDefinitions
  • RulesetEmbedder now takes a "props" object instead of arguments' list in its constructor. Example fix:

    const embedder = new RulesetEmbedder({ imodel });

    instead of:

    const embedder = new RulesetEmbedder(imodel);

Changes in @bentley/presentation-frontend

Removals and Changes

  • Presentation.initialize is now async.
  • PresentationManager.getDisplayLabel was removed in favor of PresentationManager.getDisplayLabelDefinition
  • PresentationManager.getDisplayLabels was removed in favor of PresentationManager.getDisplayLabelDefinitions
  • PresentationManager.getDisplayLabelsDefinitions was renamed to PresentationManager.getDisplayLabelDefinitions
  • PersistenceHelper was removed in favor of KeySet and its toJSON and fromJSON functions.
  • HiliteSetProvider and SelectionHandler now take a "props" object instead of arguments' list in constructor. Example fix:

    const provider = new HiliteSetProvider({ imodel });

    instead of:

    const provider = new HiliteSetProvider(imodel);

Changes in @bentley/presentation-components

Hard Deprecations

Some of already @deprecated APIs are now being hard-deprecated by adding a DEPRECATED_ prefix to increase awareness about future removal of the APIs:

  • controlledTreeWithFilteringSupport renamed to DEPRECATED_controlledTreeWithFilteringSupport. Recommended replacement - useControlledTreeFiltering React hook.
  • controlledTreeWithVisibleNodes renamed to DEPRECATED_controlledTreeWithVisibleNodes. This HOC is completely unnecessary when using React hooks which is what we recommend to use with the ControlledTree component.
  • treeWithFilteringSupport renamed to DEPRECATED_treeWithFilteringSupport. Recommended replacement - ControlledTree and useControlledTreeFiltering React hook.
  • treeWithUnifiedSelection renamed to DEPRECATED_treeWithUnifiedSelection. Recommended replacement - ControlledTree and useUnifiedSelectionTreeEventHandler React hook.

As a short term solution, you can simply do a rename when importing the package, e.g.:

import { DEPRECATED_treeWithUnifiedSelection as treeWithUnifiedSelection } from "@bentley/presentation-components";

Removals and Changes

  • All presentation data providers that used to memoize all requests now only memoize the last one to preserve consumed memory.
  • All presentation data providers are now IDisposable. This means their dispose() method has to be called whenever they're stopped being used. In the context of a hooks-based React component this can be done like this:

    import { useDisposable } from "@bentley/ui-core";
    import { IPresentationDataProvider } from "@bentley/presentation-components";
    const MyComponent = () => {
      const dataProvider = useDisposable(useCallback(() => createSomeDataProvider(), []));
      // can use `dataProvider` here - it'll be disposed as needed
    };

    In a class based React component the providers have to be disposed in either componentWillUnmount or componentDidUpdate callbacks, whenever the provider becomes unnecessary.

  • APIs that now take a "props" object instead of arguments' list:

    • ContentDataProvider
    • PresentationLabelsProvider
    • PresentationPropertyDataProvider
    • PresentationTreeDataProvider
    • useControlledTreeFiltering
    • useUnifiedSelectionTreeEventHandler Example fix:
    const provider = new PresentationLabelsProvider({ imodel });

    instead of:

    const provider = new PresentationLabelsProvider(imodel);
  • Removed FavoritePropertiesDataProvider.customRulesetId. Now it can be supplied when constructing the provider through FavoritePropertiesDataProviderProps.ruleset.

  • Renamed LabelsProvider to PresentationLabelsProvider.
  • Renamed PresentationNodeLoaderProps to PresentationTreeNodeLoaderProps.
  • Renamed PresentationTreeNodeLoaderProps.rulesetId to PresentationTreeNodeLoaderProps.ruleset.
  • Renamed usePresentationNodeLoader to usePresentationTreeNodeLoader.
  • Renamed useUnifiedSelectionEventHandler to useUnifiedSelectionTreeEventHandler.
  • Removed attributes from UnifiedSelectionTreeEventHandlerParams: dataProvider, modelSource. They're now taken from nodeLoader.

Changes in @bentley/presentation-testing

Removals and Changes

  • initialize helper function is now async.
  • ContentBuilder and HierarchyBuilder now take a "props" object instead of arguments' list. Example fix:

    const builder = new ContentBuilder({ imodel, dataProvider });

    instead of:

    const builder = new ContentBuilder(imodel, dataProvider);

Changes in @bentley/geometry-core

Remove deprecated methods

  • CurveCurve intersection methods that formerly returned a pair of arrays (with matched length and corresponding CurveLocationDetail entries) now return a single array whose entries each have the two corresponding CurveLocationDetails.
    • The affected methods are
      • For computing intersections among of the (simple xy projection of) curves:
        • old CurveCurve.intersectionXY (...) : CurveLocationDetailArrayPair
        • new CurveCurve.intersectionXYPairs (...) : CurveLocationDetailPair[]
      • For computing intersections among projections of curves with 4d (i.e. perspective) projection:
        • old CurveCurve.intersectionProjectedXY (...) : CurveLocationDetailArrayPair
        • new CurveCurve.intersectionProjectedXYPairs (...) : CurveLocationDetailPair[]
    • If oldIntersections is the old pair of arrays and newIntersections is the new array of pairs,
      • change oldIntersections.dataA[i]
      • to newIntersections[i].detailA
      • old intersections.dataB[i]
      • to newIntersections[i].detailB
    • The method CurveCurveIntersectXY.grabResults() is removed -- grabPairedResults is the modernized form.
  • GrowableXYZArray method myArray.distance(i,j) for distance between points identified by indices (i,j) is replaced by
    • old: myArray.distance(i,j)
    • new: myArray.distanceIndexIndex(i,j)
    • this clarifies the difference among distance methods:
      • myArray.distanceIndexToPoint(i: number, point: Point3d)
      • myArray.distanceSquaredIndexIndex(i: number, point: Point3d)
  • In PointHelpers, the methods to parse variant XYZ data are removed.
    • In the replacements, the original single callback is replaced by an callback object that can receive start-end callbacks in addition to the primary xyz or xyz pair callback.
    • old Point3dArray.streamXYZ (.. ) is removed.
    • new VariantPointDataStream.streamXYZ (..)
    • old Point3dArray.streamXYZXYZ(..) is removed.
    • new VariantPointDataStream.streamXYZXYZ (..)
  • PolyfaceBuilder
    • old (improperly cased) method findOrAddNormalnLineString is removed. The properly spelled replacement is findOrAddNormalInLineString (note the added capital I)
  • ArcSweep
    • old (improperly cased) method radiansArraytoPositivePeriodicFractions is renamed radiansArrayToPositivePeriodicFractions (Note the capital "T")
  • ClipPlane
    • ClipPlane supports the PlaneAltitudeEvaluator interface.
    • This allows services previously seen as specific to ClipPlane to be supported with other plane types (especially Plane3dByOriginAndNormal)
    • old instance method myClipPlane.evaluatePoint(point) is replaced by myClipPlane.altitude (point)
    • old instance method myClipPlane.dotProductVector(point) is replaced by myClipPlane.velocity (point)
    • old instance method myClipPlane.convexPolygonClipInPlace (. . ) is replaced by (static) Point3dArrayPolygonOps.convexPolygonClipInPlace
    • old instance method myClipPlane.polygonCrossings(polygonPoints, crossings) is replaced by (static) Point3dArrayPolygonOps.polygonPlaneCrossings (clipPlane, polygonPoints, crossings)`
    • old static method myClipPlane.intersectRangeConvexPolygonInPlace (. . ) is replaced by (static) IndexedXYZCollectionPolygonOps.intersectRangeConvexPolygonInPlace
  • Various methods (usually static createXXXX) that accept MultiLineStringDataVariant have previously been marked as deprecated, with replacement methods that include the words FromVariant data. This decision has been reversed, and that short names are again considered ok. This includes:
    • LineString3d.createArrayOfLineString3d (variantLinestringData:MultiLineStringDataVariant): LineString3d[] is un-deprecated, and the mangled name createArrayOfLineString3dFromVariantData is deprecated.

Various method name changes (breaking)

  • On Arc3d, there were previously (confusingly) two "get" properties for the internal matrix of the Arc3d.
    • arc.matrixRef
      • This returns a reference to the matrix.
      • This is not changed.
      • Direct access to the matrix is dangerous, but the "Ref" qualifier makes that clear.
    • arc.matrix
      • this formerly returned a clone of the matrix.
      • Cloning is expensive.
      • this is removed.
      • It is replaced by a method (not property) arc3d.matrixClone ()
      • arc3d.matrixClone() clones the matrix.
    • Ellipsoid API changed to eliminate use of Point2d as carrier for a pair of angles.
      • angle pairs are instead returned in strongly typedLongitudeLatitudeNumber objects.
      • method ellipsoid.surfaceNormalToRadians is removed. Use ellipsoid.surfaceNormalToAngles for the same result in proper LongitudeLatitudeNumber form.
      • method ellipsoid.intersectRay (ray, rayFractions[], xyz[], thetaPhiRadians[]) now returns thetaPhiRadians as an array of strongly typed LongitudeLatitudeNumber.
        • Changes made to callers in core\frontend\BackgroundMapGeometry.ts
      • SurfaceLocationDetail has a new (constructor-like) static method createUVNumbersPoint (surface, u, v, point) with the surface u and v parameters as separate numeric values (previously packaged as Point2d)
      • SphereImplicit.intersectSphereRay returns its surface angle data as LongitudeLatitudePoint instead of as Point2d.

Bug fixes

  • Apply on-plane tolerances in mesh-plane clip.
  • ClipUtils.selectIntervals01 announces fractional intervals of clipped geometry. The logic previously considered any non-zero interval to be a valid candidate, resulting in extremely small arcs with seep angle less than the smallest non-zero angle. This logic is modified so that any fractional interval shorter than Geometry.smallFractionalResult is ignored.

Small angle arc issues

  • Angle methods for converting an angle to a fraction of an AngleSweep interval have added args to specify default return value on near-zero length intervals.
    • static AngleSweep.radiansToPositivePeriodicFraction(radians: number, zeroSweepDefault?: number): number;
    • instance method angleSweep.radiansToPositivePeriodicFraction(radians: number, zeroSweepDefault?: number): number;
  • static const Geometry.smallFraction is a typical value (1.0e-10) to consider a fraction interval small.

Polyface clipping

  • New class ClippedPolyfaceBuilders is carries output data and options from clip operations
  • static ClipUtilities.announceLoopsOfConvexClipPlaneSetIntersectRange(convexSet: ConvexClipPlaneSet | ClipPlane, range: Range3d, loopFunction: (loopPoints: GrowableXYZArray) => void, includeConvexSetFaces?: boolean, includeRangeFaces?: boolean, ignoreInvisiblePlanes?: boolean): void;
    • Accepts simple ClipPlane in addition to ConvexClipPlaneSet
  • static ClipUtilities.loopsOfConvexClipPlaneIntersectionWithRange(allClippers: ConvexClipPlaneSet | UnionOfConvexClipPlaneSets | ClipPlane, range: Range3d, includeConvexSetFaces?: boolean, includeRangeFaces?: boolean, ignoreInvisiblePlanes?: boolean): GeometryQuery[]
    • accepts UnionOfConvexClipPlaneSets and ClipPlane in addition to ConvexClipPlaneSet
  • static ConvexClipPlaneSet.clipInsidePushOutside(xyz: GrowableXYZArray, outsideFragments: GrowableXYZArray[], arrayCache: GrowableXYZArrayCache): GrowableXYZArray | undefined;
    • clips xyz polygon to the clip plane set. Inside result is the return argument; outside fragments (possibly many) are pushed to the outsideFragments array (which is not cleared first.)
  • PolyfaceClip methods to do both inside and outside clip of polyfaces.
    • static PolyfaceClip.clipPolyfaceClipPlane(polyface: Polyface, clipper: ClipPlane, insideClip?: boolean, buildClosureFaces?: boolean): Polyface;
    • static PolyfaceClip.clipPolyfaceClipPlaneWithClosureFace(polyface: Polyface, clipper: ClipPlane, insideClip?: boolean, buildClosureFaces?: boolean): Polyface;

Remove deprecated API

  • Class SchemaFileLocater has been moved to the ecschema-locaters package.
  • Class SchemaJsonFileLocater has been moved to the ecschema-locaters package.
  • Class SchemaFileLocater has been moved to the ecschema-locaters package.
  • In Schema, the methods for (de)serializing to/from JSON have been renamed.
    • Schema.toJson() renamed to Schema.toJSON()
    • Schema.deserialize(...) renamed to Schema.fromJSON(...)
    • Schema.deserializeSync(...) renamed to Schema.fromJSONSync(...)
  • In Property and all classes deriving from Property, the methods for (de)serializing to/from JSON have been renamed.
    • Property.toJson() renamed to Property.toJSON()
    • Property.deserialize(...) renamed to Property.fromJSON(s...)
    • Property.deserializeSync(...) renamed to Property.fromJSONSync(...)
  • In SchemaItem and all classes deriving directly or indirectly from SchemaItem, the methods for (de)serializing to/from JSON have been renamed.
    • SchemaItem.toJson() renamed to SchemaItem.toJSON()
    • SchemaItem.deserialize(...) renamed to SchemaItem.fromJSON(...)
    • SchemaItem.deserializeSync(...) renamed to SchemaItem.fromJSONSync(...)

Last Updated: 06 May, 2020