“Find my” app on iPhone would like to open “Apple maps” to navigate to search item. If you don’t have it installed, it will open AppStore.

Gmail, by default, asks you if you want to open link in Google Chrome. “Don’t you have Google Chrome installed? Sure, let me open AppStore for you!”

The question this article answers is how to do it in Xamarin?

Context

Recently, I’ve been working on a similar task:

as a user, I would like to open Google Maps from current app. If Google Maps is not installed, I would like Google Play/AppStore to be opened, so I can download the app.

I’ve been trying to find an article that describes how to do it in Xamarin, but didn’t find one… 😔

In this blog post you will get (almost) step by step solution how to achieve this in Xamarin.

How to do it?

The code we will need is a native one, hence we will need to have DependencyService so we can consume it. I will leave this part to you. As both platforms differ, you need to find your way how you would like to do this.

As always, I will cover only Android & iOS, so please forgive me if you write apps for Windows/Tizen.

Xamarin.iOS

Before we begin, you need to check how to open external app. Either you can search in documentation (if there is one) or you can talk to developers responsible for the app. Let’s take a look at opening Google Maps. In the documentation, you can find such stuff https://developers.google.com/maps/documentation/urls/ios-urlscheme. After reading this, you will know that to open the app, the deep link starts with “comgooglemaps://”. Having that in mind, let’s see the code on iOS.

private readonly Dictionary<string, string> _storesMapping = new Dictionary<string, string>(System.StringComparer.OrdinalIgnoreCase)
{
    { "comgooglemaps://", "id585027354" }
};

public void OpenAppOrStore(string uri)
{
    var canOpen = UIApplication.SharedApplication.CanOpenUrl(new NSUrl(uri));

    if (!canOpen && _storesMapping.TryGetValue(uri, out var appId))
    {
        UIApplication.SharedApplication.OpenUrl(new NSUrl($"itms-apps://itunes.apple.com/app/apple-store/{appId}"));
        return;
    }

    if (!canOpen)
    {
        return;
    }

    UIApplication.SharedApplication.OpenUrl(new NSUrl(uri));
}

So what’s happening here? Firstly we have app to store mapping. That’s a simple dictionary saying that Google maps app can be downloaded from AppStore by id. To get id, find the app in AppStore and check the link. The id is there waiting for you. After you have the id, the logic is straightforward. Simply try opening the app. If it does not work, open AppStore.

There is one more step you need to do, in order to get it working. In your info.plist add

<key>LSApplicationQueriesSchemes</key>
<array>
  <string>comgooglemaps</string>    
</array> 

Xamarin Android

On Android, the flow is the same. We check if the app is installed. If it is, let’s open it; otherwise let’s open app store. One thing worth mentioning here, is that once again we need to know what to open in the store if the app is not installed. This means we need to know what is the app bundle of our desired app. Once again, check that in Google Play store (or talk to the developers if you have access to them 😇).

public void OpenAppOrStore(string packageName)
{
    PackageManager? packageManager = Android.App.Application.Context.PackageManager;
    if (packageManager == null)
    {
        return;
    }

    if (!_storesMapping.TryGetValue(packageName, out var appPackageName))
    {
        return;
    }

    Intent? intent;
    var isAppInstalled = IsAppInstalled(appPackageName);
    if (isAppInstalled)
    {
        intent = packageManager.GetLaunchIntentForPackage(appPackageName);
    }
    else
    {
        intent = new Intent(Intent.ActionView);
        intent.SetData(Uri.Parse($"market://details?id={appPackageName}"));
    }
    
    intent!.AddFlags(ActivityFlags.NewTask);
    
    Android.App.Application.Context.StartActivity(intent);
}

public bool IsAppInstalled(string packageName)
{
    PackageManager? pm = Android.App.Application.Context.PackageManager;
    if (pm == null)
    {
        return false;
    }

    bool installed;
    try
    {
        pm.GetPackageInfo(packageName, PackageInfoFlags.Activities);
        installed = true;
    }
    catch (PackageManager.NameNotFoundException)
    {
        installed = false;
    }

    return installed;
}

private readonly Dictionary<string, string> _storesMapping = new Dictionary<string, string>
{
    { "comgooglemaps://", "com.google.android.apps.maps" }
};

What’s more, in AndroidManifest.xml we need to add following line (assuming we want to open Google maps);

<queries>
	<package android:name="com.google.android.apps.maps" />
</queries>

DependencyService

Having native code we need to wrap it abstraction that we can inject in our shared code. If you were reading the close carefully, we probably noticed that both on iOS & Android there is logic OpenAppOrStore(string packageName). We could put that in our interface.

public interace IPlatform
{
    void OpenAppOrStore(string packageName);
}

But how to consume it? Assuming we have same deep link for two platforms, we could simply do

_platform.OpenAppOrStore("comgooglemaps");

and it should open the app. If the links are different then you need to find a way on your own. Should not be that hard.

Header image from Pixabay

Leave a Reply

avatar