Commit c2fa601a by Demid Merzlyakov

IOS-258: fix app update data migration case.

parent 308284dd
...@@ -80,7 +80,7 @@ ...@@ -80,7 +80,7 @@
<EnvironmentVariables> <EnvironmentVariables>
<EnvironmentVariable <EnvironmentVariable
key = "_XCWidgetKind" key = "_XCWidgetKind"
value = "com.onelouder.oneweather.widget.radar" value = "com.onelouder.oneweather.widget.temperature"
isEnabled = "YES"> isEnabled = "YES">
</EnvironmentVariable> </EnvironmentVariable>
<EnvironmentVariable <EnvironmentVariable
...@@ -95,7 +95,7 @@ ...@@ -95,7 +95,7 @@
</EnvironmentVariable> </EnvironmentVariable>
<EnvironmentVariable <EnvironmentVariable
key = "_XCWidgetFamily" key = "_XCWidgetFamily"
value = "large" value = "medium"
isEnabled = "YES"> isEnabled = "YES">
</EnvironmentVariable> </EnvironmentVariable>
</EnvironmentVariables> </EnvironmentVariables>
......
...@@ -12,6 +12,7 @@ class CoreDataStack { ...@@ -12,6 +12,7 @@ class CoreDataStack {
private let log = Logger(componentName: "CoreDataStorage 💾") private let log = Logger(componentName: "CoreDataStorage 💾")
private let modelName = "1WModel" private let modelName = "1WModel"
private lazy var persistentContainer: NSPersistentContainer = { private lazy var persistentContainer: NSPersistentContainer = {
log.verbose("Make persistant container")
var container: NSPersistentContainer var container: NSPersistentContainer
if if
let modelURL = Bundle(for: CoreDataStorage.self).url(forResource: "1WModel", withExtension: "momd"), let modelURL = Bundle(for: CoreDataStorage.self).url(forResource: "1WModel", withExtension: "momd"),
...@@ -42,55 +43,141 @@ class CoreDataStack { ...@@ -42,55 +43,141 @@ class CoreDataStack {
return containerUrl.appendingPathComponent("\(modelName).sqlite") return containerUrl.appendingPathComponent("\(modelName).sqlite")
} }
private func isInWidget() -> Bool {
guard let extesion = Bundle.main.infoDictionary?["NSExtension"] as? [String: String] else { return false }
guard let widget = extesion["NSExtensionPointIdentifier"] else { return false }
return widget == "com.apple.widgetkit-extension"
}
/// The Persistent Store used to be in the app directory, but was then moved to an app group container to be shared with widgets. /// The Persistent Store used to be in the app directory, but was then moved to an app group container to be shared with widgets.
/// For all users who still have it in the old location, we need to move it to the new one. /// For all users who still have it in the old location, we need to move it to the new one.
private func movePersistentStoreIfNeeded() { private func movePersistentStoreIfNeeded() {
log.verbose("Move store: start")
let legacyStoreUrl = NSPersistentContainer.defaultDirectoryURL().appendingPathComponent("\(modelName).sqlite") let legacyStoreUrl = NSPersistentContainer.defaultDirectoryURL().appendingPathComponent("\(modelName).sqlite")
guard let storeUrl = self.storeUrl else { guard let storeUrl = self.storeUrl else {
return return
} }
let fileManager = FileManager.default let fileManager = FileManager.default
log.verbose("\nLegacy path: \(legacyStoreUrl.path)\nNew path: \(storeUrl.path)")
log.verbose("Move store: will move: \(fileManager.fileExists(atPath: legacyStoreUrl.path) && !fileManager.fileExists(atPath: storeUrl.path)) old exists: \(fileManager.fileExists(atPath: legacyStoreUrl.path)) new exists: \(fileManager.fileExists(atPath: storeUrl.path))")
guard fileManager.fileExists(atPath: legacyStoreUrl.path) && !fileManager.fileExists(atPath: storeUrl.path) else { guard fileManager.fileExists(atPath: legacyStoreUrl.path) && !fileManager.fileExists(atPath: storeUrl.path) else {
log.verbose("Move store: won't do. return.")
return return
} }
do { do {
log.info("Moving the persistent store to the app group location.") log.info("Moving the persistent store to the app group location.")
try fileManager.moveItem(at: legacyStoreUrl, to: storeUrl) try fileManager.moveItem(at: legacyStoreUrl, to: storeUrl)
let additionalFileSuffixes = ["-wal", "-shm"]
for additionalSuffix in additionalFileSuffixes {
let oldPath = legacyStoreUrl.path + additionalSuffix
if fileManager.fileExists(atPath: oldPath) {
let newPath = storeUrl.path + additionalSuffix
log.debug("Move \(oldPath)")
try fileManager.moveItem(atPath: oldPath, toPath: newPath)
}
}
} }
catch { catch {
log.error("Error moving the model from the old location to the new one: \(error)") log.error("Error moving the model from the old location to the new one: \(error)")
} }
log.verbose("Move store: done")
} }
public func getManagedObjectContext(_ completion: @escaping (NSManagedObjectContext?) -> ()) { public func getManagedObjectContext(_ completion: @escaping (NSManagedObjectContext?) -> ()) {
log.verbose("get context: start")
if let existingContext = self.managedContext { if let existingContext = self.managedContext {
log.verbose("get context: got existing")
completion(existingContext) completion(existingContext)
return return
} }
else { else {
initializationSemaphore.wait() log.verbose("get context: no existing")
defer { guard shouldInit() else {
initializationSemaphore.signal() log.verbose("get context: NIL (shouldn't init)")
completion(nil)
return
}
if isInWidget() {
log.verbose("get context: WIDGET. Start wait")
initializationSemaphore.wait()
log.verbose("get context: WIDGET. Resume wait")
if !initializing {
log.debug("get context: WIDGET. Initialize stack...")
initializationSemaphore.signal()
initializeStack()
}
else {
log.verbose("get context: WIDGET. Signal!")
initializationSemaphore.signal()
}
} }
log.verbose("get context: start wait")
initializationSemaphore.wait()
log.verbose("get context: resume wait")
if self.managedContext == nil { if self.managedContext == nil {
self.managedContext = self.persistentContainer.newBackgroundContext() self.managedContext = self.persistentContainer.newBackgroundContext()
log.verbose("get context: initialized (\(self.managedContext == nil ? "fail" : "success"))")
} }
log.verbose("get context: SIGNAL")
initializationSemaphore.signal()
log.verbose("get context: completion")
completion(self.managedContext) completion(self.managedContext)
} }
} }
public init() {
private func shouldInit() -> Bool {
log.verbose("should init: start")
guard isInWidget() else {
log.verbose("should init: true (app)")
return true
}
guard let storeUrl = self.storeUrl else {
log.verbose("should init: false (no url)")
return false
}
let fileManager = FileManager.default
let storeExists = fileManager.fileExists(atPath: storeUrl.path)
log.verbose("should init: \(storeExists) (store exists)")
return storeExists
}
private var initializing = false
private func initializeStack() {
let log = self.log
log.verbose("Initialize stack (start wait)")
initializationSemaphore.wait() initializationSemaphore.wait()
log.verbose("Initialize stack (resume wait)")
initializing = true
DispatchQueue.global(qos: .userInitiated).async { DispatchQueue.global(qos: .userInitiated).async {
log.verbose("Initialize stack: async start")
self.movePersistentStoreIfNeeded() self.movePersistentStoreIfNeeded()
log.verbose("Initialize stack: load stores")
self.persistentContainer.loadPersistentStores { [weak self] (description, error) in self.persistentContainer.loadPersistentStores { [weak self] (description, error) in
defer { if self == nil {
self?.initializationSemaphore.signal() log.verbose("Initialize stack: SELF IS NIL")
} }
log.verbose("Initialize stack: signal")
self?.initializationSemaphore.signal()
log.verbose("Initialize stack: signal (done)")
if let error = error { if let error = error {
self?.log.error("Error loading persistent stores: \(error)") log.error("Error loading persistent stores: \(error)")
} }
} }
} }
} }
public init() {
log.verbose("Init")
if shouldInit() {
initializeStack()
}
}
deinit {
log.verbose("Deinit")
}
} }
...@@ -57,11 +57,12 @@ public class CoreDataStorage: Storage { ...@@ -57,11 +57,12 @@ public class CoreDataStorage: Storage {
public func load(completion: @escaping StorageCompletion) { public func load(completion: @escaping StorageCompletion) {
log.info("Load: start") log.info("Load: start")
stack.getManagedObjectContext { [weak self] context in stack.getManagedObjectContext { [weak self] context in
context?.perform { guard let context = context else {
completion([], nil, nil)
return
}
context.perform {
guard let self = self else { return } guard let self = self else { return }
guard let context = context else {
return
}
let completionWithErrorHandling: StorageCompletion = { [weak self] (locations, selectedIndex, error) in let completionWithErrorHandling: StorageCompletion = { [weak self] (locations, selectedIndex, error) in
if error != nil { if error != nil {
self?.log.error("Load: error.") self?.log.error("Load: error.")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment