数据, 术→技巧, 研发

iOS中的设备唯一标识码

钱魏Way · · 1,254 次浏览
!文章内容如有错误或排版问题,请提交反馈,非常感谢!

iOS中的设备唯一标识

在iOS7之前,曾经有过很多获取设备唯一标识的方法。但是它们都先后被苹果禁止掉了。这些被禁止掉的包括UDID、Mac地址、OpenUDID。在iOS7之后,我们可以选择的唯一标识有IDFA、IDFV、DeviceToken、UUID四种方案。他们各有利弊,下面对他们进行对比。

IDFA

IDFA是苹果iOS6开始新增的广告标识符,用于给开发者跟踪广告效果用的,可以简单理解为iPhone的设备临时身份证,说是临时身份证是因为它允许用户更换。

IDFA是目前苹果生态广告交易的主要标识,一般跟广告商交易一个用户后广告商需要给你提供用户的IDFA作为凭证,主流的广告平台腾讯广点通、新浪粉丝通对账是基于IDFA的。

IDFA(广告标识符)可以通过如下代码来获取:

// AdSupport.framework
#import <AdSupport/AdSupport.h>
// 获取IDFA
NSString *IDFA = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];

示例:E1D2194B-6C91-4094-8F5B-F7CAE5C50989

有如下几种情况下IDFA可能被重置。

  • 重置系统(设置->通用->还原->还原位置与隐私)
  • 还原广告标识符(设置->隐私->广告->还原广告标识符)
  • iOS10以下打开限制广告追踪(设置->隐私->广告->限制广告追踪)

IDFA在使用时需要注意如下几种情况:

  • 在提交App的时候勾选对应的选项,不然可能遭到拒绝。
  • 如果程序在后台运行,此时用户还原广告标识符,这时回到程序中并不会立即获得还原后的标识符。必须重新启动程序,才能获得还原后的广告标识符。
  • 在iOS10以上打开限制广告追踪后,只能获取到0。

IDFA被淘汰

在iOS14中,广告主或广告网盟将无法继续使用IDFA,除非用户在每次下载新应用时同时向广告主app和广告网盟授予权限,允许其读取IDFA。从技术角度来看,IDFA并没有消失,只是我们预计用户选择授予权限的比率会非常低。我们基本上可以断言,IDFA几乎失去了作用。

如果用户选择不接受IDFA跟踪,广告主就无法投放个性化和再营销广告,同时也无法准确对营销活动进行归因和了解营销活动的ROI。

IDFV

IDFV是给Vendor标识用户用的,每个设备在所属同一个Vendor的应用里,都有相同的值。其中的Vendor是指应用提供商,准确的说,是通过BundleID的反转的前两部分进行匹配,如果相同就是同一个Vendor,例如对于com.somecompany.appone,com.somecompany.apptwo这两个BundleID来说,就属于同一个Vender,共享同一个idfv的值。和idfa不同的是,idfv的值是一定能取到的,所以非常适合于作为内部用户行为分析的主id,来标识用户,替代OpenUDID。

IDFV(Vendor标识符)可以通过如下代码来获取:

NSString *IDFV = [[[UIDevice currentDevice] identifierForVendor] UUIDString];

备注:

  • idfv的值是一定能取到
  • 同一台设备上,来自同一个供应商的App,IDFV相同。
  • 同一台设备上,来自不同厂商的App,IDFV不同。
  • 不同设备的IDFV不同,与供应商无关。换言之,即使同一个厂商的App,在不同设备上IDFV也不相同。
  • 当iOS设备上存在同一厂商的其他应用时,删除App重装,IDFV保持不变。
  • 删除iOS设备上同一厂商全部App,重新安装App,IDFV会发生改变。
  • 使用Xcode安装测试版本或ad-hoc包时,IDFV会发生改变。

IDFV只有在相同Vendor和相同设备里才具有唯一性。所以它不适合做不同Vender的App之前的唯一标识,但非常适合做相同Vender的App的唯一标识。不过,如果用户将属于此Vender的所有App卸载,IDFV的值也会被重置。

iOS APNS DeviceToken

