18 Jan 2018

Running Forge Viewer in React Native Offline

    This is a quick post to share a project I created few weeks ago as a proof of concept for a mobile app that can run the Forge Viewer offline. I used React Native as framework to build my app as it can generate native applications easily for both platforms Android and iOS while leveraging React syntax.

    Michael was writing an article on the topic a while ago that you can find there: Forge + React Native (AU Talk). The difference is that my project focuses only on the offline aspect.

    The Forge Viewer is a JavaScript component designed to run in a web page, so even when using a native app, we still need to run it inside a WebView. The main component allowing this to work is to run a local http server inside the React Native app which will serve the svf static resources to the viewer. For that purpose I used the following package react-native-static-server which appears to do the job pretty well. 

    The next step is to have access to an actual svf package of a Forge model, for that you can refer to my previous article: Forge SVF Extractor in Node.js or here if you prefer a .Net version. You can also upload your model to extract.autodesk.io if you don't want to write any code. 

    For this simple proof of concept I was manually placing the svf resources in the device storage (in that case the simulator) and didn't implement a download mechanism from Forge which would obviously increase the complexity, but there is no doubt about feasibility of that part... You can find the project at https://github.com/leefsmp/Forge-RNV and it already contains a demo model so you can easily setup React Native on your machine and test it on your end. Below is the React Native implementation of the App:

import StaticServer from 'react-native-static-server'
import React, { Component } from 'react'
import RNFS from 'react-native-fs'
import {
  StyleSheet,
  Platform,
  WebView,
  View,
  Text
} from 'react-native'

class ForgeViewer extends Component<{}> {

  constructor (props) {
    super (props)
    this.state = {
      uri: null
    }
  }

  getAssetsPath (platform) {
    switch (platform) {
      case 'ios':
        return RNFS.MainBundlePath + '/assets'
      case 'android':
        return RNFS.DocumentDirectoryPath
    }
  }

  componentDidMount () {
    const path = this.getAssetsPath (Platform.OS)
    this.server = new StaticServer(8080, path, {
      localOnly : true 
    })
    this.server.start().then((url) => {
      this.setState({
        uri: `file:///${path}/www/viewer.html`
      })
    }) 
  }

  render() {
    const {uri} = this.state
    return (
      uri &&
      <WebView
        javaScriptEnabled={true}
        scalesPageToFit={true}
        style={styles.webView}
        scrollEnabled={false}
        source={{uri}}
      />
    )
  }
}

const styles = StyleSheet.create({
  webView:{
    backgroundColor: '#FFFFFF',
    flex: 1
  }
})

export default class App extends Component<{}> {
  render() {
    return (
      <ForgeViewer/>  
    )
  }
}

     Here is a quick recording that showcases that demo: what you can see there is that it works in the iOS simulator and on Android, the simulator doesn't support running WebGL, so I am displaying an image instead which is loaded from the static resources served by the local server.

Related Article