diff --git a/src/App/MauiProgram.cs b/src/App/MauiProgram.cs index 5645bcd2d..1f9a876b3 100644 --- a/src/App/MauiProgram.cs +++ b/src/App/MauiProgram.cs @@ -15,6 +15,7 @@ }, handlers => { + handlers.AddHandler(typeof(Bit.App.Controls.HybridWebView), typeof(Bit.App.Handlers.HybridWebViewHandler)); #if ANDROID Bit.App.Handlers.EntryHandlerMappings.Setup(); Bit.App.Handlers.EditorHandlerMappings.Setup(); @@ -29,7 +30,6 @@ Bit.App.Handlers.ButtonHandlerMappings.Setup(); handlers.AddHandler(typeof(Bit.App.Pages.TabsPage), typeof(Bit.App.Handlers.CustomTabbedPageHandler)); - handlers.AddHandler(typeof(Bit.App.Controls.HybridWebView), typeof(Bit.App.Handlers.HybridWebViewHandler)); #else iOS.Core.Utilities.iOSCoreHelpers.ConfigureMAUIHandlers(handlers); #endif diff --git a/src/App/Platforms/Android/Handlers/HybridWebViewHandler.cs b/src/App/Platforms/Android/Handlers/HybridWebViewHandler.cs index 390bb211c..7c7bcfbd5 100644 --- a/src/App/Platforms/Android/Handlers/HybridWebViewHandler.cs +++ b/src/App/Platforms/Android/Handlers/HybridWebViewHandler.cs @@ -25,7 +25,7 @@ namespace Bit.App.Handlers public static void MapUri(HybridWebViewHandler handler, HybridWebView view) { - if (view != null && view.Uri != null) + if (view != null && view?.Uri != null) { handler?.PlatformView?.LoadUrl(view.Uri); } diff --git a/src/App/Platforms/iOS/Handlers/HybridWebViewHandler.cs b/src/App/Platforms/iOS/Handlers/HybridWebViewHandler.cs new file mode 100644 index 000000000..694128f42 --- /dev/null +++ b/src/App/Platforms/iOS/Handlers/HybridWebViewHandler.cs @@ -0,0 +1,84 @@ +using System.Diagnostics.CodeAnalysis; +using Bit.App.Controls; +using CoreGraphics; +using Foundation; +using Microsoft.Maui.Handlers; +using WebKit; + +namespace Bit.App.Handlers +{ + public partial class HybridWebViewHandler : ViewHandler + { + private const string JSFunction = + "function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}"; + + private WKUserContentController _userController; + + public HybridWebViewHandler([NotNull] IPropertyMapper mapper, CommandMapper commandMapper = null) : base(mapper, commandMapper) + { + } + + protected override WKWebView CreatePlatformView() + { + _userController = new WKUserContentController(); + var script = new WKUserScript(new NSString(JSFunction), WKUserScriptInjectionTime.AtDocumentEnd, false); + _userController.AddUserScript(script); + _userController.AddScriptMessageHandler(new WebViewScriptMessageHandler(InvokeAction), "invokeAction"); + + var config = new WKWebViewConfiguration { UserContentController = _userController }; + var webView = new WKWebView(CGRect.Empty, config); + return webView; + } + + public static void MapUri(HybridWebViewHandler handler, HybridWebView view) + { + if (handler != null && view?.Uri != null) + { + handler?.PlatformView?.LoadRequest(new NSUrlRequest(new NSUrl(view.Uri))); + } + } + + protected override void ConnectHandler(WKWebView platformView) + { + if (VirtualView?.Uri != null) + { + platformView?.LoadRequest(new NSUrlRequest(new NSUrl(VirtualView?.Uri))); + } + base.ConnectHandler(platformView); + } + + protected override void DisconnectHandler(WKWebView platformView) + { + _userController.RemoveAllUserScripts(); + _userController.RemoveScriptMessageHandler("invokeAction"); + platformView?.Dispose(); + VirtualView?.Cleanup(); + _userController = null; + + base.DisconnectHandler(platformView); + } + + private void InvokeAction(WKScriptMessage message) + { + if(message?.Body != null) + { + VirtualView?.InvokeAction(message.Body.ToString()); + } + } + } + + public class WebViewScriptMessageHandler : NSObject, IWKScriptMessageHandler + { + private Action _messageReceivedAction; + + public WebViewScriptMessageHandler(Action messageReceivedAction) + { + _messageReceivedAction = messageReceivedAction ?? throw new ArgumentNullException(nameof(messageReceivedAction)); + } + + public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message) + { + _messageReceivedAction(message); + } + } +}