Xamarin.Forms WebView
WebView
is a view for displaying web and HTML content in your app:
Content
WebView
supports the following types of content:
- HTML & CSS websites – WebView has full support for websites written using HTML & CSS, including JavaScript support.
- Documents – Because WebView is implemented using native components on each platform, WebView is capable of showing documents in the formats that are supported by the underlying platform.
- HTML strings – WebView can show HTML strings from memory.
- Local Files – WebView can present any of the content types above embedded in the app.
Note
WebView
on Windows does not support Silverlight, Flash or any ActiveX controls, even if they are supported by Internet Explorer on that platform.
Websites
To display a website from the internet, set the WebView
's Source
property to a string URL:
var browser = new WebView
{
Source = "https://dotnet.microsoft.com/apps/xamarin"
};
Note
URLs must be fully formed with the protocol specified (i.e. it must have "http://" or "https://" prepended to it).
iOS and ATS
Since version 9, iOS will only allow your application to communicate with servers that implement best-practice security by default. Values must be set in Info.plist
to enable communication with unsecure servers.
Note
If your application requires a connection to an unsecure website, you should always enter the domain as an exception using NSExceptionDomains
instead of turning ATS off completely using NSAllowsArbitraryLoads
. NSAllowsArbitraryLoads
should only be used in extreme emergency situations.
The following demonstrates how to enable a specific domain (in this case xamarin.com) to bypass ATS requirements:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>xamarin.com</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
</dict>
</dict>
</dict>
...
</key>
It is best practice to only enable some domains to bypass ATS, allowing you to use trusted sites while benefitting from the additional security on untrusted domains. The following demonstrates the less secure method of disabling ATS for the app:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads </key>
<true/>
</dict>
...
</key>
See App Transport Security for more information about this new feature in iOS 9.
HTML Strings
If you want to present a string of HTML defined dynamically in code, you'll need to create an instance of HtmlWebViewSource
:
var browser = new WebView();
var htmlSource = new HtmlWebViewSource();
htmlSource.Html = @"<html><body>
<h1>Xamarin.Forms</h1>
<p>Welcome to WebView.</p>
</body></html>";
browser.Source = htmlSource;
In the above code, @
is used to mark the HTML as a verbatim string literal, meaning most escape characters are ignored.
Note
It may be necessary to set the WidthRequest
and HeightRequest
properties of the WebView
to see the HTML content, depending upon the layout the WebView
is a child of. For example, this is required in a StackLayout
.
Local HTML Content
WebView can display content from HTML, CSS and JavaScript embedded within the app. For example:
<html>
<head>
<title>Xamarin Forms</title>
</head>
<body>
<h1>Xamarin.Forms</h1>
<p>This is an iOS web page.</p>
<img src="XamarinLogo.png" />
</body>
</html>
CSS:
html,body {
margin:0;
padding:10;
}
body,p,h1 {
font-family: Chalkduster;
}
Note that the fonts specified in the above CSS will need to be customized for each platform, as not every platform has the same fonts.
To display local content using a WebView
, you'll need to open the HTML file like any other, then load the contents as a string into the Html
property of an HtmlWebViewSource
. For more information on opening files, see Working with Files.
The following screenshots show the result of displaying local content on each platform:
Although the first page has been loaded, the WebView
has no knowledge of where the HTML came from. That is a problem when dealing with pages that reference local resources. Examples of when that might happen include when local pages link to each other, a page makes use of a separate JavaScript file, or a page links to a CSS stylesheet.
To solve this, you need to tell the WebView
where to find files on the filesystem. Do that by setting the BaseUrl
property on the HtmlWebViewSource
used by the WebView
.
Because the filesystem on each of the operating systems is different, you need to determine that URL on each platform. Xamarin.Forms exposes the DependencyService
for resolving dependencies at runtime on each platform.
To use the DependencyService
, first define an interface that can be implemented on each platform:
public interface IBaseUrl { string Get(); }
Note that until the interface is implemented on each platform, the app will not run. In the common project, make sure that you remember to set the BaseUrl
using the DependencyService
:
var source = new HtmlWebViewSource();
source.BaseUrl = DependencyService.Get<IBaseUrl>().Get();
Implementations of the interface for each platform must then be provided.
iOS
On iOS, the web content should be located in the project's root directory or Resources directory with build action BundleResource, as demonstrated below:
The BaseUrl
should be set to the path of the main bundle:
[assembly: Dependency (typeof (BaseUrl_iOS))]
namespace WorkingWithWebview.iOS
{
public class BaseUrl_iOS : IBaseUrl
{
public string Get()
{
return NSBundle.MainBundle.BundlePath;
}
}
}
Android
On Android, place HTML, CSS, and images in the Assets folder with build action AndroidAsset as demonstrated below:
On Android, the BaseUrl
should be set to "file:///android_asset/"
:
[assembly: Dependency (typeof(BaseUrl_Android))]
namespace WorkingWithWebview.Android
{
public class BaseUrl_Android : IBaseUrl
{
public string Get()
{
return "file:///android_asset/";
}
}
}
On Android, files in the Assets folder can also be accessed through the current Android context, which is exposed by the MainActivity.Instance
property:
var assetManager = MainActivity.Instance.Assets;
using (var streamReader = new StreamReader (assetManager.Open ("local.html")))
{
var html = streamReader.ReadToEnd ();
}
Universal Windows Platform
On Universal Windows Platform (UWP) projects, place HTML, CSS and images in the project root with the build action set to Content.
The BaseUrl
should be set to "ms-appx-web:///"
:
[assembly: Dependency(typeof(BaseUrl))]
namespace WorkingWithWebview.UWP
{
public class BaseUrl : IBaseUrl
{
public string Get()
{
return "ms-appx-web:///";
}
}
}
Navigation
WebView supports navigation through several methods and properties that it makes available:
- GoForward() – if
CanGoForward
is true, callingGoForward
navigates forward to the next visited page. - GoBack() – if
CanGoBack
is true, callingGoBack
will navigate to the last visited page. - CanGoBack –
true
if there are pages to navigate back to,false
if the browser is at the starting URL. - CanGoForward –
true
if the user has navigated backwards and can move forward to a page that was already visited.
Within pages, WebView
does not support multi-touch gestures. It is important to make sure that content is mobile-optimized and appears without the need for zooming.
It is common for applications to show a link within a WebView
, rather than the device's browser. In those situations, it is useful to allow normal navigation, but when the user hits back while they are on the starting link, the app should return to the normal app view.
Use the built-in navigation methods and properties to enable this scenario.
Start by creating the page for the browser view:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="WebViewSample.InAppBrowserXaml"
Title="Browser">
<StackLayout Margin="20">
<StackLayout Orientation="Horizontal">
<Button Text="Back" HorizontalOptions="StartAndExpand" Clicked="OnBackButtonClicked" />
<Button Text="Forward" HorizontalOptions="EndAndExpand" Clicked="OnForwardButtonClicked" />
</StackLayout>
<!-- WebView needs to be given height and width request within layouts to render. -->
<WebView x:Name="webView" WidthRequest="1000" HeightRequest="1000" />
</StackLayout>
</ContentPage>
In the code-behind:
public partial class InAppBrowserXaml : ContentPage
{
public InAppBrowserXaml(string URL)
{
InitializeComponent();
webView.Source = URL;
}
async void OnBackButtonClicked(object sender, EventArgs e)
{
if (webView.CanGoBack)
{
webView.GoBack();
}
else
{
await Navigation.PopAsync();
}
}
void OnForwardButtonClicked(object sender, EventArgs e)
{
if (webView.CanGoForward)
{
webView.GoForward();
}
}
}
That's it!
Events
WebView raises the following events to help you respond to changes in state:
Navigating
– event raised when the WebView begins loading a new page.Navigated
– event raised when the page is loaded and navigation has stopped.ReloadRequested
– event raised when a request is made to reload the current content.
The WebNavigatingEventArgs
object that accompanies the Navigating
event has four properties:
Cancel
– indicates whether or not to cancel the navigation.NavigationEvent
– the navigation event that was raised.Source
– the element that performed the navigation.Url
– the navigation destination.
The WebNavigatedEventArgs
object that accompanies the Navigated
event has four properties:
NavigationEvent
– the navigation event that was raised.Result
– describes the result of the navigation, using aWebNavigationResult
enumeration member. Valid values areCancel
,Failure
,Success
, andTimeout
.Source
– the element that performed the navigation.Url
– the navigation destination.
If you anticipate using webpages that take a long time to load, consider using the Navigating
and Navigated
events to implement a status indicator. For example:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="WebViewSample.LoadingLabelXaml"
Title="Loading Demo">
<StackLayout>
<!--Loading label should not render by default.-->
<Label x:Name="labelLoading" Text="Loading..." IsVisible="false" />
<WebView HeightRequest="1000" WidthRequest="1000" Source="https://dotnet.microsoft.com/apps/xamarin" Navigated="webviewNavigated" Navigating="webviewNavigating" />
</StackLayout>
</ContentPage>
The two event handlers:
void webviewNavigating(object sender, WebNavigatingEventArgs e)
{
labelLoading.IsVisible = true;
}
void webviewNavigated(object sender, WebNavigatedEventArgs e)
{
labelLoading.IsVisible = false;
}
This results in the following output (loading):
Finished Loading:
Reloading content
WebView
has a Reload
method that can be used to reload the current content:
var webView = new WebView();
...
webView.Reload();
When the Reload
method is invoked the ReloadRequested
event is fired, indicating that a request has been made to reload the current content.
Performance
Popular web browsers adopt technologies like hardware accelerated rendering and JavaScript compilation. Prior to Xamarin.Forms 4.4, the Xamarin.Forms WebView
was implemented on iOS by the UIWebView
class. However, many of these technologies were unavailable in this implementation. Therefore, since Xamarin.Forms 4.4, the Xamarin.Forms WebView
is implemented on iOS by the WkWebView
class, which supports faster browsing.
Note
On iOS, the WkWebViewRenderer
has a constructor overload that accepts a WkWebViewConfiguration
argument. This enables the renderer to be configured on creation.
An application can return to using the iOS UIWebView
class to implement the Xamarin.Forms WebView
, for compatibility reasons. This can be achieved by adding the following code to the AssemblyInfo.cs file in the iOS platform project for the application:
// Opt-in to using UIWebView instead of WkWebView.
[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(Xamarin.Forms.Platform.iOS.WebViewRenderer))]
Note
In Xamarin.Forms 5.0, the WebViewRenderer
class has been removed. Therefore, Xamarin.Forms 5.0 doesn't contain a reference to the UIWebView
control.
WebView
on Android by default is about as fast as the built-in browser.
The UWP WebView uses the Microsoft Edge rendering engine. Desktop and tablet devices should see the same performance as using the Edge browser itself.
Permissions
In order for WebView
to work, you must make sure that permissions are set for each platform. Note that on some platforms, WebView
will work in debug mode, but not when built for release. That is because some permissions, like those for internet access on Android, are set by default by Visual Studio for Mac when in debug mode.
- UWP – requires the Internet (Client & Server) capability when displaying network content.
- Android – requires
INTERNET
only when displaying content from the network. Local content requires no special permissions. - iOS – requires no special permissions.
Layout
Unlike most other Xamarin.Forms views, WebView
requires that HeightRequest
and WidthRequest
are specified when contained in StackLayout or RelativeLayout. If you fail to specify those properties, the WebView
will not render.
The following examples demonstrate layouts that result in working, rendering WebView
s:
StackLayout with WidthRequest & HeightRequest:
<StackLayout>
<Label Text="test" />
<WebView Source="https://dotnet.microsoft.com/apps/xamarin"
HeightRequest="1000"
WidthRequest="1000" />
</StackLayout>
RelativeLayout with WidthRequest & HeightRequest:
<RelativeLayout>
<Label Text="test"
RelativeLayout.XConstraint= "{ConstraintExpression
Type=Constant, Constant=10}"
RelativeLayout.YConstraint= "{ConstraintExpression
Type=Constant, Constant=20}" />
<WebView Source="https://dotnet.microsoft.com/apps/xamarin"
RelativeLayout.XConstraint="{ConstraintExpression Type=Constant,
Constant=10}"
RelativeLayout.YConstraint="{ConstraintExpression Type=Constant,
Constant=50}"
WidthRequest="1000" HeightRequest="1000" />
</RelativeLayout>
AbsoluteLayout without WidthRequest & HeightRequest:
<AbsoluteLayout>
<Label Text="test" AbsoluteLayout.LayoutBounds="0,0,100,100" />
<WebView Source="https://dotnet.microsoft.com/apps/xamarin"
AbsoluteLayout.LayoutBounds="0,150,500,500" />
</AbsoluteLayout>
Grid without WidthRequest & HeightRequest. Grid is one of the few layouts that does not require specifying requested heights and widths.:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="test" Grid.Row="0" />
<WebView Source="https://dotnet.microsoft.com/apps/xamarin" Grid.Row="1" />
</Grid>
Invoking JavaScript
WebView
includes the ability to invoke a JavaScript function from C#, and return any result to the calling C# code. This is accomplished with the WebView.EvaluateJavaScriptAsync
method:
var numberEntry = new Entry { Text = "5" };
var resultLabel = new Label();
var webView = new WebView();
...
int number = int.Parse(numberEntry.Text);
string result = await webView.EvaluateJavaScriptAsync($"factorial({number})");
resultLabel.Text = $"Factorial of {number} is {result}.";
The WebView.EvaluateJavaScriptAsync
method evaluates the JavaScript that's specified as the argument, and returns any result as a string
. In this example, the factorial
JavaScript function is invoked, which returns the factorial of number
as a result. This JavaScript function is defined in the local HTML file that the WebView
loads, and is shown in the following example:
<html>
<body>
<script type="text/javascript">
function factorial(num) {
if (num === 0 || num === 1)
return 1;
for (var i = num - 1; i >= 1; i--) {
num *= i;
}
return num;
}
</script>
</body>
</html>
Cookies
Cookies can be set on a WebView
, which are then sent with the web request to the specified URL. This is accomplished by adding Cookie
objects to a CookieContainer
, which is then set as the value of the WebView.Cookies
bindable property. The following code shows an example of this:
using System.Net;
using Xamarin.Forms;
// ...
CookieContainer cookieContainer = new CookieContainer();
Uri uri = new Uri("https://dotnet.microsoft.com/apps/xamarin", UriKind.RelativeOrAbsolute);
Cookie cookie = new Cookie
{
Name = "XamarinCookie",
Expires = DateTime.Now.AddDays(1),
Value = "My cookie",
Domain = uri.Host,
Path = "/"
};
cookieContainer.Add(uri, cookie);
webView.Cookies = cookieContainer;
webView.Source = new UrlWebViewSource { Url = uri.ToString() };
In this example, a single Cookie
is added to the CookieContainer
object, which is then set as the value of the WebView.Cookies
property. When the WebView
sends a web request to the specified URL, the cookie is sent with the request.
UIWebView Deprecation and App Store Rejection (ITMS-90809)
Starting in April 2020, Apple will reject apps that still use the deprecated UIWebView
API. While Xamarin.Forms has switched to WKWebView
as the default, there is still a reference to the older SDK in the Xamarin.Forms binaries. Current iOS linker behavior does not remove this, and as a result the deprecated UIWebView
API will still appear to be referenced from your app when you submit to the App Store.
Important
In Xamarin.Forms 5.0, the WebViewRenderer
class has been removed. Therefore, Xamarin.Forms 5.0 doesn't contain a reference to the UIWebView
control.
A preview version of the linker is available to fix this issue. To enable the preview, you will need to supply an additional argument --optimize=experimental-xforms-product-type
to the linker.
The prerequisites for this to work are:
- Xamarin.Forms 4.5 or higher. Xamarin.Forms 4.6, or higher, is required if your app uses Material Visual.
- Xamarin.iOS 13.10.0.17 or higher. Check your Xamarin.iOS version in Visual Studio. This version of Xamarin.iOS is included with Visual Studio for Mac 8.4.1 and Visual Studio 16.4.3.
- Remove references to
UIWebView
. Your code should not have any references toUIWebView
or any classes that make use ofUIWebView
.
For more information about detecting and removing UIWebView
references, see UIWebView deprecation.
Configure the linker
Follow these steps for the linker to remove UIWebView
references:
- Open iOS project properties – Right-click your iOS project and choose Properties.
- Navigate to the iOS Build section – Select the iOS Build section.
- Update the Additional mtouch arguments – In the Additional mtouch arguments add this flag
--optimize=experimental-xforms-product-type
(in addition to any value that might already be in there). Note: this flag works together with the Linker Behavior set to SDK Only or Link All. If, for any reason, you see errors when setting the Linker Behavior to All, this is most likely a problem within the app code or a third-party library that is not linker safe. For more information on the linker, see Linking Xamarin.iOS Apps. - Update all build configurations – Use the Configuration and Platform lists at the top of the window to update all build configurations. The most important configuration to update is the Release/iPhone configuration, since that is typically used to create builds for App Store submission.
You can see the window with the new flag in place in this screenshot:
Now when you create a new (release) build and submit it to the App Store, there should be no warnings about the deprecated API.