Skip to content

Commit aeb4883

Browse files
authored
Merge pull request #19622 from wordpress-mobile/task/data-migration-class
Jetpack Content Migration Flow: Add a data migrator class
2 parents adbb17e + 1dc99c3 commit aeb4883

File tree

7 files changed

+383
-29
lines changed

7 files changed

+383
-29
lines changed

WordPress/Classes/Stores/UserPersistentRepositoryUtility.swift

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ private enum UPRUConstants {
1515
static let currentAnnouncementsKey = "currentAnnouncements"
1616
static let currentAnnouncementsDateKey = "currentAnnouncementsDate"
1717
static let announcementsVersionDisplayedKey = "announcementsVersionDisplayed"
18-
static let bloggingRemindersCopiedKey = "reminders-copied"
19-
static let sharedBloggingRemindersCopiedKey = "shared-reminders-copied"
2018
}
2119

2220
protocol UserPersistentRepositoryUtility: AnyObject {
@@ -166,22 +164,4 @@ extension UserPersistentRepositoryUtility {
166164
UserPersistentStoreFactory.instance().set(newValue, forKey: UPRUConstants.announcementsVersionDisplayedKey)
167165
}
168166
}
169-
170-
var bloggingRemindersCopied: Bool {
171-
get {
172-
UserPersistentStoreFactory.instance().bool(forKey: UPRUConstants.bloggingRemindersCopiedKey)
173-
}
174-
set {
175-
UserPersistentStoreFactory.instance().set(newValue, forKey: UPRUConstants.bloggingRemindersCopiedKey)
176-
}
177-
}
178-
179-
var sharedBloggingRemindersCopied: Bool {
180-
get {
181-
UserPersistentStoreFactory.instance().bool(forKey: UPRUConstants.sharedBloggingRemindersCopiedKey)
182-
}
183-
set {
184-
UserPersistentStoreFactory.instance().set(newValue, forKey: UPRUConstants.sharedBloggingRemindersCopiedKey)
185-
}
186-
}
187167
}

WordPress/Classes/System/WordPressAppDelegate.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ class WordPressAppDelegate: UIResponder, UIApplicationDelegate {
155155
NotificationCenter.default.post(name: .applicationLaunchCompleted, object: nil)
156156

157157
copyToSharedDefaultsIfNeeded()
158-
BloggingRemindersScheduler.handleRemindersMigration()
159158
return true
160159
}
161160

WordPress/Classes/Utility/Blogging Reminders/BloggingRemindersScheduler.swift

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,7 @@ class BloggingRemindersScheduler {
159159
}
160160