DeviceToken是APNs用于区分识别每个iOS设备和设备上不同app的一个标识符,还可以用于APNs通过它将推送消息路由到指定设备上。

  • iOS6以前的系统,DeviceToken不随app变化:此时DeviceToken不含有bundleid信息,那是如何区分app的呢?是因为apns和ios设备通信时通过ios设备上的证书可以区分app,当然这种方案也有bug:可能不同app之间的推送会乱。
  • iOS7以后的系统,DeviceToken随app变化。此时apns推送到ios设备上的消息通过devicetoken解析出来的bundleid即可投递给指定app。也就说是从信息量上看:devicetoken=deviceid+bundleid。其中deviceid用于识别iOS设备,bundleid用于识别iOS设备上的app。
  • 在0和iOS8.0的系统中,卸载重装应用,deviceToken不会发生变化,但是iOS9.0(包括)以后卸载重装应用,deviceToken会发生变化。

DeviceToken的一些特性:

  • 开发环境获取的deviceToken和发布环境获取的deviceToken是不一样的
  • 在一台设备中,deviceToken是系统级别的,不同App获得的deviceToken是相同的
  • deviceToken会过期
  • 单个App的更新deviceToken不会发生改变
  • 当进行备份恢复、或恢复出厂设置之类的操作时,deviceToken会发生改变,建议App在每次启动时都获取deviceToken
  • 用户抹除iPhone的数据时,为了保护隐私,deviceToken会改变
  • 升级系统deviceToken有可能变化,猜测是升级大的系统版本后deviceToken会变化
  • 删除手机上的App之后,再次下载安装,deviceToken在0+系统中会改变

DeviceToken需要使应用拥有推送功能才能获取,所以单独为了唯一标识而开启推送是不值得的。

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSMutableString *deviceTokenString = [NSMutableString string];
    const char *bytes = (const char *)deviceToken.bytes;
    NSInteger count = deviceToken.length;
    for (int i = 0; i < count; i++) {
        [deviceTokenString appendFormat:@"%02x", bytes[i] & 0x000000FF];
    }
    NSLog(@"**发送给服务器的token字符串***:%@\n", deviceTokenString);
}

SKAdNetwork

什么是SKAdNetwork?

随着iOS14的发布,Apple推出了两个新的归因和广告监测框架。

  • 第一个是ATT(App Tracking Transparency)框架。在该框架下,要想访问用户的IDFA,必须先取得用户的许可。
  • 第二个是Apple在2018年推出的SKAdNetwork。这是一种不同的推广活动监测方法,在SKA框架中,用户层级的数据不可用。随着iOS14的推出,SKAdNetwork框架将进行版本更新。Apple这样做的目的是希望能减轻由于限制开发者的IDFA访问权限而造成的影响。

SKAdNetwork的重要性何在?

SKAdNetwork是接收iOS端营销推广活动归因数据的另一种方法。广告渠道必须向Apple注册,而开发者也要保证自己的应用与注册渠道及新框架兼容。但是,SKAdNetwork会对移动营销人员当前的移动监测方式带来不少挑战。

  • 按照当前的计划,SKAdNetwork会提供长度为6比特的下游指标,且有24小时的时间限制。每次用户触发应用内事件,该6比特指标就会发生变化,成为应用指定的全新6比特事件识别码,而时间限制也会延长24小时。事件窗口过期后,第二个24小时归因窗口就会开始计时。SKAdNetwork会在这24小时窗口内随机返回归因数据。SKAdNetwork系统分享的数据都采取聚合形式,不提供用户层级上的精细数据。
  • 事件识别码只能向上叠进。例如,用户在游戏内达成了等级1,应用为”等级1″创建的事件识别码为000001;然后用户购买了游戏内货币,这一事件的识别码为000011。如果用户随后达成了”等级2″,比特值不会变为000010,因为变化是单向的。要避免这个问题,开发者需要为排列组合中所有的可能性分配不同的比特值,而不是为每种事件分配比特值。SKAdNetwork跟踪的数据无法与MMP SDK跟踪的应用内事件精细数据关联起来。
  • SKAdNetwork框架下每个渠道仅显示100个不同的推广活动。乍一看上去,您或许觉得这并不是什么大问题。但这些推广活动之下,常常还有针对不同地区、设备类型或素材的无数子级推广活动。例如,如果您通过SKAdNetwork为五个不同的国家/地区使用十种不同的素材,那么在每个渠道就只能开展两个不同的推广活动。

SKAdNetwork还会有(最少)24至48小时的数据延迟,导致即时推广活动优化受阻,因此开发者和营销人员还要做出其他改变,才能妥善应对。此外,防广告欺诈领域也会发生变化。上文提到的事件转化值没有加密签名,也就是说,应用开发者无法使用现有的方法对事件进行验证,这给欺诈者留下了可乘之机。

