diff --git a/Xamarin.Forms.ControlGallery.iOS/Properties/AssemblyInfo.cs b/Xamarin.Forms.ControlGallery.iOS/Properties/AssemblyInfo.cs
index 78e7d1d81..7323d6df2 100644
--- a/Xamarin.Forms.ControlGallery.iOS/Properties/AssemblyInfo.cs
+++ b/Xamarin.Forms.ControlGallery.iOS/Properties/AssemblyInfo.cs
@@ -40,4 +40,5 @@ using Xamarin.Forms.Controls;
[assembly: Xamarin.Forms.ResolutionGroupName (Xamarin.Forms.Controls.Issues.Effects.ResolutionGroupName)]
// Deliberately broken image source and handler so we can test handling of image loading errors
-[assembly: ExportImageSourceHandler(typeof(FailImageSource), typeof(BrokenImageSourceHandler))]
\ No newline at end of file
+[assembly: ExportImageSourceHandler(typeof(FailImageSource), typeof(BrokenImageSourceHandler))]
+[assembly: ExportRenderer(typeof(WkWebView), typeof(Xamarin.Forms.Platform.iOS.WkWebViewRenderer))]
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue1666.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue1666.cs
new file mode 100644
index 000000000..2220d74a9
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue1666.cs
@@ -0,0 +1,149 @@
+using System;
+using Xamarin.Forms;
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+
+#if UITEST
+using Xamarin.Forms.Core.UITests;
+using Xamarin.UITest;
+using NUnit.Framework;
+#endif
+
+public class WkWebView : WebView { }
+
+namespace Xamarin.Forms.Controls.Issues
+{
+
+#if UITEST
+ [Category(UITestCategories.ManualReview)]
+#endif
+ [Preserve(AllMembers = true)]
+ [Issue(IssueTracker.Github, 1666, "Use WKWebView on iOS", PlatformAffected.iOS)]
+ public class Issue1666 : TestContentPage // or TestMasterDetailPage, etc ...
+ {
+ protected override void Init()
+ {
+ var buttonBack = new Button() { Text = "<", BackgroundColor = Color.LightBlue, AutomationId = "buttonBack" };
+ var buttonNext = new Button() { Text = ">", BackgroundColor = Color.LightBlue, AutomationId = "buttonNext" };
+ var buttonClear = new Button() { Text = "--", BackgroundColor = Color.LightBlue, AutomationId = "buttonClear" };
+ var buttonState = new Button() { Text = "?", BackgroundColor = Color.LightBlue, AutomationId = "buttonState" };
+ var buttonStop = new Button() { Text = "X", BackgroundColor = Color.LightBlue, AutomationId = "buttonStop" };
+
+ var buttonA = new Button() { Text = "GO", BackgroundColor = Color.LightBlue, AutomationId = "buttonA" };
+ var buttonB = new Button() { Text = "HTML", BackgroundColor = Color.LightBlue, AutomationId = "buttonB" };
+ var buttonC = new Button() { Text = "EVAL", BackgroundColor = Color.LightBlue, AutomationId = "buttonC" };
+ var buttonD = new Button() { Text = "AEVAL", BackgroundColor = Color.LightBlue, AutomationId = "buttonD" };
+
+ var url = "https://www.microsoft.com/";
+ var html = $"
Link";
+
+ var webView = new WkWebView()
+ {
+ HeightRequest = 40,
+ Source = new HtmlWebViewSource { Html = html }
+ };
+
+ var vcr = new Grid();
+ vcr.Children.AddHorizontal(new[] { buttonBack, buttonNext, buttonClear, buttonState, buttonStop });
+
+ var evals = new Grid();
+ evals.Children.AddHorizontal(new[] { buttonA, buttonB, buttonC, buttonD });
+
+ var entry = new Entry() { AutomationId = "entry" };
+ entry.BackgroundColor = Color.Wheat;
+
+ var buttons = new Grid();
+ buttons.Children.AddVertical(vcr);
+ buttons.Children.AddVertical(evals);
+ buttons.Children.AddVertical(entry);
+
+ var console = new Label()
+ {
+ AutomationId = "console",
+ Text = "Loaded\n"
+ };
+ Action log = s => { console.Text = s + "\n" + console.Text; };
+
+ var grid = new Grid();
+ grid.Children.AddVertical(webView);
+ grid.Children.AddVertical(buttons);
+ grid.Children.AddVertical(new ScrollView() { Content = console });
+
+ buttonA.Clicked += (s, e) =>
+ {
+ webView.Source = new UrlWebViewSource() { Url = url };
+ };
+
+ buttonB.Clicked += (s, e) => {
+ webView.Source = new HtmlWebViewSource()
+ {
+ Html = html
+ };
+ };
+
+ var js = "1 + 2";
+ buttonC.Clicked += (s, e) => {
+ log($"Eval: {js}");
+ webView.Eval(js);
+ };
+
+ webView.EvalRequested += (s, e) =>
+ {
+ log($"EvalRequested: {e.Script}");
+ };
+
+ buttonD.Clicked += (s, e) => {
+ log($"AEval: {js}");
+ var promise = webView.EvaluateJavaScriptAsync(js);
+ promise.ContinueWith(a => Device.BeginInvokeOnMainThread(() => log($"Evaled: {a.Result}")));
+ };
+
+ bool cancel = false;
+ buttonNext.Clicked += (s, e) => { webView.GoForward(); log($"GoForward: {webView.CanGoBack}/{webView.CanGoForward}"); };
+ buttonBack.Clicked += (s, e) => { webView.GoBack(); log($"GoBack: {webView.CanGoBack}/{webView.CanGoForward}"); };
+ buttonClear.Clicked += (s, e) => { console.Text = ""; };
+ buttonStop.Clicked += (s, e) => { cancel = true; log("Cancelling navigation"); };
+ buttonState.Clicked += (s, e) => {
+ log($"F/B: {webView.CanGoBack}/{webView.CanGoForward}");
+ log($"Source: {webView.Source.ToString()}");
+ };
+
+ bool navigating = false;
+ webView.Navigating += (s, e) =>
+ {
+ entry.Text = e.Url;
+ entry.BackgroundColor = Color.LightPink;
+
+ if (!navigating)
+ {
+ log("Navigating");
+ navigating = true;
+ }
+
+ if (cancel)
+ {
+ e.Cancel = true;
+ log("Cancel navigation");
+ cancel = false;
+ }
+ };
+
+ webView.Navigated += (s, e) =>
+ {
+ var text = $"Navigated {e.NavigationEvent}, ";
+ text += $"Result: {e.Result}";
+ log(text);
+
+ entry.Text = e.Url;
+ entry.BackgroundColor = Color.LightBlue;
+
+ cancel = false;
+ navigating = false;
+ };
+
+ // Initialize ui here instead of ctor
+ Content = grid;
+ BackgroundColor = Color.Gray;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
index 662ec1c0c..28e2831d0 100644
--- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
@@ -478,6 +478,7 @@
+
diff --git a/Xamarin.Forms.Platform.iOS/Renderers/WkWebViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/WkWebViewRenderer.cs
new file mode 100644
index 000000000..ed3d5a74f
--- /dev/null
+++ b/Xamarin.Forms.Platform.iOS/Renderers/WkWebViewRenderer.cs
@@ -0,0 +1,289 @@
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Threading.Tasks;
+using Foundation;
+using UIKit;
+using WebKit;
+using Xamarin.Forms.Internals;
+using Uri = System.Uri;
+
+namespace Xamarin.Forms.Platform.iOS
+{
+ public class WkWebViewRenderer : WKWebView, IVisualElementRenderer, IWebViewDelegate, IEffectControlProvider
+ {
+ EventTracker _events;
+ bool _ignoreSourceChanges;
+ WebNavigationEvent _lastBackForwardEvent;
+ VisualElementPackager _packager;
+#pragma warning disable 0414
+ VisualElementTracker _tracker;
+#pragma warning restore 0414
+ public WkWebViewRenderer() : base(RectangleF.Empty, new WKWebViewConfiguration())
+ {
+ }
+
+ WebView WebView => Element as WebView;
+
+ public VisualElement Element { get; private set; }
+
+ public event EventHandler ElementChanged;
+
+ public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ return NativeView.GetSizeRequest(widthConstraint, heightConstraint, 44, 44);
+ }
+
+ public void SetElement(VisualElement element)
+ {
+ var oldElement = Element;
+ Element = element;
+ Element.PropertyChanged += HandlePropertyChanged;
+ WebView.EvalRequested += OnEvalRequested;
+ WebView.EvaluateJavaScriptRequested += OnEvaluateJavaScriptRequested;
+ WebView.GoBackRequested += OnGoBackRequested;
+ WebView.GoForwardRequested += OnGoForwardRequested;
+ NavigationDelegate = new CustomWebViewDelegate(this);
+
+ BackgroundColor = UIColor.Clear;
+
+ AutosizesSubviews = true;
+
+ _tracker = new VisualElementTracker(this);
+
+ _packager = new VisualElementPackager(this);
+ _packager.Load();
+
+ _events = new EventTracker(this);
+ _events.LoadEvents(this);
+
+ Load();
+
+ OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+
+ EffectUtilities.RegisterEffectControlProvider(this, oldElement, element);
+
+ if (Element != null && !string.IsNullOrEmpty(Element.AutomationId))
+ AccessibilityIdentifier = Element.AutomationId;
+
+ if (element != null)
+ element.SendViewInitialized(this);
+ }
+
+ public void SetElementSize(Size size)
+ {
+ Layout.LayoutChildIntoBoundingRegion(Element, new Rectangle(Element.X, Element.Y, size.Width, size.Height));
+ }
+
+ public void LoadHtml(string html, string baseUrl)
+ {
+ if (html != null)
+ LoadHtmlString(html, baseUrl == null ? new NSUrl(NSBundle.MainBundle.BundlePath, true) : new NSUrl(baseUrl, true));
+ }
+
+ public void LoadUrl(string url)
+ {
+ var uri = new Uri(url);
+ var safeHostUri = new Uri($"{uri.Scheme}://{uri.Authority}", UriKind.Absolute);
+ var safeRelativeUri = new Uri($"{uri.PathAndQuery}{uri.Fragment}", UriKind.Relative);
+ LoadRequest(new NSUrlRequest(new Uri(safeHostUri, safeRelativeUri)));
+ }
+
+ public override void LayoutSubviews()
+ {
+ base.LayoutSubviews();
+
+ // ensure that inner scrollview properly resizes when frame of webview updated
+ ScrollView.Frame = Bounds;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (IsLoading)
+ StopLoading();
+
+ Element.PropertyChanged -= HandlePropertyChanged;
+ WebView.EvalRequested -= OnEvalRequested;
+ WebView.EvaluateJavaScriptRequested -= OnEvaluateJavaScriptRequested;
+ WebView.GoBackRequested -= OnGoBackRequested;
+ WebView.GoForwardRequested -= OnGoForwardRequested;
+
+ _tracker?.Dispose();
+ _packager?.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
+ {
+ var changed = ElementChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == WebView.SourceProperty.PropertyName)
+ Load();
+ }
+
+ void Load()
+ {
+ if (_ignoreSourceChanges)
+ return;
+
+ if (((WebView)Element).Source != null)
+ ((WebView)Element).Source.Load(this);
+
+ UpdateCanGoBackForward();
+ }
+
+ void OnEvalRequested(object sender, EvalRequested eventArg)
+ {
+ EvaluateJavaScriptAsync(eventArg.Script);
+ }
+
+ async Task OnEvaluateJavaScriptRequested(string script)
+ {
+ var result = await EvaluateJavaScriptAsync(script);
+ return result.ToString();
+ }
+
+ void OnGoBackRequested(object sender, EventArgs eventArgs)
+ {
+ if (CanGoBack)
+ {
+ _lastBackForwardEvent = WebNavigationEvent.Back;
+ GoBack();
+ }
+
+ UpdateCanGoBackForward();
+ }
+
+ void OnGoForwardRequested(object sender, EventArgs eventArgs)
+ {
+ if (CanGoForward)
+ {
+ _lastBackForwardEvent = WebNavigationEvent.Forward;
+ GoForward();
+ }
+
+ UpdateCanGoBackForward();
+ }
+
+ void UpdateCanGoBackForward()
+ {
+ ((IWebViewController)WebView).CanGoBack = CanGoBack;
+ ((IWebViewController)WebView).CanGoForward = CanGoForward;
+ }
+
+ class CustomWebViewDelegate : WKNavigationDelegate
+ {
+ readonly WkWebViewRenderer _renderer;
+ WebNavigationEvent _lastEvent;
+
+ public CustomWebViewDelegate(WkWebViewRenderer renderer)
+ {
+ if (renderer == null)
+ throw new ArgumentNullException("renderer");
+ _renderer = renderer;
+ }
+
+ WebView WebView => _renderer.WebView;
+
+ public override void DidFailNavigation(WKWebView webView, WKNavigation navigation, NSError error)
+ {
+ var url = GetCurrentUrl();
+ WebView.SendNavigated(
+ new WebNavigatedEventArgs(_lastEvent, new UrlWebViewSource { Url = url }, url, WebNavigationResult.Failure)
+ );
+
+ _renderer.UpdateCanGoBackForward();
+ }
+
+ public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
+ {
+ if (webView.IsLoading)
+ return;
+
+ _renderer._ignoreSourceChanges = true;
+ var url = GetCurrentUrl();
+ WebView.SetValueFromRenderer(WebView.SourceProperty, new UrlWebViewSource { Url = url });
+ _renderer._ignoreSourceChanges = false;
+
+ var args = new WebNavigatedEventArgs(_lastEvent, WebView.Source, url, WebNavigationResult.Success);
+ WebView.SendNavigated(args);
+
+ _renderer.UpdateCanGoBackForward();
+ }
+
+ public override void DidStartProvisionalNavigation(WKWebView webView, WKNavigation navigation)
+ {
+ }
+
+ // https://stackoverflow.com/questions/37509990/migrating-from-uiwebview-to-wkwebview
+ public override void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, Action decisionHandler)
+ {
+ var navEvent = WebNavigationEvent.NewPage;
+ var navigationType = navigationAction.NavigationType;
+ switch (navigationType)
+ {
+ case WKNavigationType.LinkActivated:
+ navEvent = WebNavigationEvent.NewPage;
+ break;
+ case WKNavigationType.FormSubmitted:
+ navEvent = WebNavigationEvent.NewPage;
+ break;
+ case WKNavigationType.BackForward:
+ navEvent = _renderer._lastBackForwardEvent;
+ break;
+ case WKNavigationType.Reload:
+ navEvent = WebNavigationEvent.Refresh;
+ break;
+ case WKNavigationType.FormResubmitted:
+ navEvent = WebNavigationEvent.NewPage;
+ break;
+ case WKNavigationType.Other:
+ navEvent = WebNavigationEvent.NewPage;
+ break;
+ }
+
+ _lastEvent = navEvent;
+ var request = navigationAction.Request;
+ var lastUrl = request.Url.ToString();
+ var args = new WebNavigatingEventArgs(navEvent, new UrlWebViewSource { Url = lastUrl }, lastUrl);
+
+ WebView.SendNavigating(args);
+ _renderer.UpdateCanGoBackForward();
+ decisionHandler(args.Cancel ? WKNavigationActionPolicy.Cancel : WKNavigationActionPolicy.Allow);
+ }
+
+ string GetCurrentUrl()
+ {
+ return _renderer?.Url?.AbsoluteUrl?.ToString();
+ }
+ }
+
+ #region IPlatformRenderer implementation
+
+ public UIView NativeView
+ {
+ get { return this; }
+ }
+
+ public UIViewController ViewController
+ {
+ get { return null; }
+ }
+
+ #endregion
+
+ void IEffectControlProvider.RegisterEffect(Effect effect)
+ {
+ VisualElementRenderer.RegisterEffect(effect, this, NativeView);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj
index efd04d92b..f6e8baf2f 100644
--- a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj
+++ b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj
@@ -127,6 +127,7 @@
+
True
True