Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET Maui AppLink OnAppLinkRequestReceived doesn't fire on iOS #14671

Open
Yohancef opened this issue Apr 19, 2023 · 14 comments
Open

.NET Maui AppLink OnAppLinkRequestReceived doesn't fire on iOS #14671

Yohancef opened this issue Apr 19, 2023 · 14 comments
Labels
area-compatibility Issues related to features in the Compatibility Nuget platform/iOS 🍎 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Milestone

Comments

@Yohancef
Copy link

Description

I am trying to create some functionality when a users links into the app. I have created the apple-app-site-association file on the server. It is correctly recognized as an app link and opens my app. The issue is the OnAppLinkRequestReceived event doesn't seem to get triggered. I am using Visual Studio 17.5.4 and XCode 14.2. I am testing in on iOS Simulator 16.2 and on a physical device with iOS 16.1.2.

Following various suggestions I have added the following code:

MauiProgram.cs:

public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiCompatibility()
            .UseMauiApp<App>()
            .UseMauiCommunityToolkit()
            .ConfigureLifecycleEvents(events =>
            {
#if IOS
                if (Version.Parse(UIDevice.CurrentDevice.SystemVersion) > Version.Parse("12.0"))
                {
                    events.AddiOS(ios => ios
                        .SceneOpenUrl((parameter1, parameter2) => LogEvent(parameter1, parameter2)));

                    static bool LogEvent(UIKit.UIScene scene, Foundation.NSSet<UIKit.UIOpenUrlContext> context)
                    {
                        var url = context.ToArray().First().Url;
                        Microsoft.Maui.Controls.Application.Current.SendOnAppLinkRequestReceived(url);
                        return true;
                    }
                }
                else
                {
                    events.AddiOS(ios => ios
                        .OpenUrl((app, url, opion) => LogEvent2(app, url, opion)));

                    static bool LogEvent2(UIKit.UIApplication application, Foundation.NSUrl url, Foundation.NSDictionary options)
                    {
                        Microsoft.Maui.Controls.Application.Current.SendOnAppLinkRequestReceived(url);
                        return true;
                    }
                }                   
#endif
            });
        builder.Services.AddTransient<MainPage>();
        builder.Services.AddSingleton(typeof(IFingerprint), CrossFingerprint.Current);
#if DEBUG
        builder.Logging.AddDebug();
#endif
        return builder.Build();
    }

Platforms -> iOS -> AppDelegate.cs:

[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
    protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();

    [Export("application:openURL:options:")]
    public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
    {
        Microsoft.Maui.Controls.Application.Current.SendOnAppLinkRequestReceived(url);
        return true;
    }
}

App.xaml.cs:

public App()
{
        InitializeComponent();

        MainPage = new AppShell();
#if IOS
        (Application.Current as IApplicationController)?.SetAppIndexingProvider(new Microsoft.Maui.Controls.Compatibility.Platform.iOS.IOSAppIndexingProvider());
#endif
        try
        {
           var entry = new AppLinkEntry
            {
                Title = "Find Shop",
                Description = "Find Shop Deep Link",
                AppLinkUri = new Uri(GlobalVariables.FindShopDeepLink, UriKind.RelativeOrAbsolute),
                IsLinkActive = true
            };
            Application.Current.AppLinks.RegisterLink(entry);
        }
        catch (Exception ex)
        {
            Crashes.TrackError(ex);
            Crashes.TrackError(new Exception("Deeplink failed."));
        }         
 }

 protected async override void OnAppLinkRequestReceived(Uri uri)
 {            
        await Current.MainPage.DisplayAlert("AppLink", "You are linking!", "OK");
 }

