Migrate from packaged to hosted app

Hi, all.

We have packaged app in store some years and now we are going to migrate from packaged to hosted app silently for our clients.

Current clients have authenticated data in localStorage and we should migrate this data to hosted app (domain). We wrote migration mechanism through query string. But we faced that the data from localStorage does not captured before redirecting to hosted app sometimes.

How we can be sure that the app is loaded and localStorage is ready for use before redirecting to hosted app?

I can provide some code of the new packaged app and hosted app and mechanism of the migration localStorage data if it is needed.

Thanks.

How about using the load event?

<script>
window.addEventListener("load", (event) => {
  // your code
  window.location.href = "your URI";
});
</script>

We are using react right now (plan migrate to preact) and use componentDidMount lifecycle for getting data from localStorage and redirecting to hosted app. But in some cases data from localStorage is empty.

What can be wrong in that case?

Thanks.

Is there an event listener in componentDidMount?

No, there is.

It is just simple app component:

import React, { Component } from 'react';

import Preloader from './components/preloader';
import Error from './components/error';
import ping from './utils/ping';
import { HOSTED_APP_URL, INTERNET_CONNECTION_URL, ERROR_REASONS } from './constants';

export default class App extends Component {
  state = {
    errorReason: null,
  };

  componentDidMount() {
    this.init();
  }

  init = () => {
    ping(HOSTED_APP_URL)
      .then((isHostedAppAvailable) => {
        if (isHostedAppAvailable) {
          return true;
        }

        return ping(INTERNET_CONNECTION_URL)
          .then((isInternetAvailable) => {
            this.setState({
              errorReason: isInternetAvailable
                ? ERROR_REASONS.unavailableHostedApp
                : ERROR_REASONS.noInternetConnection,
            });

            return false;
          });
      })
      .then((canRedirect) => {
        if (canRedirect) {
          this.redirectToHosedAppUrl();
        }
      });
  };

  redirectToHosedAppUrl() {
    const migrationData = this.getMigrationData();

    location.href = `${HOSTED_APP_URL}${migrationData ? `?migration=${migrationData}` : ''}`;
  }

  getMigrationData() {
    try {
      const migrationData = encodeURI(JSON.stringify(localStorage));

      localStorage.clear();

      return migrationData;
    } catch {
      return null;
    }
  }

  render() {
    const { errorReason } = this.state;

    return errorReason ? (
      <Error errorReason={errorReason} handleRepeat={this.init} />
    ) : <Preloader />;
  }
}

You need to add an event listener to that method. Find more information on Google.

By the time react app is finished loading localStorage API should be ready, it’s not async in any way.

Is this issue related to webos version or is there any other pattern?
How did you found out that data from localStorage is empty, by debugging on device or by checking request URL on hosted application?
Could you try picking only necessary items from local storage instead of URL encoding whole local storage? Maybe there is some kind of URL length limit somewhere

@narae0.kim thanks for your response.
I did what you suggest and it’s only decrease the attempts of the redirecting without localStorage data. Unfortunately, this is not a complete solution.

@ivmilicevic hello. I’m checking on the webOS 3.0, 4.0, 5.0, 6.0. On the webOS 3.0 always without localStorage data. On another versions sometimes. I’ve added log for data from localStorage and it’s empty sometimes. I don’t understand why.

Unfortunately I don’t have any other ideas.

We have done similar migration and haven’t encountered any problems. Only difference is that we are reading only single value from local storage and it’s pretty short (device identificator).


Is there any chance that you are completely uninstalling application instead of upgrading it in place?

@ivmilicevic we always install application (update) and user logged in. But when migrate then the data is lost

As our guides are based on standard web APIs, we are not sure where you are getting the problem. Please refer to Web Storage sample app. It works without issues. Thank you.

Hi @narae0.kim thanks for your response. I’ve investigated all resources and samples. And I don’t understand why sometimes localStorage is empty. I added event listener to the app:

class App extends Component {
  state = {
    errorReason: null,
  };

  componentDidMount() {
    window.addEventListener('load', this.handleLoad);
  }

  componentWillUnmount() {
    window.removeEventListener('load', this.handleLoad);
  }

