Deep Linking does not work in Xamarin Forms where iOS is 13 or greater

Harrison of The North 196 Reputation points
2021-02-17T13:05:28.273+00:00

I'd very much appreciate a resolution to the following problem, as well as Microsoft both:

  1. Releasing some official documentation on the issue, and
  2. Updating the Xamarin Forms Solution Templates in Visual Studio

iOS 13 was released on the 19th of September 2019 and yet there is still a bug in the new iOS SceneDelegate pattern as relates to Xamarin Forms, in that when using Xamarin.Forms (not Xamarin.iOS), that the developer cannot intercept the URL that was clicked on to bring the app into the foreground.

Furthermore, these 18 months later, the Xamarin.Forms solution that is generated in Visual Studio (File / New Solution, etc) does not include the new SceneDelegate pattern _in the iOS project of that Xamarin.Forms solution.

In that changeover from iOS 12 to 13 in September 2019, every single developer has also been left stranded without documentation of the new Xamarin Forms pattern that allows a user to click a link and the application to be opened and the developer being advised of what URL opened the link.

The following code demonstrates that I have a deep link registered, and where iOS is 13 or greater, although clicking on the link opens the app, the OpenUrlContexts override does not fire, hence I am unable to filter then navigate, based upon the URL.

I've included the following code files:

  1. SceneDelegate
  2. AppDelegate
  3. apple-app-site-association
  4. Entitlements.plist
  5. Info.plist extract
  6. App.xaml.cs

Note: the following has the pre-iOS 13 code stripped out, which in the finmal result should be included to cater for iPhones that have not yet upgraded to iOS 13 and above.

In short, the OpenUrlContexts method does not fire when a link is clicked on, and even in WillConnect I'd have expected connectionOptions.UrlContexts.AnyObject.Url to show the URL however UrlContexts is null.

SceneDelegate file:

[Register("SceneDelegate")]
    public class SceneDelegate : UIWindowSceneDelegate
    {
        [Export("scene:openURLContexts:")]
        public override void OpenUrlContexts(UIScene scene, NSSet<UIOpenUrlContext> urlContexts)
        {
            Debug.WriteLine("*** URL property =====" + urlContexts.AnyObject.Url);
            var urlString = urlContexts.AnyObject.Url;
            if (urlString.Equals("https://REDACTED"))
            {
                // Run code 
            }
        }


        public override UIWindow Window { get; set; }

        public override void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
        {
            var windowScene = scene as UIWindowScene;
            if (windowScene != null)
            {
                Window = new UIWindow(windowScene.CoordinateSpace.Bounds);
                if (Window != null)
                {
                    // This just creates a new App instance without creating a new AppDelegate
                    // In iether case I still see issues with navigation events going to the wrong window,
                    // i.e. I tap Add Item in one window, but the new item page launches in another window
                    // Or use the menu to go to the ABout page, and the About page opens in the other window
                    // This is in keeping with what I have seen in reagrds to embedding forms pages into native 
                    // projects. In this case, you need to use the platforms navigation APIs, not Xamarin.Forms
                    // navigation APIs
                    var fApp = new App();
                    try
                    {
                        // Set the scene's RootViewController from the one X.Forms created
                        Window.RootViewController = fApp.MainPage.CreateViewController();


                        //// This creates the Xamarin.Forms UI with a new app delegate
                        var ad = new AppDelegate();
                        ad.GetUI();
                        //
                        //// Set the scene's RootViewController from the one X.Forms created
                        Window.RootViewController = ad.Window.RootViewController;


                        // Set the WindowScene
                        Window.WindowScene = windowScene;
                    }
                    catch(Exception ex)
                    {

                    }


                }
            }
        }
    }

AppDelegate file:

[Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        public UIApplication application;
        public NSDictionary options;
        //
        // This method is invoked when the application has loaded and is ready to run. In this 
        // method you should instantiate the window, load the UI into it and then make the window
        // visible.
        //
        // You have 17 seconds to return from this method, or iOS will terminate your application.
        //
        [Export("application:didFinishLaunchingWithOptions:")]
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();

            if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
            {
                // only used when creating a new app delegate in the scene delegate in order to be able to call 
                // LoadApplication in GetUI method
                this.application = app;
                this.options = options;

                return true;
            }
            else
            {
                LoadApplication(new App());
                return base.FinishedLaunching(app, options);
            }
        }

        // only used when creating a new app delegate in the scene delegate
        public void GetUI()
        {
            LoadApplication(new App());

            // This call populates the Window property so it needs to be called after LoadApplication for every scene 
            base.FinishedLaunching(application, options);

        }
    }

apple-app-site-association file:

{
   "applinks": {
     "apps": [],
     "details": [
       {
         "appID": "REDACTED",
         "paths": [ "/", "/REDACTED/*"]
       }
     ]
   }
}

Entitlements.plist file

<?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>
    <key>com.apple.developer.associated-domains</key>
    <array>
        <string>applinks:REDACTED</string>
    </array>
</dict>
</plist>

Registration of SceneDelegate in info.plist

<?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>
    <!-- other stuff not shown here -->
    <key>UIApplicationSceneManifest</key>
    <dict>
        <key>UIApplicationSupportsMultipleScenes</key>
        <true/>
        <key>UISceneConfigurations</key>
        <dict>
            <key>UIWindowSceneSessionRoleApplication</key>
            <array>
                <dict>
                    <key>UISceneConfigurationName</key>
                    <string>Default Configuration</string>
                    <key>UISceneDelegateClassName</key>
                    <string>SceneDelegate</string>
                    <key>UISceneStoryboardFile</key>
                    <string>EmptyStoryboard</string>
                </dict>
            </array>
        </dict>
    </dict>
</dict>

Xamarin.Forms App.xaml.cs

(Does not want to paste however the example only has the standard overrides without any user-added code.)

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,371 questions
0 comments No comments
{count} votes

Accepted answer
  1. Cole Xia (Shanghai Wicresoft Co,.Ltd.) 6,751 Reputation points
    2021-02-19T08:25:59.21+00:00

    Hello,

    Welcome to Microsoft Q&A!

    Try Application.OnAppLinkRequestReceived method in App.cs instead of OpenUrlContexts method .

    The method is triggered when the user initiates an app link request .

    Refer to the comment in the link : https://github.com/xamarin/Xamarin.Forms/issues/13786#issuecomment-781416504 .


    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


1 additional answer

Sort by: Most helpful
  1. Harrison of The North 196 Reputation points
    2021-03-01T15:10:05.76+00:00

    My resolution of the problem as a short-term fix is to implement the AppLinks as suggested.

    It is, of course, a short term fix, in the sense that as soon as something necessitates the use of SceneDelegate with Xamarin Forms, then this solution does not work: i.e. I haven't been able to get AppLinks working with SceneDelegate included in iOS in a Xamarin Forms solution.

    However I have tested it in separate POC apps, as follows:

    XF Shell App
    XF Blank App
    XF Shell App with Content Pages NOT in the XF project but in a different .Net Standard project with the XF nuget installed
    (I didn't get to try the XF Master-Detail because the standard template solution wouldn't build and by then I'd had enough).

    Android continues to work, it's a delight!

    I hope that they've solved this SceneDelegate issue by the time .Net Maui is released later this year, as 'click and view' is a major component of my client's mobile app business model.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.