Understanding Why JavaScript Auto-Redirects Fail

If you have tried using custom URI schemes like nameofmyapp:// combined with JavaScript's setTimeout() to open your mobile game from a browser, you likely noticed it doesn't work reliably. Modern mobile operating systems (iOS and Android) and browsers (Safari, Chrome) actively block auto-executing custom schemes on page load to protect security and user experience.

Using legacy setTimeout() hacks often triggers unwanted app store redirects even when the app opens, or results in broken dialog popups. To reliably open your app from a website, you need to transition to native deep linking mechanisms.

The Modern Solution: Universal Links and App Links

Rather than relying on custom URI schemes, modern web-to-app deep linking uses standard HTTP/HTTPS links verified by both your domain and your mobile application:

  • iOS Universal Links: Standard web URLs that open your iOS app directly if installed, falling back gracefully to the web page in Safari if not.
  • Android App Links: Android's equivalent to Universal Links, opening your app directly without prompting the user with an app picker dialog.

1. Setting Up iOS Universal Links

To enable Universal Links, you must update both your iOS app project and your web server:

  • In Xcode: Enable the Associated Domains capability and add applinks:yourdomain.com to your target.
  • On your server: Host an apple-app-site-association file (without an extension) at https://yourdomain.com/.well-known/apple-app-site-association that links your App ID to specific URL path patterns.

2. Setting Up Android App Links and Chrome Intent URLs

For Android App Links, host an assetlinks.json file at https://yourdomain.com/.well-known/assetlinks.json containing your app package name and SHA256 fingerprint.

Alternatively, Chrome on Android supports Intent URIs, which natively handle fallback behavior to the Google Play Store if the app isn't installed:

<a href="intent://open#Intent;scheme=nameofmyapp;package=com.company.nameofmyapp;S.browser_fallback_url=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.company.nameofmyapp;end">Launch Game</a>

Implementing Best Practices in JavaScript

Browsers generally require deep links to be triggered by explicit user interaction (such as tapping a button) rather than executing automatically when the page loads.

function openMobileGame() {
  const userAgent = navigator.userAgent || navigator.vendor || window.opera;
  const isAndroid = /android/i.test(userAgent);
  const isIOS = /iPad|iPhone|iPod/.test(userAgent) && !window.MSStream;

  if (isAndroid) {
    // Intent URL automatically opens the app or redirects to Play Store
    window.location.href = "intent://open#Intent;scheme=nameofmyapp;package=com.company.nameofmyapp;S.browser_fallback_url=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.company.nameofmyapp;end";
  } else if (isIOS) {
    // Universal Link configured for your iOS app
    window.location.href = "https://yourdomain.com/play";
  } else {
    // Fallback for desktop visitors
    window.location.href = "https://yourdomain.com";
  }
}

Alternative: Use Deep Linking Platforms

If configuring native domain association files and handling browser edge cases feels overwhelming, consider utilizing deep linking platforms such as Branch.io or AppsFlyer. These services handle fallback logic, deferred deep linking (opening specific game levels post-install), and browser updates automatically.