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

Need to modify the HybridWebView configuration (BEFORE initializing) on iOS/MacCatalyst to fix known base WebView issue ... Any solution? #67

Open
jonmdev opened this issue Jun 12, 2024 · 3 comments

Comments

@jonmdev
Copy link

jonmdev commented Jun 12, 2024

There is a problem with the WebView in iOS/MacCatalyst where one must define parameters only on initialization for the ability to play media inline (ie. not go full screen every time you play), autoplay, and configure other media playback parameters.

This is described here:

The two solutions at those locations both don't work for HybridWebView, at least not directly without Reflection for the following reasons:

1) Blazor Fix

The BlazorWebView fix is shown here: dotnet/maui#16013 (comment)

Where the user sets:
BlazorWebViewInitializing="HandleWebViewInitializing"

    private void HandleWebViewInitializing(object sender, BlazorWebViewInitializingEventArgs args)
    {
        // WebView needs the configuration updated during initialization in order for "playsinline" attributes 
        // to work in the HTML5 video player for iOS devices.
#if IOS || MACCATALYST
        args.Configuration.AllowsInlineMediaPlayback = true;
        args.Configuration.MediaPlaybackRequiresUserAction = false;
        args.Configuration.RequiresUserActionForMediaPlayback = false;
#endif
    }

However, we cannot use this directly as the only event we have in HybridWebView is only run AFTER initialization (which is too late - I tried - config must be set before creation of the WebView and can't be affected after):

            void initFunction(object sender, HybridWebViewInitializedEventArgs args) {
#if IOS || MACCATALYST

                //doesn't work as already initialized on this event
                args.WebView.Configuration.AllowsInlineMediaPlayback = true;
                args.WebView.Configuration.MediaPlaybackRequiresUserAction = false;
                args.WebView.Configuration.RequiresUserActionForMediaPlayback = false;
#endif
            }

            HybridWebView.HybridWebView webView = new();
            webView.HybridWebViewInitialized += initFunction; //does not work as only runs AFTER INITIALIZATION        

2) WebView Solution

The WebView solution is given here: dotnet/maui#4807 (comment) and here: dotnet/maui#11075 by @Redth:

There he suggests overriding the Microsoft.Maui.Handlers.WebViewHandler.PlatformViewFactory = (handler) => function. However, reviewing this in HybridWebView shows it is not so simple. The HybridWebView function is here:

However a similar approach doesn't work directly for numerous reasons:

  • Can't access: WebViewScriptMessageHandler, MessageReceived, or SchemeHandler this way.

Commenting out everything dependent on these breaks the HybridWebView.

3) Reflection Solution (WORKING)

I was just able to get a temporary fix using Reflection and redefining MessageReceived by modifying @Redth's solution approach:

