FANCOMI Ad-Tech Blog

株式会社ファンコミュニケーションズ nend・新規事業のエンジニア・技術ブログ

iOSアプリでSDKを使わないでバナーを表示(WKWebView編)

こんにちは。
nendSDK担当のt_kinamiです。

だいぶ前に"iOSアプリでnendSDKが使えなくてもnend広告を表示する方法"という記事がありました。
本来はSDKを使って広告を表示していただきたいところですが、
現在もなんらかの理由によりSDKを導入できないが、広告表示によるパフォーマンス低下を軽減したいという方向けに、
今更ですが、WKWebViewクラスを使った、バナーの表示方法について書いてみます。


WKWebViewとは?

UIWebViewに変わる新しいクラスです。
UIWebView同様に、webコンテンツを表示できます。
JavaScriptのパフォーマンスが向上するそうです。
https://developer.apple.com/videos/play/wwdc2014/206/

制限等は?

  • iOS8以降でのみ動作します

iOS8以上のシェアは97%ですので、よっぽどの理由がない限りWKWebViewの利用をお勧めします。(2016/08 執筆時現在)
https://developer.apple.com/support/app-store/

  • Interface Builderに対応していないので、Viewの設置をコードで行う必要があります。

WKWebViewを使って欲しいと言っているのに、Interface Builder では UIWebView しか設置することができません。
appleさんの対応をお待ちください。

性能アップする?

以下の内容が向上します。

  1. ページ描画速度
  2. 安定性アップ
  3. メモリ使用量ダウン

今回は、3.メモリ使用量ダウンを主な目的としています。

nendの実装方法について

新規に作成したプロジェクトでバナー広告を表示するまでの手順を説明します。

frameworkの追加

準備段階その1
WKWebViewを使用するため、WebKit.framework をプロジェクトに追加します。

f:id:fan_t_kinami:20160808120143p:plain

広告コード(JavaScript)を記述するHTMLファイルをプロジェクトに追加

準備段階その2
過去の記事で紹介したHTMLでは、WKWebViewでは表示が崩れてしまいます。
以下の内容でHTMLファイルを作成してプロジェクトに追加します。

<html style="-webkit-touch-callout: none;">
    <head>
        <meta http-equiv="Content-Type"content="text/html; charset=Shift_JIS" >
        <meta name="viewport" content="width=device-width,initial-scale=1.0" >
    </head>
    <body style="width:320px;height:50px;margin:0px;background-color:#000000" >
        <!--// 下記はサンプル用IDです。アプリごとの「広告コード」を管理画面から入手してください -->
        <!--// ここから -->
        <script type="text/javascript">
            var nend_params = {"media":82,"site":2425,"spot":3172,"type":1,"oriented":2};
        </script>
        <script type="text/javascript" src="https://js1.nend.net/js/nendAdLoader.js"></script>
        <!--// ここまで -->
    </body>
</html>

広告を表示するコードを実装

広告を表示するためのコードを実装していきます。

HTMLファイルを/tmpディレクトリにコピーする

準備段階その3
iOS8ではWKWebViewはメインバンドルの直下に置かれたファイルにアクセスできません。
そのため、アプリ起動時に /tmp ディレクトリにHTMLファイルをコピーする処理を追加します。

(例:AppDelegate.m)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    // html ファイルが /tmp ディレクトリに存在するかをチェック
    NSFileManager* fm = [NSFileManager defaultManager];
    NSString *htmlPath = [NSTemporaryDirectory() stringByAppendingString:@"nendAd.html"];
    if (![fm fileExistsAtPath:htmlPath]) {
        // html ファイルのコピーを作成する
        [fm copyItemAtPath:[[NSBundle mainBundle] pathForResource:@"nendAd"ofType:@"html"] toPath:htmlPath error:nil];
    }
    return YES;
}
広告を表示する処理を実装する

ここからが実際にバナー広告を表示する処理となります。

ヘッダーファイルと実装ファイルに処理を実装していきます

(例:ViewController.h - ヘッダーファイル)

