Skip to content

Commit 2ffb097

Browse files
committed
Add a data migrator class
1 parent 6325abf commit 2ffb097

File tree

3 files changed

+376
-0
lines changed

3 files changed

+376
-0
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
final class DataMigrator {
2+
3+
private let coreDataStack: CoreDataStack
4+
private let backupLocation: URL?
5+
private let keychainUtils: KeychainUtils
6+
private let localDefaults: UserDefaults
7+
private let sharedDefaults: UserDefaults?
8+
9+
init(coreDataStack: CoreDataStack = ContextManager.sharedInstance(),
10+
backupLocation: URL? = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.org.wordpress")?.appendingPathComponent("WordPress.sqlite"),
11+
keychainUtils: KeychainUtils = KeychainUtils(),
12+
localDefaults: UserDefaults = UserDefaults.standard,
13+
sharedDefaults: UserDefaults? = UserDefaults(suiteName: WPAppGroupName)) {
14+
self.coreDataStack = coreDataStack
15+
self.backupLocation = backupLocation
16+
self.keychainUtils = keychainUtils
17+
self.localDefaults = localDefaults
18+
self.sharedDefaults = sharedDefaults
19+
}
20+
21+
enum DataMigratorError: Error {
22+
case localDraftsNotSynced
23+
case databaseCopyError
24+
case keychainError
25+
case sharedUserDefaultsNil
26+
}
27+
28+
func exportData(completion: ((Result<Void, DataMigratorError>) -> Void)? = nil) {
29+
guard isLocalDraftsSynced() else {
30+
completion?(.failure(.localDraftsNotSynced))
31+
return
32+
}
33+
guard let backupLocation, copyDatabase(to: backupLocation) else {
34+
completion?(.failure(.databaseCopyError))
35+
return
36+
}
37+
guard copyKeychain(from: nil, to: WPAppKeychainAccessGroup) else {
38+
completion?(.failure(.keychainError))
39+
return
40+
}
41+
guard copyUserDefaults(from: localDefaults, to: sharedDefaults) else {
42+
completion?(.failure(.sharedUserDefaultsNil))
43+
return
44+
}
45+
BloggingRemindersScheduler.handleRemindersMigration()
46+
completion?(.success(()))
47+
}
48+
49+
func importData(completion: ((Result<Void, DataMigratorError>) -> Void)? = nil) {
50+
guard let backupLocation, restoreDatabase(from: backupLocation) else {
51+
completion?(.failure(.databaseCopyError))
52+
return
53+
}
54+
guard copyKeychain(from: WPAppKeychainAccessGroup, to: nil) else {
55+
completion?(.failure(.keychainError))
56+
return
57+
}
58+
guard copyUserDefaults(from: sharedDefaults, to: localDefaults) else {
59+
completion?(.failure(.sharedUserDefaultsNil))
60+
return
61+
}
62+
BloggingRemindersScheduler.handleRemindersMigration()
63+
completion?(.success(()))
64+
}
65+
66+
}
67+
68+
// MARK: - Private Functions
69+
70+
private extension DataMigrator {
71+
72+
func isLocalDraftsSynced() -> Bool {
73+
let fetchRequest = NSFetchRequest<Post>(entityName: String(describing: Post.self))
74+
fetchRequest.predicate = NSPredicate(format: "status = %@ && (remoteStatusNumber = %@ || remoteStatusNumber = %@ || remoteStatusNumber = %@ || remoteStatusNumber = %@)",
75+
BasePost.Status.draft.rawValue,
76+
NSNumber(value: AbstractPostRemoteStatus.pushing.rawValue),
77+
NSNumber(value: AbstractPostRemoteStatus.failed.rawValue),
78+
NSNumber(value: AbstractPostRemoteStatus.local.rawValue),
79+
NSNumber(value: AbstractPostRemoteStatus.pushingMedia.rawValue))
80+
guard let count = try? coreDataStack.mainContext.count(for: fetchRequest) else {
81+
return false
82+
}
83+
84+
return count == 0
85+
}
86+
87+
func copyDatabase(to destination: URL) -> Bool {
88+
do {
89+
try coreDataStack.createStoreCopy(to: destination)
90+
} catch {
91+
DDLogError("Error copying database: \(error)")
92+
return false
93+
}
94+
return true
95+
}
96+
97+
func restoreDatabase(from source: URL) -> Bool {
98+
do {
99+
try coreDataStack.restoreStoreCopy(from: source)
100+
} catch {
101+
DDLogError("Error restoring database: \(error)")
102+
return false
103+
}
104+
return true
105+
}
106+
107+
func copyKeychain(from sourceAccessGroup: String?, to destinationAccessGroup: String?) -> Bool {
108+
do {
109+
try keychainUtils.copyKeychain(from: sourceAccessGroup, to: destinationAccessGroup)
110+
} catch {
111+
DDLogError("Error copying keychain: \(error)")
112+
return false
113+
}
114+
115+
return true
116+
}
117+
118+
func copyUserDefaults(from source: UserDefaults?, to destination: UserDefaults?) -> Bool {
119+
guard let source, let destination else {
120+
return false
121+
}
122+
let data = source.dictionaryRepresentation()
123+
for (key, value) in data {
124+
destination.set(value, forKey: key)
125+
}
126+
127+
return true
128+
}
129+
}