当前,SKAdNetwork不支持深度链接(延迟和条件深度链接均不支持)和展示归因,且除了下载以外,不会将其他任何活动视为可归因活动。要满足当今营销人员的需要,SKAdNetwork当前的功能还需要升级,但对于想要使用SKAdNetwork的客户,我们也会提供全方位支持。

以下图表描述了安装验证的流程,AppA指展示广告的来源APP,AppB指用户安装的广告主APP。

请仔细看橘黄色的圆圈,这是这流程图的关键。SKAdNetwork是让广告平台在不获取IDFA的前提下对用户的点击和安装行为提供的一套追踪解决方案。这个层面的追踪已经跳开了设备这一个层面,直接内嵌在了广告平台和Apple Store之间,不再收集用户设备信息。

继续看橘黄色的圆圈,我们的目标是点击到安装都有一个身份证进行追踪是么?那么在SKAdNetwork的解决方案里面,Apple给每一次不同用户的点击行为注入了不同的标签(Ad Signature),这个标签一直伴随到安装结束。橘黄色的标签于广告平台(图二)生成(广告平台需要在Apple注册其平台)、用户点击行为(图三)以及用户安装行为(图四)。这样就撇开了设备层面的信息收集,从而变成了给每一次点击安装行为做了一个身份识别,从而完成追踪

你可以这么理解:

  • SKAdNetwork的模式就好比你去电影院看电影,从买票(点击)到入场(安装),这张票(Ad Signature)上没有任何你的个人信息,售票处就好比广告平台,只知道这票在什么卖出去的,多少钱卖出去的,哪个渠道卖出去的,是否观影,什么时候观影。但是售票系统不知道买的人具体的特征,比如有没有买爆米花,年龄,性别,是否带女朋友还是每次换个女朋友看电影等信息。
  • IDFA的模式就好比出境跟团游,你的护照、存款证明等(IDFA)被旅行社拿走,从而你的一切隐私可以被旅行社拿出来做各种促销,比如旅行社知道你是否消费了,消费了多少,年龄,性别,之前去过哪些国家等等,从而判断你属于什么类型的用户,进而推送更多的广告给你,无隐私可言。

SKAdNetwork的优势

我们还是看到了一些优势,比如App Store会帮你验证安装的有效性-这让那些欺诈的广告网络的作弊方式可以收敛一点。随着版本更新,未来我们应该会看到更多的优势。

SKAdNetwork现阶段的问题

在谈SKAdNetwork问题之前,我们先看下IDFA的优势:

  • 可以在广告平台内做“用户召回(Re-Targeting)”,
  • 可以追踪安装、注册、付费、留存等信息

但是SKAdNetwork的广告标签则没有这么细,只提供在点击到安装过程中的对应时间、对应来自于哪个广告组、具体哪个广告以及运营商网络信息。详

没有IDFA带来的问题还是蛮多的:比如最后一次点击和安装的匹配以及计算方式、网盟新的作弊方式的出炉、广告如何做针对留存和付费的优化,用户召回还怎么做等。

iCloud token

iCloud令牌,该属性的值是表示当前活动的iCloud帐户的唯一标记。您可以比较令牌以检测当前帐户是否与以前使用的帐户不同。如果用户在设备上启用飞行模式,则iCloud本身将无法访问,但当前的iCloud帐户仍保持登录状态。即使在飞行模式下,该ubiquityIdentityToken属性也包含当前iCloud帐户的令牌。如果用户退出iCloud,例如关闭“设置”中的“文档和数据”,则ubiquityIdentityToken属性的值将更改为nil。

NSFileManager *fileManager = [NSFileManager defaultManager];
id currentiCloudToken = fileManager.ubiquityIdentityToken;

奇巧淫技

仅对非越狱设备有效。不确定会一致有效。在系统不升级的情况下,ID唯一,与应用无关。

设备唯一标识使用到的技术

Keychain

Keychain Services是macOS和iOS都提供一种安全的存储敏感信息的工具。比如,网络密码(用户访问服务器或者网站),通用密码(用来保存应用程序或者数据库密码)。与此同时,用于认证的证书、密钥、和身份信息也可以存储在Keychain中。Keychain Services的安全机制保证了存储这些敏感信息不会被窃取。简单说来,Keychain就是一个安全容器。在iOS中keychian依赖用于签名的provisioning profile描述文件,确保发布不同版本的时候使用同一个文件。

