Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(9)

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(9)

カスタムURLスキーム方式/iOSで特定のページの読み込みを制限する

2014年4月1日

JavaScriptからネイティブ側へ命令を投げる方法(Android&iOS対応)を紹介。またiOSでセキュリティのために、特定のドメインのページの読み込みを制限する方法も紹介。書籍転載の9本目。

  • このエントリーをはてなブックマークに追加

 オープンソースのフレームワーク「Apache Cordova」(Adobe版:「 PhoneGap」)を用いると、HTML5でiOSとAndroid向けのアプリをまとめて作成できます。この連載記事(=書籍転載)の第1回第6回で、その開発方法を一通り解説しています。また、第7回からは、「JavaScriptコード」と「iOS/Andoridネイティブ機能」をつなぐ仕組みを説明しています。

書籍転載について

 本コーナーは、技術評論社発行の書籍『[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門』の中から、特にBuild Insiderの読者に有用だと考えられる項目を編集部が選び、同社の許可を得て転載したものです。

 『[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門』の詳細や購入は技術評論社のサイト目次ページをご覧ください。

ご注意

本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。

 前回は「addJavascriptInterface方式(後編)/ネイティブからJavaScriptへ値を渡す」を説明しました。本稿はその続きです。

11.5 カスタムURLスキーム方式

 今度は、カスタムURLスキームを用いてJavaScriptからネイティブ側へ命令を投げる方法を紹介します。この方法はAndroidとiOS、両方のプラットフォームで利用できます。

 iOSのUIWebViewクラスとAndroidのWebViewクラスの両方で、オリジナルのスキームを持ったURLに対する読み込み方法を制御できます。例えば「http://」から始まるURLは通常どおりページを読み込ませるけれども、ある特定のスキームを持ったURLを読み込む場合は全く別の挙動をする、といった設定ができます。

 この方法では、JavaScriptからネイティブへ値を受け渡すことはできますが、ネイティブから結果を受け取ることができません。したがって、ネイティブからJavaScriptへ値を受け渡す方法も同時に用いられます。

 具体的なカスタムURLの例としては、リスト11.14のように「apicall://」というスキーマのURLを読み込もうとした場合には、そのURLに渡されたAPI名やパラメータを用いてネイティブ側でネイティブAPIの呼び出しを行う、といったことを行います。

URL
apicall://(呼び出しAPI名)/?(JSONパラメータ)
リスト11.14 カスタムURLスキーム

11.5.1 Androidでの実装

 Androidでは、WebViewに対してWebViewClientオブジェクトを設定することで、カスタムURLスキームを扱うときの挙動を設定できます。具体的には、WebViewClientクラスのshouldOverrideUrlLoadingメソッドを設定します。

 WebView内で新たなURLを読み込む際には、必ずこのshouldOverrideUrlLoadingメソッドが呼ばれます。このメソッドの返り値にtrueを返すことで、WebViewのデフォルトの挙動を消すことができます。

 例えば、WebViewに「http://google.com」というURLにアクセスさせたくない場合には、shouldOverrideUrlLoadingメソッド内で渡されたURLが「http://google.com」を含んでいればtrueを返す、というロジックを記述することで実現できます。

 このメソッドの引数には、これから読み込む予定のURLが渡ってきます。このURLがカスタムスキームを含んでいればネイティブ側の処理を呼び出す、そうでなければデフォルトの動作をする、というロジックでこのブリッジは提供されます(リスト11.15)。

Java
package com.example.hybridappsandbox;

import java.net.MalformedURLException;
import java.net.URL;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class CustomSchemaExample extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    WebView webView = new WebView(this);
    webView.getSettings().setJavaScriptEnabled(true);

    webView.setWebViewClient(new MyWebViewClient());

    webView.loadUrl("file:///android_asset/customSchemaExample.html");

    setContentView(webView);
  }

  class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
      if (url.startsWith("apicall://")) {
        String[] params = url.split("/?");
        // apicall://というURLが来たときには、ネイティブの処理を呼び出す
        // ...

        return true;
      }

      return false;
    }
  }
}
リスト11.15 AndroidのWebViewでカスタムスキームを利用する

 JavaScript側では、リスト11.16のようにiframe要素やlocation.hrefを利用することでカスタムURLスキームを呼び出すことができます。