WordPress/WordPress.xcodeproj/project.pbxproj

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,6 +1798,9 @@
17981798
8320BDE6283D9359009DF2DE /* BlogService+BloggingPrompts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8320BDE4283D9359009DF2DE /* BlogService+BloggingPrompts.swift */; };
17991799
8323789828526E6D003F4443 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA25F9FD2609AA830005E08F /* AppConfiguration.swift */; };
18001800
8323789928526E6E003F4443 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA25F9FD2609AA830005E08F /* AppConfiguration.swift */; };
1801+
8332DD2429259AE300802F7D /* DataMigrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8332DD2329259AE300802F7D /* DataMigrator.swift */; };
1802+
8332DD2529259AE300802F7D /* DataMigrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8332DD2329259AE300802F7D /* DataMigrator.swift */; };
1803+
8332DD2829259BEB00802F7D /* DataMigratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8332DD2729259BEB00802F7D /* DataMigratorTests.swift */; };
18011804
834CE7341256D0DE0046A4A3 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 834CE7331256D0DE0046A4A3 /* CFNetwork.framework */; };
18021805
8350E49611D2C71E00A7B073 /* Media.m in Sources */ = {isa = PBXBuildFile; fileRef = 8350E49511D2C71E00A7B073 /* Media.m */; };
18031806
8355D7D911D260AA00A61362 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8355D7D811D260AA00A61362 /* CoreData.framework */; };
@@ -6862,6 +6865,8 @@
68626865
83043E54126FA31400EC9953 /* MessageUI.framework */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; };
68636866
830A58D72793AB4400CDE94F /* LoginEpilogueAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginEpilogueAnimator.swift; sourceTree = "<group>"; };
68646867
8320BDE4283D9359009DF2DE /* BlogService+BloggingPrompts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BlogService+BloggingPrompts.swift"; sourceTree = "<group>"; };
6868+
8332DD2329259AE300802F7D /* DataMigrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataMigrator.swift; sourceTree = "<group>"; };
6869+
8332DD2729259BEB00802F7D /* DataMigratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataMigratorTests.swift; sourceTree = "<group>"; };
68656870
833AF259114575A50016DE8F /* PostAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostAnnotation.h; sourceTree = "<group>"; };
68666871
833AF25A114575A50016DE8F /* PostAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PostAnnotation.m; sourceTree = "<group>"; };
68676872
834CE7331256D0DE0046A4A3 /* CFNetwork.framework */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
@@ -12327,6 +12332,7 @@
1232712332
803DE81D29063689007D4E9C /* Jetpack */ = {
1232812333
isa = PBXGroup;
1232912334
children = (
12335+
8332DD2629259B9700802F7D /* Utility */,
1233012336
803DE81E290636A4007D4E9C /* JetpackFeaturesRemovalCoordinatorTests.swift */,
1233112337
);
1233212338
name = Jetpack;
@@ -12446,6 +12452,22 @@
1244612452
path = Cells;
1244712453
sourceTree = "<group>";
1244812454
};
12455+
8332DD2229259ABA00802F7D /* Utility */ = {
12456+
isa = PBXGroup;
12457+
children = (
12458+
8332DD2329259AE300802F7D /* DataMigrator.swift */,
12459+
);
12460+
path = Utility;
12461+
sourceTree = "<group>";
12462+
};
12463+
8332DD2629259B9700802F7D /* Utility */ = {
12464+
isa = PBXGroup;
12465+
children = (
12466+
8332DD2729259BEB00802F7D /* DataMigratorTests.swift */,
12467+
);
12468+
name = Utility;
12469+
sourceTree = "<group>";
12470+
};
1244912471
850BD4531922F95C0032F3AD /* Networking */ = {
1245012472
isa = PBXGroup;
1245112473
children = (
@@ -14974,6 +14996,7 @@
1497414996
C72A52CD2649B14B009CA633 /* System */,
1497514997
C7124E4B2638527D00929318 /* NUX */,
1497614998
C7F7ABD5261CED7A00CE547F /* JetpackAuthenticationManager.swift */,
14999+
8332DD2229259ABA00802F7D /* Utility */,
1497715000
C7F7AC72261CF1C900CE547F /* ViewRelated */,
1497815001
);
1497915002
path = Classes;
@@ -21304,6 +21327,7 @@
2130421327
57AA848F228715DA00D3C2A2 /* PostCardCell.swift in Sources */,
2130521328
FE43DAAF26DFAD1C00CFF595 /* CommentContentTableViewCell.swift in Sources */,
2130621329
8F22804451E5812433733348 /* TimeZoneSearchHeaderView.swift in Sources */,
21330+
8332DD2429259AE300802F7D /* DataMigrator.swift in Sources */,
2130721331
8F228F2923045666AE456D2C /* TimeZoneSelectorViewController.swift in Sources */,
2130821332
);
2130921333
runOnlyForDeploymentPostprocessing = 0;
@@ -21950,6 +21974,7 @@
2195021974
80EF92932810FA5A0064A971 /* QuickStartFactoryTests.swift in Sources */,
2195121975
D848CBFF20FF010F00A9038F /* FormattableCommentContentTests.swift in Sources */,
2195221976
9123471B221449E200BD9F97 /* GutenbergInformativeDialogTests.swift in Sources */,
21977+
8332DD2829259BEB00802F7D /* DataMigratorTests.swift in Sources */,
2195321978
C80512FE243FFD4B00B6B04D /* TenorDataSouceTests.swift in Sources */,
2195421979
323F8F3023A22C4C000BA49C /* SiteCreationRotatingMessageViewTests.swift in Sources */,
2195521980
FF1FD02620912AA900186384 /* URL+LinkNormalizationTests.swift in Sources */,
@@ -23053,6 +23078,7 @@
2305323078
FABB234B2602FC2C00C8785C /* UIBarButtonItem+MeBarButton.swift in Sources */,
2305423079
FABB234C2602FC2C00C8785C /* PostSettingsViewController+FeaturedImageUpload.swift in Sources */,
2305523080
F158542E267D3B8A00A2E966 /* BloggingRemindersFlowSettingsViewController.swift in Sources */,
23081+
8332DD2529259AE300802F7D /* DataMigrator.swift in Sources */,
2305623082
FABB234D2602FC2C00C8785C /* ReaderLikeAction.swift in Sources */,
2305723083
DCF892CD282FA3BB00BB71E1 /* SiteStatsImmuTableRows.swift in Sources */,
2305823084
FABB234E2602FC2C00C8785C /* ReaderMenuAction.swift in Sources */,

0 commit comments

Comments
 (0)