Steps to Reproduce

  1. Create an AppLink for your app.
  2. Follow this link (https://popupshop.azurewebsites.net/findshop) into the sample app.
  3. Put a break point in OnAppLinkRequestReceived event.

Link to public reproduction project repository

https://github.com/Yohancef/AppLinkBug

Version with bug

7.0 (current)

Last version that worked well

Unknown/Other

Affected platforms

iOS

Affected platform versions

iOS 16.1.2, iOS Simulator 16.2

Did you find any workaround?

No

Relevant log output

No response

@Yohancef Yohancef added the t/bug Something isn't working label Apr 19, 2023
@Yohancef
Copy link
Author

For the Steps To Reproduce it should be:

  1. Run the linked App.
  2. Put a break point in OnAppLinkRequestReceived event.
  3. Follow this link (https://popupshop.azurewebsites.net/findshop) into the sample app.

@justintemplar
Copy link

It does look like you need to use Maui Compatibility. Also Android also does not work.
The UI tests use the compatibility mode from what I can see, so they will pass until we finally make Maui Compatibility fully obsolete.

Fix for me:

iOS:
The new ContinueUserActivity() never calls "CheckForAppLink(userActivity)" that then calls "SendOnAppLinkRequestReceived()" on the main Application.
Add "Platform/iOS/AppDelegate.cs"

public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
    // Later when I do URL linking
    CheckForAppLink(userActivity);
    return true;
}
void CheckForAppLink(NSUserActivity userActivity)
{
    var strLink = string.Empty;

    switch (userActivity.ActivityType)
    {
        case "NSUserActivityTypeBrowsingWeb":
            strLink = userActivity.WebPageUrl.AbsoluteString;
            break;
        case "com.apple.corespotlightitem":
            if (userActivity.UserInfo.ContainsKey(CSSearchableItem.ActivityIdentifier))
                strLink = userActivity.UserInfo.ObjectForKey(CSSearchableItem.ActivityIdentifier).ToString();
            break;
        default:
            if (userActivity.UserInfo.ContainsKey(new NSString("link")))
                strLink = userActivity.UserInfo[new NSString("link")].ToString();
            break;
    }

    if (!string.IsNullOrEmpty(strLink))
        App.Current.SendOnAppLinkRequestReceived(new Uri(strLink));
}

Android:
Add to "Platform/Android/MainActivity.cs"

protected override void OnNewIntent(Intent intent)
{
    base.OnNewIntent(intent);

    string action = intent.Action;
    string strLink = intent.DataString;
    if (Intent.ActionView != action || string.IsNullOrWhiteSpace(strLink))
        return;

    var link = new Uri(strLink);
    App.Current.SendOnAppLinkRequestReceived(link);
}

@Yohancef
Copy link
Author

Seems to be working now thanks!

@justintemplar
Copy link

@Yohancef let me know if you have an error like this with android #11684

@Yohancef
Copy link
Author

Yohancef commented Jun 12, 2023

The error I have with Android is in the App.xaml.cs App() constructor. When I attempt to simulate the same iOS code:

#if IOS
        (Application.Current as IApplicationController)?.SetAppIndexingProvider(new 
Microsoft.Maui.Controls.Compatibility.Platform.iOS.IOSAppIndexingProvider());
#endif
#if ANDROID
        (Application.Current as IApplicationController)?.SetAppIndexingProvider(new 
Microsoft.Maui.Controls.Compatibility.Platform.Android.AndroidAppIndexProvider(Android.App.Application.Context));
#endif
....
Application.Current.AppLinks.RegisterLink(entry);

I get 'No AppLinks implementation was found, if in Android make sure you installed the Microsoft.Maui.Controls.AppLinks'

@gerneio
Copy link

gerneio commented Jun 15, 2023

@Yohancef Same error on Android when calling Application.Current.AppLinks.RegisterLink().

Looking at the Application.AppLinks source code, looks like it's wanting the AndroidAppIndexProvider.Applinks property to already be set when we assign it via SetAppIndexingProvider. Looking at the class definition of AndroidAppIndexProvider, it looks like it does some dynamic instantiation of the AndroidAppLinks class IF it finds that the type is loaded, which it is not by default. Seems like it is looking for it in the assembly Microsoft.Maui.Controls.Compatibility.Platform.Android.AppLinks, which I can't find anywhere. Can't find a nuget reference or anything, so no clue how we're supposed to pull that in atm. There is this Microsoft.Maui.Controls.Compatibility package, but that only appears to be for net8.0 preview and I'm on net7.0 anyways.

Ultimately, looks like we'd have to copy the two source files from the MAUI Android.AppLinks repo src folder to use this functionality in net7.0.

I suspect this will all work and be included out of the box in net8.0 (🤞) so I will be holding off on this feature for now. However, if all u need is generic Deep/Universal link handling, then you can probably skip these IAppLinks altogether and just propgate your own listening service (reference issue #3788).

Now with that said, I'm personally a little bit confused on the intent of IAppLinks.RegisterLink since my Deep/Universal links are working w/o it (see above). I'm assuming these might be those additional context options you can add to an app on the home screen (i.e. hold the app icon and see a list of actions that can be performed)? Or is it perhaps some sort of way to index spotlight search results for your specific app? Can someone confirm if either of these assumptions are correct, or point out exactly what does registering these links through this method add in functionality?

Underneath the Android code specifically I did see that it's using FirebaseAppIndex, so I came across this article that makes me think this particular functionality is no longer useful:

Firebase App Indexing is no longer the recommended way of indexing content for display as suggested results in Google Search App.

Caution: The Google Search App for Android no longer uses local content indexed via Firebase App Indexing to provide results to users.

For iOS, I'm assuming we're taping into this functionality:

The Core Spotlight framework provides ways to index the content within your app and manage the private on-device index. Core Spotlight works best for indexing user-specific content, such as friends, items marked as favorite, purchased items, and so on. When you use Core Spotlight APIs to make items searchable, you make it easy for users to find their content in Spotlight search results.

@greg84
Copy link

greg84 commented Jun 22, 2023

I got this working on iOS using the workaround from @justintemplar

But it's still not working on Android - the app opens when I visit the link, but OnNewIntent never gets called. Any more ideas?

I've added the IntentFilter attribute to MainActivity - copied it form the Xamarin version, which works fine.

@justintemplar
Copy link

@greg84 I have had the Android part working but I have had to remove it because of deeper issues on launch. I am using the tab bar and android launch from links crashes each time. apparently something to do with the use of OpenWith() in the tab bar control itself.

@prabhavmehra
Copy link

I have the deep linking for android partially. I am unable to open the app but the OnAppLinkRequestReceived is not being triggered. Any ideas/ anyone got it working?

@prabhav-mehra
Copy link

@justintemplar @greg84 Did you use the Entitlements.plist to get app linking working for iOS? I see an example in Xamarin.iOS and they did it.

@jspfister-sep
Copy link

jspfister-sep commented Aug 24, 2023

I believe I have a workaround for Android:

[IntentFilter(new[] {Intent.ActionView},
    DataScheme = "https", // Or "http" if applicable
    DataHost = "your.base.url.here",
    AutoVerify = true,
    Categories = new[]
    {
        Intent.ActionView, Intent.CategoryDefault, Intent.CategoryBrowsable
    })]
public class MainActivity : MauiAppCompatActivity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        // Usual stuff here

        // Need this handling here because OnNewIntent seems to not get called
        // when the app isn't already running in the background
        CheckForLink(Intent);
        
        base.OnCreate(savedInstanceState)
    }

    protected override void OnNewIntent(Intent intent)
    {
        // Need this handling here because OnCreate doesn't get called when the
        // app is already running in the background, but OnNewIntent does get called
        CheckForLink(intent);

        base.OnNewIntent(intent);
    }

    private void CheckForLink(Intent intent)
    {
        if (intent.DataString?.StartsWith("your.base.url.here") ?? false)
        {
            Microsoft.Maui.Controls.Application.Current?.SendOnAppLinkRequestReceived(new Uri(intent.DataString));
        }
    }
}

