Xamarin.FormsでオフラインWEBアプリを作成する方法

基本的にXamarinStudioでの説明にはなりますが、VisualStudioでも同様に可能です。

事前準備

まず、参照するHTML側に以下の準備が必要です。
・使用しているコンテンツをWebフォルダ内にひとまとめにする。
・ファイルのリンクを相対パスにする。
・ファイル名やフォルダ名に空白を使わない。

当たり前ですが、オフラインで利用するためにはオンラインアクセスをしないようにする必要があります。そのため、共通利用しているjqueryやCSSなどのファイルや画像コンテンツの参照はWebフォルダ内で完結する必要があります。
ファイル名に空白を使えない理由は、コンテンツをすべてリソース化するためです。(リソース名に空白は設定できません。)また、日本語名のファイルは避けたほうが無難です。

ここでは、「http://www.jssor.com/」のBanner Rotatorのサンプルをダウンロードして利用してみます。
「banner-rotator.slider.zip」を解凍すると、「banner-rotator.slider」フォルダが出てきますので、フォルダ名を「webdir」に変更しておきます。

プロジェクトの作成

まず、「OffLineApp」という名前でMultiplatformのXamarin.FormプロジェクトをPortable Class Libraryで作成します。
※プロジェクト名は好きに変更して、読み替えてください。

newsln1

newsln2

newsln3

無事「OffLineApp」と「OffLineApp.Droid」、「OffLineApp.iOS」の3つのプロジェクトが作成出来たら、3つともパッケージの更新を実施しておいてください。

コンテンツの追加

次に、事前準備で用意したWebフォルダをコンテンツとして追加します。
※ここでは前述のとおり「webdir」というフォルダを利用しています。
Androidアプリ側は「OffLineApp.Droid」内の「Assets」フォルダ内に入れます。
「Add Existing Folder…」で登録すれば、フォルダごと一括で登録ができます。

addfolder
selectwebdir
注意点として、なぜかAndroid側では「Assets」フォルダに入れただけでは、「AndroidAsset」として認識してくれないので、すべてのファイルを確認しつつ、ビルドアクションを「None」から「AndroidAsset」に変更します。
※複数選択して、右クリックから変更すると一括で変更もできます。
droidasset

同様にiOSアプリ側は「OffLineApp.iOS」内の「Resources」フォルダ内に同じものを追加します。
iOSでは問題なくビルドアクションが「BundleResource」になるので、こちらは設定する必要はありません。
iosres

DependencyServiceを使ったベースURLの設定コード追加

今度は追加したコンテンツのフォルダを各プラットフォームごとにベースURLの設定をします。
各プラットフォームに空のクラスで「BaseUrl.cs」を作成して、以下のように修正します。

Android側[OffLineApp.Droid>BaseUrl.cs]

using OffLineApp.Droid;
using Xamarin.Forms;

[assembly: Dependency(typeof(BaseUrl))]
namespace OffLineApp.Droid
{
	public class BaseUrl : IBaseUrl
	{
		public string Get()
		{
			return "file:///android_asset/webdir/";
		}
	}
}

iOS側[OffLineApp.iOS>BaseUrl.cs]

using OffLineApp.iOS;
using Foundation;
using Xamarin.Forms;

[assembly: Dependency(typeof(BaseUrl))]
namespace OffLineApp.iOS
{
	public class BaseUrl : IBaseUrl
	{
		public string Get()
		{
			return NSBundle.MainBundle.BundlePath + "/webdir";
		}
	}
}

カスタムWebViewのコード追加

Android側のみカスタムレンダラーを利用してアクセスセキュリティを緩和させる必要がありますので、
Androidのプラットフォームに空のクラスで「CustomWebViewRenderer.cs」を作成して、以下のように修正します。

Android側[OffLineApp.Droid>CustomWebViewRenderer.cs]

using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using OffLineApp;
using OffLineApp.Droid;

[assembly: ExportRenderer(typeof(WebView), typeof(WebViewRenderer))]
namespace OffLineApp.Droid
{
	public class CustomWebViewRenderer : WebViewRenderer
	{
		protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
		{
			base.OnElementChanged(e);
			if (Control == null) return;

			Control.Settings.DomStorageEnabled = true;
			Control.Settings.JavaScriptEnabled = true;

			//javascriptのfileスキームへのアクセス許可
			Control.Settings.AllowFileAccessFromFileURLs = true;
			Control.Settings.AllowUniversalAccessFromFileURLs = true;
		}
	}
}

WebViewの開始ページを設定するコード追加

これで、準備が整いましたのでWebViewで開始ページを表示させるコードを書きます。
※Appの書き換えとCustomWebViewクラス、IBaseUrlインターフェイス宣言、GetHTML関数を追加します。

共通部分[OffLineApp>OffLineApp.cs(修正)]

using System;
using Xamarin.Forms;

namespace OffLineApp
{
	public class CustomWebView : WebView { }
	public interface IBaseUrl { string Get(); }

	public class App : Application
	{
		public App()
		{
			// The root page of your application
			var content = new ContentPage()
			{
				Title = "テスト",
				Content= new CustomWebView { Source = GetHTML() }
			};

			MainPage = content;
		}

		private HtmlWebViewSource GetHTML() 
		{ 
			HtmlWebViewSource html = new HtmlWebViewSource
			{
				Html=@"<!DOCTYPE html><meta http-equiv=""refresh"" content=""0;URL=./index.html""/></html>",
				BaseUrl = DependencyService.Get<IBaseUrl>().Get()
			};
			return html;
		}
	}
}

コードではわかりやすく「index.html」に設定していますが、ここを今回のサンプル用に合わせて「with-jquery.html」に変更します。

				Html=@"<!DOCTYPE html><meta http-equiv=""refresh"" content=""0;URL=./with-jquery.html""/></html>",

動作確認

これで完了しましたので、スタートアッププロジェクトを各デバイスに変更して動作を確認します。
もしコンパイルでエラーが出てしまった場合は、コード記述を見直してください。
iosdebug

droiddebug

このように各プラットフォームでWebアプリの動作を確認できました。
これで機内モードにしてもちゃんと動いているのが確認できると思います。

サンプルコードはこちら(GitHub)

課題

Webフォルダも本来はEmbedded Resourceを利用して各プラットフォームで共通化できればよかったのですが、もっと複雑な処理を埋め込まなければいけなさそうなので、止めておきました。面倒ですが、2重管理してください。
※この部分においてはApache Cordovaの方が楽かもしれません。
また、UWPアプリ側(Windows 10 mobile)はまだ検証していませんのでまた今度。

<追記 2016/7/11>
少し試しましたが、UWPアプリ側では挙動が異なるようです。
残念ながらUWPで利用されるWebViewでは、オンラインのみ対応しているだけで、オフラインでのWebアプリは対応していないようです。
(EdgeでのProgressive Web Appsを開発中とのことです。。。)

参考サイト

ティラノスクリプトをxamarin-formsで動かす(android編)
ここを参考にしましたので、もちろんティラノビルダーのリリースにもしっかり動作します。(確認済
※UWPアプリは除く。

Search index:
[Xamarin.Forms] Create an Offline Web Application

この記事をシェアする
Tweet about this on Twitter
Twitter
Share on Facebook
Facebook
0

Comments

Comments