App wrapping a website with Xamarin
Clients often want their website available in the app store, mostly just for app store presence. I got asked to do app wrapping at work a while ago, because the client purchased a tool that did not have a native app available, only a mobile website. It just had to be made available in the internal app store. Colleagues had to be able to install it by themselves and use it on their own devices without too much fuzz.
In this post I want to show you some of the hurdles while developing a website wrapper and how to overcome them.
What is app wrapping?
It’s how I call packaging a website into an app. Wrapping a website into an app. Or just wrapping an app around a website. Hence, app wrapping. It’s actually just a form of hybrid app where you make a website available as an app.
Enterprise environment
When you are dealing with internal apps, you often have an internal app store available that is provided by an MDM (Mobile Device Management) solution. A few examples of these are Mobile Iron, AirWatch, Microsoft Intune and many more. In my case there is a mobile browser available, provided by the MDM solution as well, which sets up a secure tunnel to the intranet website that the client wanted to package as an app.
So you can already see that internal apps have to deal with different constraints than public apps.
There is only a requirement to support iOS in this case, so I’m using Xamarin.iOS for this one. You could easily extend the solution to Xamarin Forms by creating the appropriate renderers.
Show the website full screen
First thing to do when app wrapping is show the website in a full screen browser. You don’t get a navigation bar like a normal browser in this case, so this poses a few requirements on your mobile website:
- Supply navigation on all pages
- Show a back button at all times
In the main Storyboard, you need to create a Web View (UIWebView) that spans the whole screen, so set the constraints accordingly. Then in the controller, use the ViewDidLoad method to load the website:
public override async void ViewDidLoad ()
{
base.ViewDidLoad();
// Set to background color of website to load, so you don't get a white flash while loading the page
WebView.BackgroundColor = new UIColor(0, 0.635f, 0.925f, 1.0f); // equals #e3ebf3
// Prevent bounces so it feels more like an actual app
WebView.ScrollView.Bounces = false;
// Load the actual page
WebView.LoadRequest (new NSUrlRequest (new NSUrl ("https://example.com")));
}
In this snippet, you can see I did some other settings as well. First is supplying a background color that matches the background of the website that is loaded. This is to prevent a white flash while loading the website. Secondly, I disable the bouncing of the default browser, so the website feels more like an actual app.
Opening links
This is an internal website, so there are 2 different kind of links available:
- Intranet pages – these need to be handled by our MDM browser
- Internet pages – these can be handled by the normal browser
The UIWebView has a method ShouldStartLoad that can check which link is about to be opened, and allow you to handle the request yourself.
public override void ViewDidLoad ()
{
WebView.ShouldStartLoad = (webView, request, navigationType) =>
{
// ...
return true; // Notify the WebView that it can handle the request
};
}
You can notify the UIWebView that you handled the request yourself, by returning false from this method. In that case, the UIWebView won’t handle the request anymore.
To open a different app on iOS, you can leverage app schemes:
public override void ViewDidLoad ()
{
WebView.ShouldStartLoad = (webView, request, navigationType) =>
{
if (this.OpenInMDMBrowser(request))
{
// Naive implementation to change the URL scheme
UIApplication.SharedApplication.OpenUrl(
new NSUrl(
request.Url.AbsoluteString.Replace("http://", "mdmbrowser://").Replace("https://", "mdmbrowser://")
)
);
return false; // Notify the WebView that we handled the action ourselves
}
return true; // Notify the WebView that it can handle the request
};
}
In the snippet above, I did a naive implementation to change http:// and https:// to the required app scheme. The mdmbrowser:// scheme is different depending on the app you want to open. A different MDM provider, means a different scheme.
So if you want to open a URL in Safari, outside of your app, you can do the same thing but leave the http:// or https:// scheme.
Dealing with impossible requests
It’s not all about opening links though. Sometimes theres an action that’s you want to prevent or leverage in your app. In my case I had to prevent users from changing their password through the app wrapper. For this, you can once again leverage the ShouldStartLoad method:
WebView.ShouldStartLoad = (webView, request, navigationType) =>
{
if (this.IsPasswordChange(request))
{
var alertController = UIAlertController.Create("Change password",
"You can't change your password through this app.", UIAlertControllerStyle.Alert);
// Add action
alertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
// Present alert
PresentViewController(alertController, true, null);
return false; // Notify the WebView that we handled the action ourselves
}
return true;
};
This example shows that you can actually do whatever you want, depending on the request that comes in. In my case, that’s just showing an alert to the user that the action is not possible. And in the end returning false so that the UIWebView won’t deal with the request.
Apple App Store rejection?
If you try to submit these kinds of apps to the public App Store without a lot of added functionality, Apple will most likely reject it based on their Minimum Functionality rule which states:
Your app should include features, content, and UI that elevate it beyond a repackaged website. If your app is not particularly useful, unique, or “app-like,” it doesn’t belong on the App Store.
— App Store Review Guidelines, by Apple Developer
But for internal purposes you don’t have to deal with Apple’s review process, so you have more opportunities and no risk for rejection there.
Closing words
There are still some other things to keep in mind. On iOS, you can’t open http:// schemes in an app without configuring ATS. If you have a file upload somewhere, make sure that you include the permissions to use the camera and/or photo library in your Info.plist. If you don’t, your app will crash.
Checking the reachability of the website is an important part as well. You should show the users an appropriate message when they don’t have an internet connection, the VPN tunnel isn’t available or the website they’re trying to open is down.
I also have a requirement to login into ADFS without the user noticing, because they are logged out automatically every day. That means I need to open a different ViewController where I can store the credentials (in a safe way!) initially, and make use of Javascript to automate the login afterwards. How you can achieve this will be for another post.
So it is certainly possible to do app wrapping with Xamarin, but I’m not sure if using Xamarin is actually the best possible approach. Which techniques do you use for app wrapping? Please leave a comment!
Leave a comment