@rjhind
Copy link

rjhind commented Nov 16, 2023

It does look like you need to use Maui Compatibility. Also Android also does not work. The UI tests use the compatibility mode from what I can see, so they will pass until we finally make Maui Compatibility fully obsolete.

Fix for me:

iOS: The new ContinueUserActivity() never calls "CheckForAppLink(userActivity)" that then calls "SendOnAppLinkRequestReceived()" on the main Application. Add "Platform/iOS/AppDelegate.cs"

Your fix works for me with Maui 8.0.3 and iOS 17 also. Thank you very much.

@justintemplar
Copy link

There is also new documentation and examples out for .NET 8.

https://github.com/Redth/MAUI.AppLinks.Sample

This might help many people.

@Zhanglirong-Winnie Zhanglirong-Winnie added s/verified Verified / Reproducible Issue ready for Engineering Triage s/triaged Issue has been reviewed labels Jan 17, 2024
@Zhanglirong-Winnie
Copy link

Verified this issue with Visual Studio Enterprise 17.9.0 Preview 2.1. Can repro on iOS platforms.

@PureWeen PureWeen added this to the Backlog milestone May 31, 2024
@PureWeen PureWeen added the area-compatibility Issues related to features in the Compatibility Nuget label May 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-compatibility Issues related to features in the Compatibility Nuget platform/iOS 🍎 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working
Projects
None yet
Development

No branches or pull requests