iOS Application の実装
サンプルコードを例に、idfaなどの受け渡し、そのほか実装上の注意事項に関して説明します。
- JavaScriptとの連携方法
- IDFAの取得
- Webページにデータ取得用のインターフェース作成
- Webページからidfa, appbundle, platformvを受け取る
- 広告リダイレクトの対応
- 動画の再生
JavaScriptとの連携方法
各種パラメーターの取得については公式リファレンスなどをご参照ください。
IDFAの取得
AppTrackingTransparency.framework(*1) および、AdSupport.framework(*2) を追加する必要があります。
(*1)iOS14以降で有効なIDFAが取得可能であるか判定するために必要です。
(*2)IDFAの取得のために必要です。
また、下記で後述しているサンプルコードでは、ATTrackingManagerAuthorizationStatusAuthorized のステータスにてIDFAを取得するように記述しております。
- Swift
- Objective-C
// IDFAを取得するためのサンプルコード
import AppTrackingTransparency
import AdSupport
~~省略~~
if #available(iOS 14, *) {
// iOS14以降ではAppTrackingTransparencyを利用してIDFA取得に関する判定をする
switch ATTrackingManager.trackingAuthorizationStatus {
case .authorized:
// IDFAの取得がユーザーにより許可されている
idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
case .denied:
// IDFAの取得がユーザーにより拒否されている
print("denied")
case .restricted:
// IDFAの取得がユーザーの設定により制限されている
print("restricted")
case .notDetermined:
// IDFAの取得に関するリクエストをまだユーザーが受け取っていない
print("notDetermined")
@unknown default:
fatalError()
}
} else {
// iOS13以前ではAppTrackingTransparencyを利用せずIDFA取得に関する判定をする
if (ASIdentifierManager.shared().isAdvertisingTrackingEnabled) {
idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
}
}
// IDFAを取得するためのサンプルコード
#import <AppTrackingTransparency/ATTrackingManager.h>
#import <AdSupport/AdSupport.h>
~~省略~~
NSString *idfa = @"";
if (@available(iOS 14, *)) {
// iOS14以降ではAppTrackingTransparencyを利用してIDFA取得に関する判定をする
switch (ATTrackingManager.trackingAuthorizationStatus) {
case ATTrackingManagerAuthorizationStatusAuthorized:
// IDFAの取得がユーザーにより許可されている
idfa = [ASIdentifierManager sharedManager].advertisingIdentifier.UUIDString;
break;
case ATTrackingManagerAuthorizationStatusDenied:
// IDFAの取得がユーザーにより拒否されている
break;
case ATTrackingManagerAuthorizationStatusRestricted:
// IDFAの取得がユーザーの設定により制限されている
break;
case ATTrackingManagerAuthorizationStatusNotDetermined:
// IDFAの取得に関するリクエストをまだユーザーが受け取っていない
break;
default:
break;
}
} else {
// iOS13以前ではAppTrackingTransparencyを利用せずIDFA取得に関する判定をする
if ([ASIdentifierManager sharedManager].isAdvertisingTrackingEnabled) {
idfa = [ASIdentifierManager sharedManager].advertisingIdentifier.UUIDString;
}
}
Webページにデータ取得用のインターフェース作成
JavaScriptの雛形を準備し、Web画面に読み込ませる。 そのJavaScriptを介して、データを受け渡します。
- Swift
- Objective-C
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
// ページの読み込み準備開始
updateUserScript()
}
// MARK: - HTMLの読み込み前に動作するスクリプトの作成
func updateUserScript() {
let script = createUserScript()
// 登録されているスクリプトを削除
userContentController.removeAllUserScripts()
// 新規のスクリプトを登録
userContentController.addUserScript(script)
}
func createUserScript() -> WKUserScript {
// bundleId
let bundleId = Bundle.main.bundleIdentifier ?? ""
// IDFA, ATTダイアログの表示は別に行う必要があります
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
// iOSバージョンを取得
let platformv = UIDevice.current.systemVersion
// js
let jsString = "window.adgAdParams = {idfa:'\(idfa)', appbundle:'\(bundleId)', platformv:'\(platformv)'};"
let userScript = WKUserScript(source: jsString, injectionTime: .atDocumentStart, forMainFrameOnly: true)
return userScript
}
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
//ページの読み込み準備開始
[self updateUserScript];
}
#pragma mark - HTMLの読み込み前に動作するスクリプトの作成
- (void) updateUserScript {
WKUserScript *script = [self createUserScript];
//登録されているスクリプトを削除
[self.userContentController removeAllUserScripts];
//新規のスクリプトを登録
[self.userContentController addUserScript:script];
}
- (WKUserScript *) createUserScript {
//bundleId
NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];
// IDFA, ATTダイアログの表示は別に行う必要があります
NSString *idfa = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
// iOSバージョンを取得
NSString *platformv = [[UIDevice currentDevice] systemVersion];
//js
NSString *jsStringTemplate = @"window.adgAdParams = {idfa:'%@', appbundle:'%@', platformv:'%@'};";
NSString *jsString = [NSString stringWithFormat:jsStringTemplate, idfa, bundleId, platformv];
WKUserScript *userScript = [[WKUserScript alloc]
initWithSource:jsString
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:YES];
return userScript;
}
Webページからidfa, appbundle, platformvを受け取る
const idfa = window?.adgAdParams?.idfa;
const appbundle = window?.adgAdParams?.appbundle;
const platformv = window?.adgAdParams?.platformv;
取得したadid,packageNameを利用し 広告タグにパラメータを生成してください。 下記サンプルでは、iOS又はAndroidからパラメータを受け取り、広告タグのパラメータに含めています。
<div id="adg_area"></div>
<script>
/*
idfa : iOSの場合この変数にidfaがセットされているとする(オプトアウト時除く)
aaid : Androidの場合この変数にAdvertising IDがセットされているとする(オプトアウト時除く)
appbundle : 実際にはこの変数にBundle IDかPackage Nameがセットされているとする。
platformv : iOS/Androidのバージョンがセットされているとする。
*/
(function() {
const div = document.getElementById("adg_area");
const script = document.createElement('script');
script.type = 'text/javascript';
let idfa = window?.adgAdParams?.idfa ?? "";
let advertising_id = window?.Android?.getAdvertisingId?.() ?? "";
let appbundle = window?.adgAdParams?.appbundle ?? window?.Android?.getAppBundle?.() ?? "";
let platformv = window?.adgAdParams?.platformv ?? window?.Android?.getPlatformV?.() ?? "";
// scriptのsrcにセットするテキストは実際に管理画面から取得されるタグのsrcと合わせてください。ただし、asyncは必ずtrueにしてください。
let srcText ='https://i.socdm.com/sdk/js/adg-script-loader.js?id=48547&targetID=adg_48547&displayid=1&adType=SP&async=true&tagver=2.0.0';
if(idfa.length > 0){
srcText += '&idfa=' + encodeURIComponent(idfa);
}
else if(advertising_id.length > 0){
srcText += '&advertising_id=' +encodeURIComponent(advertising_id);
}
if(appbundle.length > 0){
srcText += '&appbundle=' + encodeURIComponent(appbundle);
}
if(platformv.length > 0){
srcText += '&platformv=' + encodeURIComponent(platformv);
}
script.src =srcText;
if(div != null){
div.appendChild(script);
}
})();
</script>
広告リダイレクトの対応
配信される広告のタップ動作によって、外部のサイトへ移動したり AppleStoreのリンクなどを開けるようにする。 サンプルではドメインによって、外部サイトを判定しています。
- Swift
- Objective-C
func configureWebView() {
let configuration = WKWebViewConfiguration()
...(中略)...
// jsのコンテンツを差し込むことで、idfaやappbundleをjsから取得できるようにする
userContentController = WKUserContentController()
configuration.userContentController = userContentController
...(中略)...
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
// URL遷移時に呼ばれる
guard let url = navigationAction.request.url else {
decisionHandler(.allow)
return
}
let currentDomain = webView.url?.host
let targetDomain = url.host
if handleClick(url: url, currentDomain: currentDomain, targetDomain: targetDomain, navigationAction: navigationAction) {
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
// window.openの時などに呼ばれる
print("createWebViewWithConfiguration")
guard let url = navigationAction.request.url else {
return nil
}
let currentDomain = webView.url?.host
let targetDomain = url.host
if handleClick(url: url, currentDomain: currentDomain, targetDomain: targetDomain, navigationAction: navigationAction) {
print("URL opened in SFSafariViewController.")
}
return nil
}
func handleClick(url: URL, currentDomain: String?, targetDomain: String?, navigationAction: WKNavigationAction) -> Bool {
// http(s) 以外のプロトコル: 他のアプリへ移動する場合
if !url.absoluteString.hasPrefix("http://") && !url.absoluteString.hasPrefix("https://") && UIApplication.shared.canOpenURL(url) {
print("NOT HTTP/HTTPS : \(url.absoluteString)")
UIApplication.shared.open(url, options: [:], completionHandler: nil)
return true
}
guard let currentDomain = currentDomain, let targetDomain = targetDomain else {
return false
}
// hrefが指定された場合、新しいwindowで開こうとしている場合
// 現在のドメインとは別のドメインのページを開こうとしている場合
// 外部サイトへの遷移としてSFSafariViewで表示する。
if (navigationAction.navigationType == .linkActivated || navigationAction.targetFrame == nil) && currentDomain != targetDomain {
// SFSafariView
let safariViewController = SFSafariViewController(url: url)
present(safariViewController, animated: true, completion: nil)
return true
}
return false
}
- (void) configureWebView {
...(中略)...
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
...(中略)...
//jsのコンテンツを差し込むことで,idfaやappbundleをjsから取得できるようにする
self.userContentController = [[WKUserContentController alloc] init];
[configuration setUserContentController:self.userContentController];
self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
...(中略)...
}
- (void)webView:(WKWebView *)webView
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
//URL遷移時に呼ばれる
NSURL *url = navigationAction.request.URL;
NSLog(@"decidePolicyForNavigationAction:%@", url.absoluteString);
NSString *currentDomain = webView.URL.host;
NSString *targetDomain = navigationAction.request.URL.host;
// 外部のブラウザで閲覧するかどうかを決める
if ([self handleClick: url
currentDomain: currentDomain
targetDomain: targetDomain
navigationAction: navigationAction]) {
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
- (WKWebView *) webView:(WKWebView *)webView
createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
forNavigationAction:(WKNavigationAction *)navigationAction
windowFeatures:(WKWindowFeatures *)windowFeatures {
//window.openの時などに呼ばれる
NSLog(@"createWebViewWithConfiguration");
NSURL *url = navigationAction.request.URL; //読み込むURL
NSString *currentDomain = webView.URL.host; //現在表示しているドメイン
NSString *targetDomain = navigationAction.request.URL.host; //読み込む先のドメイン
if ([self handleClick: url
currentDomain: currentDomain
targetDomain: targetDomain
navigationAction: navigationAction]) {
NSLog(@"URL opened in SFSafariViewController.");
}
return nil;
}
- (BOOL) handleClick:(NSURL *)url
currentDomain:(NSString *)currentDomain
targetDomain:(NSString *)targetDomain
navigationAction:(WKNavigationAction *)navigationAction {
// http(s) 以外のプロトコル: 他のアプリへ移動する場合
if (![url.absoluteString hasPrefix:@"http://"]
&& ![url.absoluteString hasPrefix:@"https://"]
&& [[UIApplication sharedApplication] canOpenURL:url])
{
NSLog(@"NOT HTTP/HTTPS : %@", url.absoluteString);
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
return YES;
}
if (!url || !currentDomain || !targetDomain) {
return NO;
}
// hrefが指定された場合、新しいwindowで開こうとしている場合
if ((navigationAction.navigationType == WKNavigationTypeLinkActivated
|| !navigationAction.targetFrame)
// 現在のドメインとは別のドメインのページを開こうとしている場合
// 外部サイトへの遷移としてSFSafariViewで表示する。
// サイトに応じて適宜変更が必要
&& ![currentDomain isEqualToString: targetDomain]) {
// SFSafariView
SFSafariViewController *safariViewController =
[[SFSafariViewController alloc] initWithURL:url];
[self presentViewController:safariViewController animated:YES
completion:nil];
return YES;
}
return NO;
}
動画の再生
広告内に動画が流れることがあります。インライン・自動再生を許可してください。
- Swift
- Objective-C
let configuration = WKWebViewConfiguration()
// インライン再生を許可
configuration.allowsInlineMediaPlayback = true
// すべてのメディアについてユーザインタラクションなしの自動再生を許可
configuration.mediaTypesRequiringUserActionForPlayback = []
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
//インライン再生を許可
[configuration setAllowsInlineMediaPlayback:YES];
// すべてのメディアについてユーザインタラクションなしの自動再生を許可
[configuration setMediaTypesRequiringUserActionForPlayback:WKAudiovisualMediaTypeNone];