JavaScript
<script type="text/javascript">
document.addEventListener('load', function() {
  var element = document.createElement('iframe');
  element.setAttribute('width', 0);
  element.setAttribute('height', 0);
  element.setAttribute('src', 'about:blank');
  document.body.appendChild(element);

  // ネイティブ側へAPI呼び出しをする関数
  window.callAPI = function(name, parameters) {
    element.src = 'apicall://' name + '/?' + JSON.stringify(parameters);
  };
}, false);
</script>
リスト11.16 JavaScriptからカスタムスキームを利用する

11.5.2 iOSでの実装

 iOSでは、UIWebViewクラスに対して、delegateとしてUIWebViewDelegateを設定してカスタムURLスキームを実装します。UIWebViewクラスでなんらかのURLが読み込まれる前に、UIWebViewDelegateのwebView:shouldStartLoadWithRequest:navigationType:というメソッドが呼ばれます。このメソッドがYESを返せば、そのURLの読み込みが実行されます。逆にNOを返すと、そのURLに対する読み込みは実行されません。

 リスト11.17は、「apicall://」というカスタムURLスキームを使ってJavaScriptからネイティブへの機能を呼び出す例です。UIViewControllerクラスを継承しているので、そのまま利用されます。

Objective-C
#import <UIKit/UIKit.h>

@interface CustomSchemaExampleViewController : UIViewController
  <UIWebViewDelegate>
@end

@implementation CustomSchemaExampleViewController

- (void)viewDidLoad
{
  [super viewDidLoad];

  CGRect screenRect = UIScreen.mainScreen.bounds;
  UIWebView *webView =
    [UIWebView.alloc initWithFrame:CGRectMake(0, 0,
      screenRect.size.width, screenRect.size.height)];

  NSURL *url = [NSURL fileURLWithPath:
    [NSBundle.mainBundle pathForResource:@"www/custom_schema_example.html"
      ofType:nil]];
  webView.delegate = self;

  [webView loadRequest:[NSURLRequest requestWithURL:url]];
  [self.view addSubview:webView];
}

- (BOOL)webView:(UIWebView *)webView
  shouldStartLoadWithRequest:(NSURLRequest *)request
    navigationType:(UIWebViewNavigationType)navigationType
{
  NSString *prefix = @"apicall://";

  NSString *urlStr = request.URL.absoluteString;
  if ([urlStr hasPrefix:prefix]) {
    NSLog(@"hogehoge!!");
    NSLog(@"host=%@ query=%@", request.URL.host, request.URL.query);
    return NO;
  }

  return YES;
}

@end
リスト11.17 iOSでWebViewのカスタムスキームを利用する

11.6 iOSで特定のページの読み込みを制限する

 AndroidのWebViewと同様に、iOSのUIWebViewクラスでもセキュリティを担保するために特定のドメインのページの読み込みを制限する必要があります。ここでは、iOSでの制限方法を紹介します。

11.6.1 webView:shouldStartLoadWithRequest:navigationType:を用いる

 iOSのUIWebViewクラスにはdelegateを設定できます。とくにWebViewDelegateプロトコルのwebView:shouldStartLoadWithRequest:navigationType:メソッドを記述することで、URLによって読み込みの挙動を変えることができます。

 リスト11.18のWebViewDelegateに実装したメソッドでは、URLのスキームを「file://」のみ許可して、それ以外のスキーム―例えば「http」や「https」を持つURLへのアクセスを禁止しています。これはカスタムURLスキーム方式と同じです。

Objective-C
- (BOOL)webView:(UIWebView *)webView
  shouldStartLoadWithRequest:(NSURLRequest *)request
    navigationType:(UIWebViewNavigationType)navigationType
{
  NSString *prefix = @"file://";
  NSString *urlStr = request.URL.absoluteString;
  if ([urlStr hasPrefix:prefix]) {
    // fileプロトコルの読み込みのみ許可する
    return YES;
  }

  return NO;
}
リスト11.18 iOSでfileプロトコル以外の読み込みを制御する

