iOS repeatedly asks for camera permission
Opened this issue · 0 comments
Given this repos example code, iOS will consistently request camera permissions once per app start (tested on iOS 16.4 Simulator). Looks like there are some ways to mitigate this by following some of the steps in this SO post. I implemented a modified version of it, since we want to preserve the existing functionality of the IOSWebViewManager.WebViewUIDelegate
used by BlazorWebView
if possible (i.e. alert
, prompt
, confirm
dialogs). Just wanted to share it in case others may find it useful.
- Add within
CreateMauiApp()
BlazorWebViewHandler.BlazorWebViewMapper.Add("custom", (handler, view) =>
{
#if IOS || MACCATALYST
// Add custom UIDelegate's so that we can grant permission requests
// https://stackoverflow.com/a/75649052/10388359
var existingDelegate = handler.PlatformView.UIDelegate;
handler.PlatformView.UIDelegate = new CustomWrapperWebViewDelegate(existingDelegate);
#endif
});
- Add under
Platforms\iOS
folder
internal static class IWKUIDelegateExtensions
{
internal static void GrantDecision(this IWKUIDelegate _, Action<WKPermissionDecision> decisionHandler)
{
if (OperatingSystem.IsIOSVersionAtLeast(15, 0))
decisionHandler(WKPermissionDecision.Grant);
}
}
/// <summary>Used as a wrapper to automatically grant permissions for <see cref="WebView"/>'s using internal implementations of <see cref="WKUIDelegate"/> such as <see cref="BlazorWebView"/> which uses <see cref="IOSWebViewManager.WebViewUIDelegate"/></summary>
public class CustomWrapperWebViewDelegate : WKUIDelegate
{
private readonly IWKUIDelegate? existingDelegate;
public CustomWrapperWebViewDelegate(IWKUIDelegate? existingDelegate)
=> this.existingDelegate = existingDelegate;
public override void RequestDeviceOrientationAndMotionPermission(WKWebView webView, WKSecurityOrigin origin, WKFrameInfo frame, Action<WKPermissionDecision> decisionHandler)
=> this.GrantDecision(decisionHandler);
public override void RequestMediaCapturePermission(WKWebView webView, WKSecurityOrigin origin, WKFrameInfo frame, WKMediaCaptureType type, Action<WKPermissionDecision> decisionHandler)
=> this.GrantDecision(decisionHandler);
public override void CommitPreviewingViewController(WKWebView webView, UIViewController previewingViewController)
=> existingDelegate?.CommitPreviewingViewController(webView, previewingViewController);
public override void ContextMenuDidEnd(WKWebView webView, WKContextMenuElementInfo elementInfo)
=> existingDelegate?.ContextMenuDidEnd(webView, elementInfo);
public override void ContextMenuWillPresent(WKWebView webView, WKContextMenuElementInfo elementInfo)
=> existingDelegate?.ContextMenuWillPresent(webView, elementInfo);
public override WKWebView? CreateWebView(WKWebView webView, WKWebViewConfiguration configuration, WKNavigationAction navigationAction, WKWindowFeatures windowFeatures)
=> existingDelegate?.CreateWebView(webView, configuration, navigationAction, windowFeatures);
public override void DidClose(WKWebView webView)
=> existingDelegate?.DidClose(webView);
public override UIViewController? GetPreviewingViewController(WKWebView webView, WKPreviewElementInfo elementInfo, IWKPreviewActionItem[] previewActions)
=> existingDelegate?.GetPreviewingViewController(webView, elementInfo, previewActions);
public override void RunJavaScriptAlertPanel(WKWebView webView, string message, WKFrameInfo frame, Action completionHandler)
=> existingDelegate?.RunJavaScriptAlertPanel(webView, message, frame, completionHandler);
public override void RunJavaScriptConfirmPanel(WKWebView webView, string message, WKFrameInfo frame, Action<bool> completionHandler)
=> existingDelegate?.RunJavaScriptConfirmPanel(webView, message, frame, completionHandler);
public override void RunJavaScriptTextInputPanel(WKWebView webView, string prompt, string? defaultText, WKFrameInfo frame, Action<string> completionHandler)
=> existingDelegate?.RunJavaScriptTextInputPanel(webView, prompt, defaultText, frame, completionHandler);
public override void SetContextMenuConfiguration(WKWebView webView, WKContextMenuElementInfo elementInfo, Action<UIContextMenuConfiguration> completionHandler)
=> existingDelegate?.SetContextMenuConfiguration(webView, elementInfo, completionHandler);
public override bool ShouldPreviewElement(WKWebView webView, WKPreviewElementInfo elementInfo)
=> (bool)(existingDelegate?.ShouldPreviewElement(webView, elementInfo));
public override void ShowLockDownMode(WKWebView webView, string firstUseMessage, Action<WKDialogResult> completionHandler)
=> existingDelegate?.ShowLockDownMode(webView, firstUseMessage, completionHandler);
public override void WillCommitContextMenu(WKWebView webView, WKContextMenuElementInfo elementInfo, IUIContextMenuInteractionCommitAnimating animator)
=> existingDelegate?.WillCommitContextMenu(webView, elementInfo, animator);
}
Interestingly, for me it doesn't even prompt to allow camera access at all, just allows it w/o prompt, however I haven't tested outside of a simulator or iOS 16.4 yet.
For those of you interested, if you're working with just the default MAUI WebView
, we can take a similar approach:
- Add within
CreateMauiApp()
WebViewHandler.Mapper.AppendToMapping(nameof(WKUIDelegate), (handler, view) =>
{
#if IOS || MACCATALYST
// Add custom UIDelegate's so that we can grant permission requests
// https://stackoverflow.com/a/75649052/10388359
if (existingDelegate is MauiWebViewUIDelegate d)
handler.PlatformView.UIDelegate = new CustomMauiWebViewUIDelegate(handler);
#endif
});
- Add under
Platforms\iOS
folder
/// <summary>Used to automatically grant permissions for <see cref="WebView"/>'s using <see cref="MauiWebViewUIDelegate"/></summary>
public class CustomMauiWebViewUIDelegate : MauiWebViewUIDelegate
{
public CustomMauiWebViewUIDelegate(IWebViewHandler handler) : base(handler) { }
public override void RequestMediaCapturePermission(WKWebView webView, WKSecurityOrigin origin, WKFrameInfo frame, WKMediaCaptureType type, Action<WKPermissionDecision> decisionHandler)
=> this.GrantDecision(decisionHandler);
}
And just one extra tidbit, if working with the MAUI WebView
, you'll need to enable some WKWebViewConfiguration
options (primarily AllowsInlineMediaPlayback
& MediaTypesRequiringUserActionForPlayback
), which will be enabled by default in net8.0
, however you can enable them in net7.0
by following the example in this PR (which is what added these options to be default for next version). These same options can be configured for the BlazorWebView
within the BlazorWebViewInitializing
event, which this example repo already covers:
What a rabbit hole... Hope someone finds this useful!