#if IOS || MACCATALYST

            HybridWebView.HybridWebViewHandler.PlatformViewFactory = (handler) => {
                
                var config = new WKWebViewConfiguration();
                //====================================================
                //CUSTOM CONFIG
                config.AllowsAirPlayForMediaPlayback = true;
                config.AllowsInlineMediaPlayback = true;
                config.AllowsPictureInPictureMediaPlayback = true;
                config.MediaTypesRequiringUserActionForPlayback = WebKit.WKAudiovisualMediaTypes.None;

                //simple enough can just redundancy this one:
                Action<Uri, string> MessageReceivedAction = delegate (Uri uri, string message) { // to replace https://github.com/Eilon/MauiHybridWebView/blob/3ca801076a1e3fbe3b8922b2429524df20def6a4/HybridWebView/Platforms/iOS/HybridWebViewHandler.iOS.cs#L40
                    ((HybridWebView.HybridWebView)handler.VirtualView).OnMessageReceived(message);
                };

                //https://stackoverflow.com/a/39076814/10305478
                object CreatePrivateClassInstance(string typeName, object[] parameters) {
                    Type type = AppDomain.CurrentDomain.GetAssemblies().
                             SelectMany(assembly => assembly.GetTypes()).FirstOrDefault(t => t.Name == typeName);
                    return type.GetConstructors()[0].Invoke(parameters);
                }

                IWKScriptMessageHandler webViewScriptMessageHandler = (IWKScriptMessageHandler)CreatePrivateClassInstance("WebViewScriptMessageHandler", [MessageReceivedAction]);
                config.UserContentController.AddScriptMessageHandler(webViewScriptMessageHandler, "webwindowinterop");

                IWKUrlSchemeHandler? wKUrlSchemeHandler = (IWKUrlSchemeHandler?)CreatePrivateClassInstance("SchemeHandler", [handler as HybridWebViewHandler]);
                config.SetUrlSchemeHandler(wKUrlSchemeHandler, urlScheme: "app");
                //============================================


                //default programming built in
                //config.UserContentController.AddScriptMessageHandler(new WebViewScriptMessageHandler(MessageReceived), "webwindowinterop");
                //config.SetUrlSchemeHandler(new SchemeHandler(this), urlScheme: "app");

                // Legacy Developer Extras setting.
                var enableWebDevTools = ((HybridWebView.HybridWebView)(handler as HybridWebViewHandler).VirtualView).EnableWebDevTools;
                config.Preferences.SetValueForKey(NSObject.FromObject(enableWebDevTools), new NSString("developerExtrasEnabled"));
                var platformView = new MauiWKWebView(RectangleF.Empty, handler as HybridWebViewHandler, config);

                if (OperatingSystem.IsMacCatalystVersionAtLeast(major: 13, minor: 3) ||
                    OperatingSystem.IsIOSVersionAtLeast(major: 16, minor: 4)) {
                    // Enable Developer Extras for Catalyst/iOS builds for 16.4+
                    platformView.SetValueForKey(NSObject.FromObject(enableWebDevTools), new NSString("inspectable"));
                }

                return platformView;
            };
#endif

I tested and this does seem to work. I have an inline video autoplaying now.

Long Term Solution?