#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>

@interface ViewController : UIViewController <WKNavigationDelegate>

@end

(例:ViewController.m - 実装ファイル)

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic,retain) WKWebView* nendWebView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    // ここから:広告用WebViewを配置
    self.nendWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
    self.nendWebView.navigationDelegate = self;
    self.nendWebView.center = self.view.center;
    
    // UIに関する各種設定
    self.nendWebView.opaque = NO;
    self.nendWebView.backgroundColor = [UIColor clearColor];
    self.nendWebView.scrollView.bounces = NO;
    
    // ローカルのhtmlを読み込む
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingString:@"nendAd.html"];
    NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]];
    [self.nendWebView loadRequest:req];
    
    [self.view addSubview:self.nendWebView];
    // ここまで
}

#pragma mark - WKNavigationDelegate Request開始時
-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
        // クリック時はsafariを起動する
        if ([[UIApplication sharedApplication] canOpenURL:navigationAction.request.URL]) {
            // Browser open.
            [[UIApplication sharedApplication] openURL:navigationAction.request.URL];
        }
        decisionHandler(WKNavigationActionPolicyCancel);
    } else {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}
@end

ここまで実装したら、シミュレーター等で動作確認してみます。

以下のように表示できれば、実装完了です。
f:id:fan_t_kinami:20160808123313p:plain

で、性能は上がったの?

どのくらい性能が上がったのか、描画速度は比較しづらいのでメモリ使用量で比較してみます。

まずは、UIWebViewを使用した方法で広告を表示した際のメモリ使用量です。
f:id:fan_t_kinami:20160808123957p:plain
続いて、WKWebViewを使用した際のメモリ使用量です。
f:id:fan_t_kinami:20160808124025p:plain


いかがでしょうか、アプリのメモリ使用量は削減できています。

広告に限らず、WKWebViewへの移行の手助けになればと思います。


おまけ Swiftでの実装コード

余力があったので、Swiftでのコードも書いてみます。
準備段階その2までは Objective-c, Swift 共通です。

HTMLファイルを/tmpディレクトリにコピーする

(例:AppDelegate.swift)

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        // html ファイルが /tmp ディレクトリに存在するかをチェック
        let fm = NSFileManager.defaultManager()
        let htmlPath = NSTemporaryDirectory().stringByAppendingString("nendAd.html")
        
        if !fm.fileExistsAtPath(htmlPath) {
            try! fm.copyItemAtPath(NSBundle.mainBundle().pathForResource("nendAd", ofType: "html")!, toPath: htmlPath)
        }
        return true
    }
 (...省略...)
}
広告を表示する処理を実装する

(例:ViewController.swift)

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {
    var nendWebView: WKWebView!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        // ここから:広告用WebViewを配置
        self.nendWebView = WKWebView(frame: CGRect(x: 0, y: 0, width: 320, height: 50))
        self.nendWebView.navigationDelegate = self
        self.nendWebView.center = self.view.center
        
        // UIに関する各種設定
        self.nendWebView.opaque = false
        self.nendWebView.backgroundColor = UIColor.clearColor()
        self.nendWebView.scrollView.bounces = false
        
        // ローカルのhtmlを読み込む
        let filePath = NSTemporaryDirectory().stringByAppendingString("nendAd.html")
        let req = NSURLRequest.init(URL: NSURL(fileURLWithPath: filePath))
        self.nendWebView.loadRequest(req)
        
        self.view.addSubview(self.nendWebView)
        // ここまで
    }

    func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
        if navigationAction.navigationType == WKNavigationType.LinkActivated {
            // クリック時はsafariを起動する
            if UIApplication.sharedApplication().canOpenURL(navigationAction.request.URL!) {
                // Browser open.
                UIApplication.sharedApplication().openURL(navigationAction.request.URL!)
            }
            decisionHandler(WKNavigationActionPolicy.Cancel)
        }
        else {
            decisionHandler(WKNavigationActionPolicy.Allow)
        }
    }
}