苹果自己也用 KeyChain 来保存 Wi-Fi 密码,VPN 凭证等,实际上以一个数据库,路径在 /private/var/Keychains/keychain-2.db。

keyChain 是一个相对独立的空间,当程序替换,删除时并不会删除 keyChain 的内容,这个要比 Library/Cache 好。刷机,恢复出厂应该就没有了。我们把 keyChain 看做一个 Dictionary,所有数据都以 Key-Value 形式存储,可以对其进行 add, update, get, delete 四个操作,一个应用的 keyChain 都有两个访问区,私有区和公有区,私有区是一个个 sandbox, 本程序储存的对其他程序不可见,想将数据存在公有区,则需要声明公有区的名称,官方名称 “keychain access group”

KeyChain 的结构

每一个 KeyChain 由多个 KeyChain item 组成,KeyChain item 的结构类似字典,为 Key-Value,同时每条 KeyChain Item 还包含一条 data 和多个 attributes 组成。

比如一个银行就是一个 KeyChain,银行里可以有多个保险库,对应的就是 KeyChain Item,而每个保险库都有自己该存放的东西,比如现金,黄金等,这个就是 attributes,而存储的内容就是 data。

其中苹果提供了这些类型的 keychain item,并且对不同类型的 item 做了不同的处理,比如 password 和 key 类的 item 就会做加密,而 certificates 类的就不会。

上述唯一标识的方案都存在或多或少有被重置或者获取不到的可能。但是他们都可以与 Keychina 进行结合。Keychina 是独立于软件外可以用来存放账号密码的区域,它不会随着软件的卸载和系统重置而消失。Keychina 的特性结合 UUID 的特性已经能做到近乎完美的设备唯一标识。这也是现在大部分软件的做法。

备注:一键改机工具可清除 KeyChain 数据。

FCUUID

FCUUID是 iOS UUID/Universally Unique Identifiers 库,作为 UDID 和 identifierForVendor 的替代品。

FCUUID 该框架诞生于 2015 年 10 月左右,其实现原理就是 CFUUID + KeyChain,跟已经废弃的 OpenUDID 是差不多的,只是存储方式不同,OpenUDID 的原理是用 UIPasteboard 来保存 UUID。需要注意的是,FCUUID 框架依赖于框架 UICKeyChainStore。

FCUUID 提供的类方法:

// 每次运行应用都会变
+(NSString *)uuid;

// changes each time (no persistent), but allows to keep in memory more temporary uuids
+(NSString *)uuidForKey:(id<NSCopying>)key;

// 每次运行应用都会变
+(NSString *)uuidForSession;

// 重新安装的时候会变
+(NSString *)uuidForInstallation;

// 卸载后重装会变
+(NSString *)uuidForVendor;

// 抹掉 iPhone 的时候才会变,适合做唯一标识
+(NSString *)uuidForDevice;

NSUbiquitousKeyValueStore

iOS 开发中为了防止用户将 app 卸载,再安装的时候丢失数据,所以关于 apple 提供的沙盒本地存储外,还提供了云存储 iCloud。iCloud key-value 仅适用于保存小数据,如用户偏好、系统设置和一些简单的 App 状态。如果需要存储大文件、数据库数据等.

iCloud key-value 存储限制:

  • 每个 App 每个用户总共最多可存储 1MB 数据
  • key 的最大数量是 1024
  • 每个 key 的最大长度是 64Bytes
  • 每个 key 最大可以存储 1MB 的 value

如果你使用了一个 key 存储了一个 1MB 的 value,那么存储容量已经到达上限;如果你每个 key 的都存储 1kb 的数据,那么你可以使用 1000 个这样的 key 存储

特别注意 NSData:虽然 value 可以是 NSData 数据类型,但是因为空间的关系,苹果并不推荐存储该类型的数据。另外,每次你对 data 只是做了很小的改动,在传输过程中,依然会把整个 data 重新上传。推荐的做法是,在业务层面,将 data 尽可能分成多个 key 去存储,避免每次做多余的上传。

用法和 NSUserDefaults 一样,使用 setValue:ForKey: 方法存储,但是并不需要调用 synchronize 方法,调用了反而可能在特定情况下出错:

NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];

// 存储
[store setObject:@"testValue" forKey:@"testKey"];

// 读取
[store objectForKey:@"testKey"];

// 删除
[store removeObjectForKey:@"testKey"];

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注