The simplest solution I can think of would be to just provide us with an Action<WKWebViewConfiguration> customConfiguration in iOS/MacCatalyst we can set into the HybridWebView or the Handler somehow before this function is run. It can get utilized inside that existing function like this:

        protected override WKWebView CreatePlatformView() {
                
                var config = new WKWebViewConfiguration();
                //=================================================
                customConfiguration?.Invoke(config); //modify the config file on the way through here
                //=================================================

                //then continue default programming built in
                config.UserContentController.AddScriptMessageHandler(new HybridWebView.WebViewScriptMessageHandler(MessageReceived), "webwindowinterop");
                config.SetUrlSchemeHandler(new SchemeHandler(this), urlScheme: "app");

Or otherwise I am not sure how you guys would want to fix this.

What do you think @Eilon ? Or @Redth ?

@jonmdev jonmdev changed the title Need to modify the HybridWebView configuration (BEFORE initializing) on iOS/MacCatalyst to fix known base WebView issue ... Any workaround? Need to modify the HybridWebView configuration (BEFORE initializing) on iOS/MacCatalyst to fix known base WebView issue ... Any workaround or solution? Jun 12, 2024
@jonmdev jonmdev changed the title Need to modify the HybridWebView configuration (BEFORE initializing) on iOS/MacCatalyst to fix known base WebView issue ... Any workaround or solution? Need to modify the HybridWebView configuration (BEFORE initializing) on iOS/MacCatalyst to fix known base WebView issue ... Any solution? Jun 12, 2024
@HelloooJoe
Copy link

@Eilon Can you consider inviting other developers help maintain this repo that have contributed already? It seems like you're not giving this repo enough attention.

@Eilon
Copy link
Owner

Eilon commented Jul 20, 2024

@Eilon Can you consider inviting other developers help maintain this repo that have contributed already? It seems like you're not giving this repo enough attention.

I've been working on the official version here: #70

So for that reason my time hasn't been as focused here. Having said that, I'm certainly willing to work with people on features, fixes, and PRs within this repo for features that are good candidates to be in the official version.

@jonmdev
Copy link
Author

jonmdev commented Jul 20, 2024

@Eilon Can you consider inviting other developers help maintain this repo that have contributed already? It seems like you're not giving this repo enough attention.

I've been working on the official version here: #70

So for that reason my time hasn't been as focused here. Having said that, I'm certainly willing to work with people on features, fixes, and PRs within this repo for features that are good candidates to be in the official version.

Congratulations on being officially included. Please keep in mind the issue reported in this thread, and if nothing else, please don't break this method without providing us an alternative.

Thanks again.

#if IOS || MACCATALYST

            HybridWebView.HybridWebViewHandler.PlatformViewFactory = (handler) => {
                
                var config = new WKWebViewConfiguration();
                //====================================================
                //CUSTOM CONFIG
                config.AllowsAirPlayForMediaPlayback = true;
                config.AllowsInlineMediaPlayback = true;
                config.AllowsPictureInPictureMediaPlayback = true;
                config.MediaTypesRequiringUserActionForPlayback = WebKit.WKAudiovisualMediaTypes.None;

                //simple enough can just redundancy this one:
                Action<Uri, string> MessageReceivedAction = delegate (Uri uri, string message) { // to replace https://github.com/Eilon/MauiHybridWebView/blob/3ca801076a1e3fbe3b8922b2429524df20def6a4/HybridWebView/Platforms/iOS/HybridWebViewHandler.iOS.cs#L40
                    ((HybridWebView.HybridWebView)handler.VirtualView).OnMessageReceived(message);
                };

                //https://stackoverflow.com/a/39076814/10305478
                object CreatePrivateClassInstance(string typeName, object[] parameters) {
                    Type type = AppDomain.CurrentDomain.GetAssemblies().
                             SelectMany(assembly => assembly.GetTypes()).FirstOrDefault(t => t.Name == typeName);
                    return type.GetConstructors()[0].Invoke(parameters);
                }

                IWKScriptMessageHandler webViewScriptMessageHandler = (IWKScriptMessageHandler)CreatePrivateClassInstance("WebViewScriptMessageHandler", [MessageReceivedAction]);
                config.UserContentController.AddScriptMessageHandler(webViewScriptMessageHandler, "webwindowinterop");

                IWKUrlSchemeHandler? wKUrlSchemeHandler = (IWKUrlSchemeHandler?)CreatePrivateClassInstance("SchemeHandler", [handler as HybridWebViewHandler]);
                config.SetUrlSchemeHandler(wKUrlSchemeHandler, urlScheme: "app");
                //============================================


                //default programming built in
                //config.UserContentController.AddScriptMessageHandler(new WebViewScriptMessageHandler(MessageReceived), "webwindowinterop");
                //config.SetUrlSchemeHandler(new SchemeHandler(this), urlScheme: "app");

                // Legacy Developer Extras setting.
                var enableWebDevTools = ((HybridWebView.HybridWebView)(handler as HybridWebViewHandler).VirtualView).EnableWebDevTools;
                config.Preferences.SetValueForKey(NSObject.FromObject(enableWebDevTools), new NSString("developerExtrasEnabled"));
                var platformView = new MauiWKWebView(RectangleF.Empty, handler as HybridWebViewHandler, config);

                if (OperatingSystem.IsMacCatalystVersionAtLeast(major: 13, minor: 3) ||
                    OperatingSystem.IsIOSVersionAtLeast(major: 16, minor: 4)) {
                    // Enable Developer Extras for Catalyst/iOS builds for 16.4+
                    platformView.SetValueForKey(NSObject.FromObject(enableWebDevTools), new NSString("inspectable"));
                }

                return platformView;
            };
#endif

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants