概要
Flutterに広告を表示する機能をつけたが、トラッキング承認のダイアログを表示する便利なライブラリであるapp_tracking_transparencyがメンテナンスされていないようなので、 iOS/Swift側にNativeコードを書いて、MethodChannelで呼ぶのやってみました。
- 対象者
- Flutterでトラッキング機能を実装しようとしてる人
- iOSアプリの開発経験がある人
手順
Xcodeを開かなくもコードを書くことができますが、こちらはお好みで。
- AppDelegate.swiftを編集
import Flutter
import UIKit
import AppTrackingTransparency
import AdSupport
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
// MethodChannel for ATT
let controller = window?.rootViewController as! FlutterViewController
let attChannel = FlutterMethodChannel(name: "com.junichi.soloscan/att",
binaryMessenger: controller.binaryMessenger)
attChannel.setMethodCallHandler { [weak self] (call, result) in
switch call.method {
case "requestTracking":
self?.requestTrackingAuthorization(result: result)
case "getTrackingStatus":
self?.getTrackingStatus(result: result)
default:
result(FlutterMethodNotImplemented)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func requestTrackingAuthorization(result: @escaping FlutterResult) {
if #available(iOS 14, *) {
ATTrackingManager.requestTrackingAuthorization { status in
DispatchQueue.main.async {
result(self.statusToString(status))
}
}
} else {
// iOS 14未満は常に許可扱い
result("authorized")
}
}
private func getTrackingStatus(result: @escaping FlutterResult) {
if #available(iOS 14, *) {
let status = ATTrackingManager.trackingAuthorizationStatus
result(statusToString(status))
} else {
result("authorized")
}
}
@available(iOS 14, *)
private func statusToString(_ status: ATTrackingManager.AuthorizationStatus) -> String {
switch status {
case .notDetermined:
return "notDetermined"
case .restricted:
return "restricted"
case .denied:
return "denied"
case .authorized:
return "authorized"
@unknown default:
return "unknown"
}
}
}
- info.plistの下の方のの上あたりにこちらのコードを追加してください。
<key>NSUserTrackingUsageDescription</key>
<string>広告をパーソナライズするために、トラッキングの許可をお願いします。許可しない場合でもアプリは通常通りご利用いただけます。</string>
-
att_service.dartというファイルを作成します。MethodChannelを使用して、iOS/Swift側のロジックを呼び出して、アプリ起動時にトラッキングの承認ダイアログを表示できるようにします。
import 'package:flutter/services.dart';
/// App Tracking Transparency サービス
/// iOS 14以降で広告トラッキングの許可をリクエストする
class AttService {
static const _channel = MethodChannel('com.junichi.soloscan/att');
static final AttService _instance = AttService._internal();
factory AttService() => _instance;
AttService._internal();
/// トラッキング許可をリクエスト
/// 戻り値: 'authorized', 'denied', 'restricted', 'notDetermined', 'unknown'
Future<String> requestTracking() async {
try {
final result = await _channel.invokeMethod<String>('requestTracking');
return result ?? 'unknown';
} on PlatformException catch (_) {
return 'unknown';
}
}
/// 現在のトラッキング許可状態を取得
Future<String> getTrackingStatus() async {
try {
final result = await _channel.invokeMethod<String>('getTrackingStatus');
return result ?? 'unknown';
} on PlatformException catch (_) {
return 'unknown';
}
}
/// トラッキングが許可されているか
Future<bool> isTrackingAuthorized() async {
final status = await getTrackingStatus();
return status == 'authorized';
}
}
- アプリ起動時に起動するように、
main.dartで同じファイルということで、プライベートな関数を定義して、main関数の中で、実行します。いろんなコード混ざってますが、await _requestTrackingPermission();を呼び出して実行してあげれば大丈夫です。
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
await _initPurchases();
// ATTダイアログを表示(iOS 14以降)
await _requestTrackingPermission();
runApp(ProviderScope(
retry: (retryCount, error) {
// 5回以上はリトライしない
if (retryCount >= 5) return null;
// ネットワーク関連のエラーのみリトライ(指数バックオフ)
if (error is SocketException || error is TimeoutException) {
return Duration(milliseconds: 200 * (1 << retryCount));
}
return null;
},
child: const SoloScanApp(),
));
}
/// App Tracking Transparency のリクエスト
Future<void> _requestTrackingPermission() async {
if (!Platform.isIOS) return;
final attService = AttService();
final status = await attService.requestTracking();
debugPrint('ATT status: $status');
}
実行結果
最後に
今回は自作の実装をしてトラッキングの承認対応をしてみました。ライブラリは便利だがメンテナンスがしばらくされてないなんてことがあるので、こうなってくるとiOS/Swiftでコードを書いてAppleの広告への対策をする必要がありました。
ガイドラインあるのですが、果たして守れているのかも気になっていたり。。。。
なんせセンシティブな広告が表示されることもありますので😅