11.6.2 NSURLProtocolクラスを用いる

 iOSでは、個別のUIWebViewクラスに対して読み込むURLを制限できるUIWebView Delegateを用いる方法の他にも、NSURLProtocolクラスを用いてURLを制限する方法があります。NSURLProtocolクラスを用いると、どのWebViewに対しても共通の制限を加えることができます。

 NSURLProtocolクラスは、NSURLRequestクラスの読み込み処理を自由に拡張するための仕組みを提供しています。canInitWithRequest:メソッドでYESを返すことで、そのリクエストの読み込み処理をstartLoadingメソッドやstopLoadingメソッドで実装できます(リスト11.19)。

Objective-C
@interface MyURLProtocol : NSURLProtocol
@end

@implementation MyURLProtocol

+(BOOL)canInitWithRequest:(NSURLRequest *)request
{
  return [request.URL.scheme isEqualToString:@"http"]
    || [request.URL.scheme isEqualToString:@"https"];
}

- (void)startLoading
{
  NSLog(@"[DEBUG] startLoading: %@", self.request.URL);
}

- (void)stopLoading
{
  NSLog(@"[DEBUG] stopLoading");
}
@end
リスト11.19 iOSでNSURLProtocolクラスを使ってfileプロトコル以外の読み込みを制御する

 まず、NSURLProtocolクラスを継承したMyURLProtocolクラスを宣言します。ここでは、「http」や「https」のスキームを持つURLをブロックする処理をしています。startLoadingメソッド内では、読み込み処理を記述していないので、結果として「http」と「https」プロトコルを持つURLの読み込みは自動的にブロックされます。

 ここで宣言したMyURLProtocolクラスを有効にするには、リスト11.20のように、WebViewを読み込む前にNSURLProtocolクラスのregisterメソッドでMyURLProtocolクラスを追加します。

Objective-C
[NSURLProtocol registerClass:MyURLProtocol.class];
リスト11.20 iOSでMyURLProtocolクラスを有効にする

 次回は「JsAlert方式/ローカルHTTPサーバ方式」を説明します。

久保田光則(くぼたみつのり)

東京都在住。アシアル株式会社に所属するUI/UXデザイナー兼ソフトウェアエンジニア。社内では,HTML5ハイブリッドアプリの開発に多数関わる。優れたデザインとエンジニアリングを両立したオーバークオリティなアプリケーションの開発を実現するために日々,頑張る。

アシアル株式会社(あしあるかぶしきがいしゃ、Asial Corporation)

PHPなどのサーバサイドの技術と,PhoneGapなどのスマートフォン関連を中心とした開発を手がける技術ベンチャー。HTML5ハイブリッドアプリをブラウザ上で開発できるMonacaや,PhoneGapの日本語情報を配信するPhoneGap Fanなどのウェブサービスを手がける。

※以下では、本稿の前後を合わせて5回分(第6回~第10回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(9)
6. Android向けのCordovaプラグインを実装する

アプリの一部をネイティブで記述するには、プラグインの実装が必要。そこでAndroid向けにCordovaプラグインを実装する方法を解説。今回で「Cordovaアプリ開発の基礎」に関する部分の転載完了。

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(9)
7. JavaScriptからネイティブの機能を呼び出す方法 ― addJavascriptInterface方式(前編)

Apache Cordova/Adobe PhoneGapによるハイブリッドアプリ内での、JavaScriptとネイティブとの通信の仕組みを解説する。書籍転載の7本目(「Part 2《実践編》 第11章 JavaScriptとネイティブとのブリッジ」より)。

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(9)
8. addJavascriptInterface方式(後編)/ネイティブからJavaScriptへ値を渡す

Apache Cordova/Adobe PhoneGapによるハイブリッドアプリ内での、JavaScriptとネイティブとの通信の仕組みを解説。書籍転載の8本目。

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(9)
9. 【現在、表示中】≫ カスタムURLスキーム方式/iOSで特定のページの読み込みを制限する

JavaScriptからネイティブ側へ命令を投げる方法(Android&iOS対応)を紹介。またiOSでセキュリティのために、特定のドメインのページの読み込みを制限する方法も紹介。書籍転載の9本目。

書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(9)
10. JavaScriptとネイティブの通信の仕組み ― JsAlert方式/ローカルHTTPサーバ方式

ついに書籍転載最終回。Apache Cordova/Adobe PhoneGapによるハイブリッドアプリ内での、JavaScriptとネイティブの通信の仕組みの解説が完結する。

サイトからのお知らせ

Twitterでつぶやこう!