In this blog post, I will show you how to open external application from Xamarin.Forms application.
My demo application will try to open locally installed application, otherwise it will open app store web page where app can be downloaded and installed.
So, let’s start.
First, I create Xamarin Forms application targeting Android and iOS. In Visual studio this is trivial: New project-> Xamarin Forms -> and then select one of proposed templates.
Application launching procedure is different between platforms. Therefore, I will implement platform specific service for this task. First, I need definition of my service in my common project:
1 2 3 4 5 6 7 8 9 |
using System.Threading.Tasks; namespace Jenx.AppOpenDemo.Services { public interface IOpenExternalAppService { Task<bool> LaunchApp(string uri); } } |
Let’s start with platform specific implementation of this service interface.
Android implementation
I want go into details, but I think the code below is straightforward and enough self-descriptive:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
using System.Threading.Tasks; using Android.Content; using Android.Content.PM; using Jenx.AppOpenDemo.Services; using Application = Android.App.Application; namespace Jenx.AppOpenDemo.Droid.Services { public class OpenExternalAppService : IOpenExternalAppService { public Task<bool> LaunchApp(string packageName) { var result = false; try { var pm = Application.Context.PackageManager; if (pm != null && IsAppInstalled(packageName)) { var intent = pm.GetLaunchIntentForPackage(packageName); if (intent != null) { intent.SetFlags(ActivityFlags.NewTask); Application.Context.StartActivity(intent); result = true; } } } catch { // something went wrong... } return Task.FromResult(result); } private static bool IsAppInstalled(string packageName) { var installed = false; try { var pm = Application.Context.PackageManager; if (pm != null) { pm.GetPackageInfo(packageName, PackageInfoFlags.Activities); installed = true; } } catch { // something went wrong... } return installed; } } } |
Ok, I have Android launcher code ready. Now, I need to register this service in my Dependency Container to be accessible also on my shared code where my main logic is. You can find service registration in line 27 in code below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
using Android.App; using Android.Content.PM; using Android.OS; using Jenx.AppOpenDemo.Droid.Services; using Jenx.AppOpenDemo.Services; using Xamarin.Forms; namespace Jenx.AppOpenDemo.Droid { [Activity(Label = "Jenx AppOpenDemo", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )] public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity { protected override void OnCreate(Bundle savedInstanceState) { TabLayoutResource = Resource.Layout.Tabbar; ToolbarResource = Resource.Layout.Toolbar; base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); Forms.Init(this, savedInstanceState); // register android type registration in DI container DependencyService.Register<IOpenExternalAppService, OpenExternalAppService>(); LoadApplication(new App()); } } } |
On Android, there is no any special permissions, just android.permission.ACCESS_NETWORK_STATE
in AndroidManifest.xml so the app can navigate to Google play web app if app is not installed.
That’s all to it, lets switch to iOS.
iOS implementation
Again, no much to debate, simple iOS service implementation and DI registration.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
using System.Threading.Tasks; using Jenx.AppOpenDemo.Services; using Foundation; using UIKit; namespace Jenx.AppOpenDemo.iOS.Services { public class OpenExternalAppService : IOpenExternalAppService { public Task<bool> LaunchApp(string uri) { try { var canOpen = UIApplication.SharedApplication.CanOpenUrl(new NSUrl(uri)); return Task.FromResult(canOpen && UIApplication.SharedApplication.OpenUrl(new NSUrl(uri))); } catch { return Task.FromResult(false); } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
using Jenx.AppOpenDemo.iOS.Services; using Jenx.AppOpenDemo.Services; using Foundation; using UIKit; using Xamarin.Forms; namespace Jenx.AppOpenDemo.iOS { [Register("AppDelegate")] public class AppDelegate : Xamarin.Forms.Platform.iOS.FormsApplicationDelegate { public override bool FinishedLaunching(UIApplication app, NSDictionary options) { // register android type registration in DI container DependencyService.Register<IOpenExternalAppService, OpenExternalAppService>(); Forms.Init(); LoadApplication(new App()); return base.FinishedLaunching(app, options); } } } |
iOS has more strict policy regarding opening external apps. Both target and source (calling) apps must have defined app schema in order to be able to start target application.
In my case, my demo app was planning to open Netflix application (se code below), this app defines nflx://
app query schema. Likewise, my iOS calling application needs application query schema definition in Info.plist
. Here is the extract:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> ... removed for brevity..... <!-- this schema key added --> <key>LSApplicationQueriesSchemes</key> <array> <string>nflx</string> </array> </dict> </plist> |
Finally, iOS and Android launcher service implementations are ready and registered in DI Container. I will switch to common code where all my logic is.
Common code
I have simple one-page app…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
using Jenx.AppOpenDemo.Views; using Xamarin.Forms; namespace Jenx.AppOpenDemo { public partial class App { public App() { InitializeComponent(); MainPage = new NavigationPage(new MainPage()); } protected override void OnStart() { } protected override void OnSleep() { } protected override void OnResume() { } } } |
…with one button on my main page MainPage.xaml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<ContentPage x:Class="Jenx.AppOpenDemo.Views.MainPage" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:vm="clr-namespace:Jenx.AppOpenDemo.ViewModels" Title="Open external App"> <ContentPage.BindingContext> <vm:MainViewModel /> </ContentPage.BindingContext> <Button Command="{Binding OpenExternalAppCommand}" Text="Start Netflix" VerticalOptions="Start" /> </ContentPage> |
My ViewModel which handles all the GUI logic is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
using System; using System.Threading.Tasks; using System.Windows.Input; using Jenx.AppOpenDemo.Services; using Xamarin.Essentials; using Xamarin.Forms; namespace Jenx.AppOpenDemo.ViewModels { public class MainViewModel { public MainViewModel() { // Missing out-of-the-box constructor injection in XF // use fx which support this, e.g. Prism, AutoFac, etc... var openExternalAppService = DependencyService.Get<IOpenExternalAppService>(); OpenExternalAppCommand = new Command(async () => { try { if(!await openExternalAppService.LaunchApp(Device.RuntimePlatform == Device.iOS ? "nflx://" : "com.netflix.mediaclient")) { await TryLaunchingNetflixWebAsync(); } } catch { await TryLaunchingNetflixWebAsync(); } }); } public ICommand OpenExternalAppCommand { get; } private Task TryLaunchingNetflixWebAsync() { return Launcher.TryOpenAsync( Device.RuntimePlatform == Device.iOS ? new Uri("https://apps.apple.com/us/app/netflix/id363590051") : new Uri("https://play.google.com/store/apps/details?id=com.netflix.mediaclient")); } } } |
Here you go, app flow is straightforward: If Netflix app is not installed or cannot be started then Google App or App Store web site is open.
My app…
… redirecting to App Store or Google play, if Netflix app is not installed.
If app is installed then Netflix client app is started, as shown below.
All is working just fine!
Summary
In this blog post I presented how to open external app from Xamarin.Forms application.
This application is also good example how to abstract platform specific functionality (Android/iOS) and use it in common code. Commonly, Xamarin Forms libraries nicely handle this – so developer does not need to deal with any platform specific code/API. But, in any serious Xamarin.Forms application targeting these two platforms, you will sooner or later hit into these kind of problems.
If you find this post useful leave a message below. You can freely download demo code from https://github.com/josipx/Jenx.AppOpenDemo.
Happy coding!
3 thoughts on “Xamarin Forms: Open external application”
Compañero, primero que todo felicitarte por tus post, son de mucha ayuda, sobre todo para cuando no se maneja en el mundo de programación móvil, quisiera consultarte sobre algo que me pasó con el post (https://www.jenx.si/2021/02/05/xamarin-forms-open-external-application), lo implementpe y funciona súper Android 10, sin embargo en Android 11 cuando tengo instalada la app que deseo abrir de igual forma me lleva al store y desde ahí debo darle abrir.
Agradezco me pudieras ayudar con eso, muchas gracias!
Hi @marco.
For Android 11 (API 30+) you need to put this section in android manifest file:
<queries>
<package android:name="com.netflix.mediaclient" />
</queries>
This demo code is amazing and thanks so much…