161161
private static func copyStoreToSharedFile() {
162-
guard !UserPersistentStoreFactory.instance().bloggingRemindersCopied,
163-
let store = try? defaultStore(),
162+
guard let store = try? defaultStore(),
164163
let fileUrl = try? defaultDataFileURL(),
165164
let sharedFileUrl = sharedDataFileURL() else {
166165
return
@@ -170,13 +169,10 @@ class BloggingRemindersScheduler {
170169
if store.configuration.count > 0 {
171170
try? FileManager.default.copyItem(at: fileUrl, to: sharedFileUrl)
172171
}
173-
174-
UserPersistentStoreFactory.instance().bloggingRemindersCopied = true
175172
}
176173

177174
private static func copyStoreToLocalFile() {
178-
guard !UserPersistentStoreFactory.instance().sharedBloggingRemindersCopied,
179-
let localStore = try? defaultStore(),
175+
guard let localStore = try? defaultStore(),
180176
let sharedFileUrl = sharedDataFileURL(),
181177
FileManager.default.fileExists(at: sharedFileUrl),
182178
let sharedStore = try? BloggingRemindersStore(dataFileURL: sharedFileUrl) else {
@@ -190,8 +186,6 @@ class BloggingRemindersScheduler {
190186
try? localStore.save(scheduledReminders: schedule, for: blogIdentifier)
191187
}
192188
}
193-
194-
UserPersistentStoreFactory.instance().sharedBloggingRemindersCopied = true
195189
}
196190

197191
// MARK: - Initializers

WordPress/Classes/Utility/CoreDataHelper.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ extension CoreDataStack {
223223
/// Creates a copy of the current open store and saves it to the specified destination
224224
/// - Parameter backupLocation: Location to save the store copy to
225225
func createStoreCopy(to backupLocation: URL) throws {
226+
let (backupLocation, shmLocation, walLocation) = databaseFiles(for: backupLocation)
227+
try? FileManager.default.removeItem(at: backupLocation)
228+
try? FileManager.default.removeItem(at: shmLocation)
229+
try? FileManager.default.removeItem(at: walLocation)
226230
guard let storeCoordinator = mainContext.persistentStoreCoordinator,
227231
let store = storeCoordinator.persistentStores.first else {
228232
throw ContextManager.ContextManagerError.missingCoordinatorOrStore
@@ -267,6 +271,7 @@ extension CoreDataStack {
267271
throw ContextManager.ContextManagerError.missingDatabase
268272
}
269273

274+
mainContext.reset()
270275
try storeCoordinator.remove(store)
271276
let databaseReplaced = replaceDatabase(from: databaseLocation, to: currentDatabaseLocation)
272277

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
@@ -1809,6 +1809,9 @@
18091809
8320BDE6283D9359009DF2DE /* BlogService+BloggingPrompts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8320BDE4283D9359009DF2DE /* BlogService+BloggingPrompts.swift */; };
18101810
8323789828526E6D003F4443 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA25F9FD2609AA830005E08F /* AppConfiguration.swift */; };
18111811
8323789928526E6E003F4443 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA25F9FD2609AA830005E08F /* AppConfiguration.swift */; };
1812+
8332DD2429259AE300802F7D /* DataMigrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8332DD2329259AE300802F7D /* DataMigrator.swift */; };
1813+
8332DD2529259AE300802F7D /* DataMigrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8332DD2329259AE300802F7D /* DataMigrator.swift */; };
1814+
8332DD2829259BEB00802F7D /* DataMigratorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8332DD2729259BEB00802F7D /* DataMigratorTests.swift */; };
18121815
834CE7341256D0DE0046A4A3 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 834CE7331256D0DE0046A4A3 /* CFNetwork.framework */; };
18131816
8350E49611D2C71E00A7B073 /* Media.m in Sources */ = {isa = PBXBuildFile; fileRef = 8350E49511D2C71E00A7B073 /* Media.m */; };
18141817
8355D7D911D260AA00A61362 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8355D7D811D260AA00A61362 /* CoreData.framework */; };
@@ -6891,6 +6894,8 @@
68916894
83043E54126FA31400EC9953 /* MessageUI.framework */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; };
68926895
830A58D72793AB4400CDE94F /* LoginEpilogueAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginEpilogueAnimator.swift; sourceTree = "<group>"; };
68936896
8320BDE4283D9359009DF2DE /* BlogService+BloggingPrompts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BlogService+BloggingPrompts.swift"; sourceTree = "<group>"; };
6897+
8332DD2329259AE300802F7D /* DataMigrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataMigrator.swift; sourceTree = "<group>"; };
6898+
8332DD2729259BEB00802F7D /* DataMigratorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataMigratorTests.swift; sourceTree = "<group>"; };
68946899
833AF259114575A50016DE8F /* PostAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostAnnotation.h; sourceTree = "<group>"; };
68956900
833AF25A114575A50016DE8F /* PostAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PostAnnotation.m; sourceTree = "<group>"; };
68966901
834CE7331256D0DE0046A4A3 /* CFNetwork.framework */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
@@ -12382,6 +12387,7 @@
1238212387
803DE81D29063689007D4E9C /* Jetpack */ = {
1238312388
isa = PBXGroup;
1238412389
children = (
12390+
8332DD2629259B9700802F7D /* Utility */,
1238512391
803DE81E290636A4007D4E9C /* JetpackFeaturesRemovalCoordinatorTests.swift */,
1238612392
801D951C291ADB7E0051993E /* JetpackOverlayFrequencyTrackerTests.swift */,
1238712393
);
@@ -12502,6 +12508,22 @@
1250212508
path = Cells;
1250312509
sourceTree = "<group>";
1250412510
};
12511+
8332DD2229259ABA00802F7D /* Utility */ = {
12512+
isa = PBXGroup;
12513+
children = (
12514+
8332DD2329259AE300802F7D /* DataMigrator.swift */,
12515+
);
12516+
path = Utility;
12517+
sourceTree = "<group>";
12518+
};
12519+
8332DD2629259B9700802F7D /* Utility */ = {
12520+
isa = PBXGroup;
12521+
children = (
12522+
8332DD2729259BEB00802F7D /* DataMigratorTests.swift */,
12523+
);
12524+
name = Utility;
12525+
sourceTree = "<group>";
12526+
};
1250512527
850BD4531922F95C0032F3AD /* Networking */ = {
1250612528
isa = PBXGroup;
1250712529
children = (
@@ -15030,6 +15052,7 @@
1503015052
C72A52CD2649B14B009CA633 /* System */,
1503115053
C7124E4B2638527D00929318 /* NUX */,
1503215054
C7F7ABD5261CED7A00CE547F /* JetpackAuthenticationManager.swift */,
15055+
8332DD2229259ABA00802F7D /* Utility */,
1503315056
C7F7AC72261CF1C900CE547F /* ViewRelated */,
1503415057
);
1503515058
path = Classes;
@@ -21369,6 +21392,7 @@
2136921392
57AA848F228715DA00D3C2A2 /* PostCardCell.swift in Sources */,
2137021393
FE43DAAF26DFAD1C00CFF595 /* CommentContentTableViewCell.swift in Sources */,
2137121394
8F22804451E5812433733348 /* TimeZoneSearchHeaderView.swift in Sources */,
21395+
8332DD2429259AE300802F7D /* DataMigrator.swift in Sources */,
2137221396
8F228F2923045666AE456D2C /* TimeZoneSelectorViewController.swift in Sources */,
2137321397
);
2137421398
runOnlyForDeploymentPostprocessing = 0;
@@ -22022,6 +22046,7 @@
2202222046
80EF92932810FA5A0064A971 /* QuickStartFactoryTests.swift in Sources */,
2202322047
D848CBFF20FF010F00A9038F /* FormattableCommentContentTests.swift in Sources */,
2202422048
9123471B221449E200BD9F97 /* GutenbergInformativeDialogTests.swift in Sources */,
22049+
8332DD2829259BEB00802F7D /* DataMigratorTests.swift in Sources */,
2202522050
C80512FE243FFD4B00B6B04D /* TenorDataSouceTests.swift in Sources */,
2202622051
323F8F3023A22C4C000BA49C /* SiteCreationRotatingMessageViewTests.swift in Sources */,
2202722052
FF1FD02620912AA900186384 /* URL+LinkNormalizationTests.swift in Sources */,
@@ -23127,6 +23152,7 @@
2312723152
FABB234B2602FC2C00C8785C /* UIBarButtonItem+MeBarButton.swift in Sources */,
2312823153
FABB234C2602FC2C00C8785C /* PostSettingsViewController+FeaturedImageUpload.swift in Sources */,
2312923154
F158542E267D3B8A00A2E966 /* BloggingRemindersFlowSettingsViewController.swift in Sources */,
23155+
8332DD2529259AE300802F7D /* DataMigrator.swift in Sources */,
2313023156
FABB234D2602FC2C00C8785C /* ReaderLikeAction.swift in Sources */,
2313123157
DCF892CD282FA3BB00BB71E1 /* SiteStatsImmuTableRows.swift in Sources */,
2313223158
FABB234E2602FC2C00C8785C /* ReaderMenuAction.swift in Sources */,

0 commit comments

Comments
 (0)