  init = () => {
    ping(HOSTED_APP_URL)
      .then((isHostedAppAvailable) => {
        if (isHostedAppAvailable) {
          return true;
        }

        return ping(INTERNET_CONNECTION_URL)
          .then((isInternetAvailable) => {
            this.setState({
              errorReason: isInternetAvailable
                ? ERROR_REASONS.unavailableHostedApp
                : ERROR_REASONS.noInternetConnection,
            });

            return false;
          });
      })
      .then((canRedirect) => {
        if (canRedirect) {
          this.redirectToHosedAppUrl();
        }
      });
  };

  handleLoad = () => {
    this.init();
  };

  redirectToHosedAppUrl() {
    const migrationData = this.getMigrationData();

    location.href = `${HOSTED_APP_URL}${migrationData ? `?migration=${migrationData}` : ''}`;
  }

  getMigrationData() {
    try {
      const migrationData = encodeURI(JSON.stringify(localStorage));

      localStorage.clear();

      return migrationData;
    } catch {
      return null;
    }
  }

  render() {
    const { errorReason } = this.state;

    return errorReason ? (
      <Error errorReason={errorReason} handleRepeat={this.init} />
    ) : <Preloader />;
  }
}

And now our test result:

Always reproduces for webos 3.0, 6.0.

On other TVs sometimes reproduces ~ 1 from 4 times

As we said, our guides are based on standard web APIs. We cannot debug your app. Please check the sample app mentioned above and let us know if it has the same issue.

Do you mean “without react”? And I should try to create poor js app for validation?

We meant you could make a test sample based on Web Storage. Please check if the issue is reproduced with the sample. If it is not, it means the issue is with your app, not the webOS TV.

@narae0.kim thanks for the response. I’ll try to create native js app with event listener load and respond after testing here.

@narae0.kim hello. I’ve created and tested native js app and the result the same.

import {
  ERROR_MESSAGES,
  ERROR_REASONS,
  HOSTED_APP_URL,
  INTERNET_CONNECTION_URL,
} from './constants';
import ping from './utils/ping';

import './styles/style.less';

const getLocalStorageData = () => {
  try {
    const localStorageData = encodeURI(JSON.stringify(localStorage));

    localStorage.clear();

    return localStorageData;
  } catch {
    return null;
  }
};

const redirectToHosedAppUrl = () => {
  const migrationData = getLocalStorageData();

  location.href = `${HOSTED_APP_URL}${migrationData ? `?migration=${migrationData}` : ''}`;
};

const renderError = (errorReason) => {
  const errorMessage = ERROR_MESSAGES[errorReason];

  document.getElementById('root').innerHTML = `<div class="error-msg">${errorMessage}</div>`;
};

const run = () => {
  let errorReason;

  ping(HOSTED_APP_URL)
    .then((isHostedAppAvailable) => {
      if (isHostedAppAvailable) {
        return true;
      }

      return ping(INTERNET_CONNECTION_URL)
        .then((isInternetAvailable) => {
          errorReason = isInternetAvailable
            ? ERROR_REASONS.unavailableHostedApp
            : ERROR_REASONS.noInternetConnection;

          return false;
        });
    })
    .then((canRedirect) => {
      if (canRedirect) {
        redirectToHosedAppUrl();
      } else {
        renderError(errorReason);
      }
    });
};

window.addEventListener('load', () => {
  run();
});

Could you please suggest how we can wait localStorage data. We can’t release our app with this issue (

The webOS TV apps share one local storage. Since webOS TV 3.5, the local storage size is limited to 16MB, and the data can be deleted by the “Initial setting” menu (factory reset) and depending on the following app types and conditions.
Packaged app: The data is deleted when users update or remove your app on webOS TV.
Hosted app: The data is deleted when the total usage of data reaches the limit.
Therefore, you should consider the case where the data is not in the local storage.

window.addEventListener("load", () => {
  if (localStorage.datacheck) {
    localStorage.datacheck = Number(localStorage.datacheck) + 1;
  } else { // This means there is no data, not that it isn't loaded.
    localStorage.datacheck = 1;
  }
  console.log(localStorage.datacheck);
});

@narae0.kim Hi, I am working alongside with Sergey. We discovered one thing that happens with our apps - when we update an application and change it’s version it runs twice, so all javascript code is performed twice. Is it regular behaviour after update or there may be any additional things like active developer mode that may affect on application? Could you please provide some information about this?