WebView中的同意:保持原生与网页同意同步
双重横幅问题
许多应用一部分是原生的,一部分是网页的。游戏在WebView中显示其商店、新闻或帮助页面;工具类应用渲染来自其网站的文章;混合应用则把大部分界面包裹在网页内容中。双方各有自己的同意机制:原生应用使用应用内CMP或SDK,网页则加载网页CMP。除非二者相互连接,否则用户会被询问两次,在一处做出的选择在另一处被忽略,而来自WebView内部的广告请求则带着错误的信号发出。
本指南介绍如何让原生与网页的同意保持一致,使用户只需回答一次,每个广告请求都携带相同的决定。
双方分别在哪里存储同意
原生
IAB TCF规定了应用内CMP如何存储同意:在iOS上存于NSUserDefaults,在Android上存于SharedPreferences,使用IABTCF_TCString、IABTCF_gdprApplies和IABTCF_PurposeConsents等标准键。广告SDK直接读取这些键。IAB GPP定义了带有IABGPP_前缀的类似键,例如IABGPP_HDR_GppString。
网页
网页CMP通常把决定存储在网站域名下的第一方cookie或本地存储中,并通过__tcfapi和__gpp这两个JavaScript函数对外提供。页面中的广告标签调用的是这些函数,而不是原生存储。
差距
WebView拥有自己的cookie和存储空间,与设备浏览器分开,也与应用的原生偏好设置分开。在iOS上,使用非持久数据存储的WKWebView在关闭时会忘记一切。除非你自己搭建,否则双方之间不会有任何数据流动。
选定唯一可信来源
首先要决定由哪一方拥有同意。对大多数应用而言,答案是原生CMP,因为:
- 它先运行,早于任何WebView被打开。
- 通常贡献大部分收入的原生广告SDK读取的是原生存储。
- 在iOS上,它可以与App Tracking Transparency提示协调配合。
对于整个界面都是网页内容的混合应用,网页优先可能说得通,但那样你必须把决定写回原生存储,供原生SDK使用。
将原生同意传入WebView
以原生为可信来源时,WebView需要看到相同的决定。常见的做法有:
- 注入一个
__tcfapi实现。在页面脚本运行之前,注入一小段脚本,定义__tcfapi(如有需要还有__gpp),并用从原生存储读取的TC string和各项值回应调用。这样页面中的广告标签就能通过标准API获取原生决定。 - 抑制网页横幅。配置网页CMP,使其在页面于你的应用内加载时保持隐藏,例如基于查询参数,或你自己控制的自定义用户代理标记。否则用户会看到第二个横幅。
- 在加载时传递字符串。一些网页CMP可以在初始化时接收现有的同意字符串并采用它。请查阅你的网页CMP文档,了解支持的方法。
无论采用哪种做法,都要确保当用户在原生设置界面中更改选择时,注入的值也随之更新,而不只是在下次启动应用时更新。
将网页同意传回原生
如果用户可以在WebView内更改同意,例如通过隐私设置页面,应用就必须得知此事。使用JavaScript桥:iOS上是消息处理器,Android上是JavaScript接口。网页端通过__tcfapi('addEventListener', ...)监听,并把新字符串发送给原生代码,由原生代码写入标准键。写入前要验证来自WebView的输入;只接受来自你自己内容的消息。
WebView内的广告
在WebView中渲染的广告属于网页广告。它们从页面而不是原生存储读取同意,除非广告SDK提供了关联二者的方法。例如,Google的Mobile Ads SDK提供了一个API,用于注册WebView,使其中的广告能够使用来自应用的信号;请查阅你所用SDK的最新文档。无论如何,都要通过测试确认来自WebView的广告请求携带的TC或GPP字符串与原生广告请求相同。
测试清单
- 全新安装:只出现一次同意提示,而不是两次。
- 在原生CMP中全部拒绝,打开WebView,检查广告请求:字符串应当反映该拒绝。
- 在原生设置中更改同意,返回已打开的WebView:页面应当看到新的决定。
- 如果存在网页设置,在那里更改同意,并确认原生键已更新。
- 终止并重新启动应用:双方的决定都得以保留。
- 在iOS和Android上都进行测试,因为WebView存储在两者上的表现不同。
核心要点
- 原生同意与WebView同意存放在不同的存储中,默认不会同步。
- 选定唯一可信来源;对大多数应用来说是原生CMP。
- 通过注入的
__tcfapi或__gpp向网页内容提供原生同意,并隐藏网页横幅。 - 使用JavaScript桥把网页端的更改传回原生存储。
- 检查来自WebView的广告请求携带的同意字符串与原生请求相同。