iOS/Swift/SwiftUI如何仅对旧版升级老用户执行指定代码
实现方案
核心判断逻辑基于本地非同步持久化标记+版本追踪,同时兼容已上线无预埋标记的1.0存量版本,不会误判重装用户、跨设备同步用户。
避坑前置说明
以下方案误判率极高,不建议使用:
- 不要用Keychain存储判断标记:用户删除App重装时,Keychain数据可能不会被系统清空,会将重装用户误判为升级老用户
- 不要用开启iCloud同步的存储区存标记:同Apple ID下的多设备会同步标记,导致新设备全新安装被误判
- 不要只依赖首次启动判断:不做版本号匹配的话,后续大版本升级会重复触发逻辑
具体实现步骤
- 第一步:选定存储位置
用系统默认的UserDefaults(本地域,不要手动开启iCloud同步)或者沙盒Application Support目录下的本地配置文件存标记即可,这类数据的特性是:App删除后会被系统完全清空,不会残留,刚好匹配判断需求。 - 第二步:兼容无预埋的1.0版本
因为已经上线的1.0版本没有提前写入任何版本标记,需要做一层兜底判断:- 如果你1.0版本运行时会在本地写入固定业务数据(比如登录态、本地缓存、自定义配置文件),直接检查这类数据是否存在即可,存在就是1.0升级的老用户
- 如果1.0版本没有写入任何业务数据,就用沙盒目录创建时间差判断:升级安装时系统会替换App的bundle文件,但会保留原沙盒的
Documents/Library目录,这两个目录的创建时间是用户最初安装1.0的时间,远早于2.0版本bundle的创建时间;全新安装时沙盒目录和bundle是安装过程中连续创建的,时间差在10秒以内,设置60秒的阈值就能100%区分两类用户。
- 第三步:版本标记与执行防重
判断完成后立刻做两个持久化写入:- 写入当前App的版本号作为
kAppLastLaunchedVersion,供后续版本升级判断使用 - 如果判定为1.0升级用户,额外写入
kHasRunV2SpecialLogic的布尔标记,确保这段特殊逻辑在用户整个App生命周期里只执行一次,不会因为后续版本升级、重复启动重复触发。
- 写入当前App的版本号作为
代码示例
// 建议在application:didFinishLaunchingWithOptions:最靠前的位置调用,不要放在异步队列 - (BOOL)shouldRunV2ExclusiveLogic { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // 已经执行过特殊逻辑,直接返回NO if ([defaults boolForKey:@"kHasRunV2SpecialLogic"]) { return NO; } NSString *currentVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]; NSString *lastLaunchedVersion = [defaults stringForKey:@"kAppLastLaunchedVersion"]; // 之前已经写入过版本标记,直接对比版本 if (lastLaunchedVersion) { // 判断之前的版本是否低于2.0 BOOL isUpgradeFromV1 = [lastLaunchedVersion compare:@"2.0" options:NSNumericSearch] == NSOrderedAscending; [defaults setObject:currentVersion forKey:@"kAppLastLaunchedVersion"]; if (isUpgradeFromV1) { [defaults setBool:YES forKey:@"kHasRunV2SpecialLogic"]; } [defaults synchronize]; return isUpgradeFromV1; } // 没有历史版本标记,走兜底判断(针对1.0版本无预埋的场景) NSFileManager *fileManager = [NSFileManager defaultManager]; // 获取当前App bundle的创建时间,即本次安装/升级到2.0的时间 NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; NSDate *bundleCreateDate = [[fileManager attributesOfItemAtPath:bundlePath error:nil] fileCreationDate]; // 获取Documents目录创建时间 NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSDate *docCreateDate = [[fileManager attributesOfItemAtPath:docPath error:nil] fileCreationDate]; // 时间差大于60秒说明Documents目录远早于2.0安装时间创建,是1.0升级用户 NSTimeInterval timeGap = [bundleCreateDate timeIntervalSinceDate:docCreateDate]; BOOL isV1User = timeGap > 60; // 写入标记 [defaults setObject:currentVersion forKey:@"kAppLastLaunchedVersion"]; if (isV1User) { [defaults setBool:YES forKey:@"kHasRunV2SpecialLogic"]; } [defaults synchronize]; return isV1User; }
调用时直接匹配原有逻辑即可:
if ([self shouldRunV2ExclusiveLogic]) { // 执行仅针对1.0升级用户的特殊代码 }
内容的提问来源于stack exchange,提问作者fs_tigre
相关产品推荐
相关产品推荐

