You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何获取iOS应用首次安装/下载日期以保留老用户免费权益?

搞定iOS应用首次安装日期的靠谱方案

嘿,我完全懂你的需求——要把现在免费的功能商业化,但得让老用户继续免费,核心就是要准确拿到用户首次安装/下载应用的时间对吧?你之前看到的用UserDefaults存首次启动日期的方法,确实有个坑:用户要是卸载重装,这个记录就没了,没法真正识别“老用户”。下面给你几个更稳妥的方案:

1. 用Keychain存,卸载不丢失

Keychain里的数据不会随应用卸载清空(除非用户重置设备或者手动删除所有应用数据),完美适合存首次安装时间:

  • 首次启动时,先检查Keychain里有没有firstInstallTimestamp这个键
  • 没有的话,就把当前日期存进去;有的话直接读取用就行
  • 给你一段Swift的示例代码:
import Security

func fetchFirstInstallDate() -> Date? {
    // 查询Keychain
    let query: [CFString: Any] = [
        kSecClass: kSecClassGenericPassword,
        kSecAttrAccount: "firstInstallTimestamp",
        kSecReturnData: kCFBooleanTrue!,
        kSecMatchLimit: kSecMatchLimitOne
    ]
    
    var result: AnyObject?
    let status = SecItemCopyMatching(query as CFDictionary, &result)
    
    switch status {
    case errSecSuccess:
        // 解析已存储的日期
        guard let data = result as? Data else { return nil }
        return try? JSONDecoder().decode(Date.self, from: data)
    case errSecItemNotFound:
        // 首次启动,存储当前日期
        let now = Date()
        guard let data = try? JSONEncoder().encode(now) else { return nil }
        let addQuery: [CFString: Any] = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: "firstInstallTimestamp",
            kSecValueData: data
        ]
        let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
        return addStatus == errSecSuccess ? now : nil
    default:
        return nil
    }
}

2. 解析App Store收据,拿官方首次购买日期

这是最准确的方法——通过验证App Store的应用收据,能拿到original_purchase_date字段,这个字段是用户首次下载(购买)应用的日期,哪怕卸载重装也不会变:

  • 先从App Store获取应用收据(注意区分测试环境和生产环境的处理逻辑)
  • 解析收据里的original_purchase_date,转换成Date格式
  • 优点是完全依赖苹果官方数据,绝对准确;缺点是需要处理收据验证的逻辑,而且离线状态下没法获取收据

3. 后端服务器记录,数据更可控

如果你的应用有自己的后端,可以在用户首次打开应用时,把设备的合规标识(比如IDFA,记得要符合隐私政策)和当前日期传到服务器存起来

  • 后续每次启动,先向服务器查询该设备对应的首次安装日期
  • 优点是数据完全在你掌控中,不受客户端操作影响;缺点是需要后端支持,还要注意隐私合规,别踩Apple的红线

小补充:UserDefaults的兜底用法

要是你想先用UserDefaults快速实现,也可以结合Keychain做兜底:先查UserDefaults,没有的话再查Keychain,都没有就记录当前日期并同时存在两个地方——这样既能覆盖普通场景,又能解决卸载重装的问题

内容的提问来源于stack exchange,提问作者nontomatic

火山引擎 最新活动