@@ -2,9 +2,12 @@ import Foundation
22
33enum UserDefaultsMigrator {
44 private static let sentinelKey = " didMigrateFromSandboxDefaults "
5+ private static let skippedKeyPrefixes = [ " NS " , " Apple " , " AV " , " SU " ]
56
6- static func migrateIfNeeded( ) {
7- let defaults = UserDefaults . standard
7+ static func migrateIfNeeded(
8+ defaults: UserDefaults = . standard,
9+ fileManager: FileManager = . default
10+ ) {
811 if defaults. bool ( forKey: sentinelKey) {
912 return
1013 }
@@ -14,53 +17,37 @@ enum UserDefaultsMigrator {
1417 return
1518 }
1619
17- let fileManager = FileManager . default
18- let containerPlist = fileManager. homeDirectoryForCurrentUser
20+ let containerPlistURL = fileManager. homeDirectoryForCurrentUser
1921 . appendingPathComponent ( " Library/Containers/ \( bundleId) /Data/Library/Preferences/ \( bundleId) .plist " )
20- let targetPlist = fileManager. homeDirectoryForCurrentUser
21- . appendingPathComponent ( " Library/Preferences/ \( bundleId) .plist " )
2222
23- guard fileManager. fileExists ( atPath: containerPlist . path) else {
23+ guard fileManager. fileExists ( atPath: containerPlistURL . path) else {
2424 defaults. set ( true , forKey: sentinelKey)
2525 return
2626 }
2727
28- do {
29- if fileManager. fileExists ( atPath: targetPlist. path) {
30- let backup = targetPlist. appendingPathExtension ( " preMigration " )
31- do {
32- try ? fileManager. removeItem ( at: backup)
33- try fileManager. moveItem ( at: targetPlist, to: backup)
34- } catch {
35- print ( " ⚠️ UserDefaultsMigrator: failed to back up existing defaults: \( error) " )
36- }
37- }
38-
39- if fileManager. fileExists ( atPath: targetPlist. path) {
40- try fileManager. removeItem ( at: targetPlist)
41- }
42-
43- try fileManager. copyItem ( at: containerPlist, to: targetPlist)
44- if let migratedDefaults = NSDictionary ( contentsOf: targetPlist) as? [ String : Any ] {
45- var mergedDefaults = defaults. persistentDomain ( forName: bundleId) ?? [ : ]
46- var didMerge = false
28+ guard let legacyDomain = NSDictionary ( contentsOf: containerPlistURL) as? [ String : Any ] , legacyDomain. isEmpty == false else {
29+ defaults. set ( true , forKey: sentinelKey)
30+ return
31+ }
4732
48- for ( key, value ) in migratedDefaults where mergedDefaults [ key ] == nil {
49- mergedDefaults [ key ] = value
50- didMerge = true
51- }
33+ let filteredLegacy = legacyDomain . filter { key, _ in
34+ guard key != sentinelKey else { return false }
35+ return skippedKeyPrefixes . contains { prefix in key . hasPrefix ( prefix ) } == false
36+ }
5237
53- if didMerge {
54- // Hydrate missing keys so callers see the migrated values without clobbering newer writes.
55- defaults. setPersistentDomain ( mergedDefaults, forName: bundleId)
56- }
57- } else {
58- print ( " ⚠️ UserDefaultsMigrator: failed to hydrate migrated defaults from \( targetPlist. path) " )
59- }
38+ if filteredLegacy. isEmpty {
6039 defaults. set ( true , forKey: sentinelKey)
61- print ( " UserDefaultsMigrator: copied sandbox defaults to \( targetPlist. path) " )
62- } catch {
63- print ( " ⚠️ UserDefaultsMigrator: failed to migrate defaults: \( error) " )
40+ return
41+ }
42+
43+ var mergedDomain = defaults. persistentDomain ( forName: bundleId) ?? [ : ]
44+ for (key, value) in filteredLegacy {
45+ mergedDomain [ key] = value
6446 }
47+
48+ defaults. setPersistentDomain ( mergedDomain, forName: bundleId)
49+ defaults. set ( true , forKey: sentinelKey)
50+
51+ print ( " UserDefaultsMigrator: migrated \( filteredLegacy. count) keys from sandbox defaults " )
6552 }
6653}
0 commit comments