Allow specifying a navigation delegate (iOS implementation). (#1323)
This is the iOS implementation of the navigation delegate method channel.
The Dart and Android implementations are in #1236
Splitting off the iOS to keep a reasonable change size.
This PR will be merged first.
flutter/flutter#25329
diff --git a/packages/webview_flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/webview_flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/webview_flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?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>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/packages/webview_flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/webview_flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..949b678
--- /dev/null
+++ b/packages/webview_flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?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>BuildSystemType</key>
+ <string>Original</string>
+</dict>
+</plist>
diff --git a/packages/webview_flutter/ios/Classes/FLTWKNavigationDelegate.h b/packages/webview_flutter/ios/Classes/FLTWKNavigationDelegate.h
new file mode 100644
index 0000000..1625c49
--- /dev/null
+++ b/packages/webview_flutter/ios/Classes/FLTWKNavigationDelegate.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Flutter/Flutter.h>
+#import <WebKit/WebKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FLTWKNavigationDelegate : NSObject <WKNavigationDelegate>
+
+- (instancetype)initWithChannel:(FlutterMethodChannel*)channel;
+
+/**
+ * Whether to delegate navigation decisions over the method channel.
+ */
+@property(nonatomic, assign) BOOL hasDartNavigationDelegate;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/ios/Classes/FLTWKNavigationDelegate.m b/packages/webview_flutter/ios/Classes/FLTWKNavigationDelegate.m
new file mode 100644
index 0000000..3638ddc
--- /dev/null
+++ b/packages/webview_flutter/ios/Classes/FLTWKNavigationDelegate.m
@@ -0,0 +1,59 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "FLTWKNavigationDelegate.h"
+
+@implementation FLTWKNavigationDelegate {
+ FlutterMethodChannel* _methodChannel;
+}
+
+- (instancetype)initWithChannel:(FlutterMethodChannel*)channel {
+ self = [super init];
+ if (self) {
+ _methodChannel = channel;
+ }
+ return self;
+}
+
+- (void)webView:(WKWebView*)webView
+ decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction
+ decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
+ if (!self.hasDartNavigationDelegate) {
+ decisionHandler(WKNavigationActionPolicyAllow);
+ return;
+ }
+ NSDictionary* arguments = @{
+ @"url" : navigationAction.request.URL.absoluteString,
+ @"isForMainFrame" : @(navigationAction.targetFrame.isMainFrame)
+ };
+ [_methodChannel invokeMethod:@"navigationRequest"
+ arguments:arguments
+ result:^(id _Nullable result) {
+ if ([result isKindOfClass:[FlutterError class]]) {
+ NSLog(@"navigationRequest has unexpectedly completed with an error, "
+ @"allowing navigation.");
+ decisionHandler(WKNavigationActionPolicyAllow);
+ return;
+ }
+ if (result == FlutterMethodNotImplemented) {
+ NSLog(@"navigationRequest was unexepectedly not implemented: %@, "
+ @"allowing navigation.",
+ result);
+ decisionHandler(WKNavigationActionPolicyAllow);
+ return;
+ }
+ if (![result isKindOfClass:[NSNumber class]]) {
+ NSLog(@"navigationRequest unexpectedly returned a non boolean value: "
+ @"%@, allowing navigation.",
+ result);
+ decisionHandler(WKNavigationActionPolicyAllow);
+ return;
+ }
+ NSNumber* typedResult = result;
+ decisionHandler([typedResult boolValue] ? WKNavigationActionPolicyAllow
+ : WKNavigationActionPolicyCancel);
+ }];
+}
+
+@end
diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/ios/Classes/FlutterWebView.m
index 87d4d25..ea3b192 100644
--- a/packages/webview_flutter/ios/Classes/FlutterWebView.m
+++ b/packages/webview_flutter/ios/Classes/FlutterWebView.m
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#import "FlutterWebView.h"
+#import "FLTWKNavigationDelegate.h"
#import "JavaScriptChannelHandler.h"
@implementation FLTWebViewFactory {
@@ -40,6 +41,7 @@
NSString* _currentUrl;
// The set of registered JavaScript channel names.
NSMutableSet* _javaScriptChannelNames;
+ FLTWKNavigationDelegate* _navigationDelegate;
}
- (instancetype)initWithFrame:(CGRect)frame
@@ -64,6 +66,8 @@
configuration.userContentController = userContentController;
_webView = [[WKWebView alloc] initWithFrame:frame configuration:configuration];
+ _navigationDelegate = [[FLTWKNavigationDelegate alloc] initWithChannel:_channel];
+ _webView.navigationDelegate = _navigationDelegate;
__weak __typeof__(self) weakSelf = self;
[_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[weakSelf onMethodCall:call result:result];
@@ -229,6 +233,9 @@
if ([key isEqualToString:@"jsMode"]) {
NSNumber* mode = settings[key];
[self updateJsMode:mode];
+ } else if ([key isEqualToString:@"hasNavigationDelegate"]) {
+ NSNumber* hasDartNavigationDelegate = settings[key];
+ _navigationDelegate.hasDartNavigationDelegate = [hasDartNavigationDelegate boolValue];
} else {
NSLog(@"webview_flutter: unknown setting key: %@", key);
}