Open Source & Free  

TIP: Cross Platform Update Available Strategy

TIP: Cross Platform Update Available Strategy

Header Image

One of the nice things in mobile development vs. desktop is the fact that updates are seamless. We supposedly
don’t need to worry about them and most newer OS’s turn them on by default. This keeps are users with the latest
version which is important, e.g. if we fixed a crucial bug or added a new monetization option…​

However, the reality rarely fits into this nice image. In practice an update can be delayed because of permission
changes user settings and many other problems. As a result old and even discontinued apps can still exist on
user devices and give you a hard time with user complaints.

We still get notifications from apps that haven’t been listed in the stores for over 2 years!

Over the years we developed a policy of update management that handles all potential edge cases:

  • Update recommended – we have a new update out and we want you to switch to it but it isn’t crucial

  • Update required – if you don’t update now we’d rather the app stops working

  • Switch App recommended – This app was discontinued, we suggest you switch to a new app. This can
    happen when a product line changes or even if you lose your certificate an can no longer update the app

  • Switch App required – This app was discontinued but we have this alternative app you can migrate to, the
    app will stop working and activating it will launch that URL after an error message

  • App was discontinued but you can keep using for now

  • App was discontinued and will no longer work

Those are all potential outcomes that we can forsee but there are a lot of things we can’t forsee so we need
to make this process as generic as possible.

The Solution

We store properties files in our servers as plain text, this is easy and requires almost no configuration. These
properties files use the following naming convention: AppName-OS-Status.properties.

Inside the properties file we have the following settings:

  • deprecatedVersion – the version number we no longer support but still run

  • derprecatedMessage – a message to show users of the deprecated version

  • derprecatedAltURL – URL to send users of the deprecated version to

  • unsupportedVersion – the version we no longer support

  • unsupportedMessage – a message we show to users of the unsupported version

  • unsupportedAltURL – URL to send users of the unsupported version

We can leave most properties blank if they aren’t applicable and might not even have a file.

Host these files in your own domain under your control. A dropbox URL might change and might be blocked in
some cases and the same is true for github

The Code

Notice that this code needs to execute after the first form was shown and not in the init(Object) method.
It will fail gracefully and the app will keep working if there is no Internet connection.

String url = "https://myurl.com/mydir/" + Display.getInstance().getProperty("AppName", "MyApp") +
        "-" + Display.getInstance().getPlatformName() + "-Status.properties";
Log.p("Checking update URL: " + url);
ConnectionRequest c = new ConnectionRequest(url, false) {
    private Properties props;
    @Override
    protected void readResponse(InputStream input) throws IOException {
        props = new Properties();
        props.load(input);
    }

    @Override
    protected void postResponse() {
        if(props != null) {
            float version = Float.parseFloat(Display.getInstance().getProperty("AppVersion", "1.0"));
            float unsupportedVersion = Float.parseFloat(props.getProperty("unsupportedVersion", "-1"));
            if(version <= unsupportedVersion) {
                String message = props.getProperty("unsupportedMessage", "The current application version is no longer supported");
                String url = props.getProperty("unsupportedAltURL", null);
                if(url == null) {
                    Dialog.show("Unsupported Version", message, "OK", null);
                    Display.getInstance().exitApplication();
                } else {
                    if(!Dialog.show("Unsupported Version", message, "OK", "Exit")) {
                        Display.getInstance().exitApplication();
                    }
                    Display.getInstance().execute(url);
                    Display.getInstance().exitApplication();
                }
            }
            float deprecatedVersion = Float.parseFloat(props.getProperty("deprecatedVersion", "-1"));
            if(version <= deprecatedVersion) {
                String message = props.getProperty("derprecatedMessage", "A new version of the app is available, please update");
                String url = props.getProperty("derprecatedAltURL", null);
                if(url == null) {
                    Dialog.show("New Version", message, "OK", null);
                } else {
                    if(Dialog.show("New Version", message, "Download", "OK")) {
                        Display.getInstance().execute(url);
                    }
                }
            }
        }
    }

};
c.setFailSilently(true);
NetworkManager.getInstance().addToQueue(c);

Now you will need properties files matching these URL’s, notice that the name is OS specific to allow you to have
different version deprecation policies for different OS’s (e.g. for the case of losing the Android certificate).

2 Comments

  • Jérémy MARQUER says:

    Interesting post but what about offline app or online app that can work offline ? I mean, for example, exiting application in case of unsupported version will work only if device is connected …

  • Shai Almog says:

    This fails silently when offline.

    I wanted to keep the code simple so I didn’t go into the more complex situation of a “user turns on airplane mode to use the app”. I think that if a user resorts to that rather than just update the app he’ll also download an illegal APK/IPA and jailbrake his device so this ventures into another domain.

Leave a Reply