Commit c2fa601a by Demid Merzlyakov

IOS-258: fix app update data migration case.

parent 308284dd
......@@ -80,7 +80,7 @@
<EnvironmentVariables>
<EnvironmentVariable
key = "_XCWidgetKind"
value = "com.onelouder.oneweather.widget.radar"
value = "com.onelouder.oneweather.widget.temperature"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
......@@ -95,7 +95,7 @@
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetFamily"
value = "large"
value = "medium"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
......
......@@ -12,6 +12,7 @@ class CoreDataStack {
private let log = Logger(componentName: "CoreDataStorage 💾")
private let modelName = "1WModel"
private lazy var persistentContainer: NSPersistentContainer = {
log.verbose("Make persistant container")
var container: NSPersistentContainer
if
let modelURL = Bundle(for: CoreDataStorage.self).url(forResource: "1WModel", withExtension: "momd"),
......@@ -42,55 +43,141 @@ class CoreDataStack {
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.
/// For all users who still have it in the old location, we need to move it to the new one.
private func movePersistentStoreIfNeeded() {
log.verbose("Move store: start")
let legacyStoreUrl = NSPersistentContainer.defaultDirectoryURL().appendingPathComponent("\(modelName).sqlite")
guard let storeUrl = self.storeUrl else {
return
}
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 {
log.verbose("Move store: won't do. return.")
return
}
do {
log.info("Moving the persistent store to the app group location.")
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 {
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?) -> ()) {
log.verbose("get context: start")
if let existingContext = self.managedContext {
log.verbose("get context: got existing")
completion(existingContext)
return
}
else {
log.verbose("get context: no existing")
guard shouldInit() else {
log.verbose("get context: NIL (shouldn't init)")
completion(nil)
return
}
if isInWidget() {
log.verbose("get context: WIDGET. Start wait")
initializationSemaphore.wait()
defer {
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 {
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)
}
}
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()
log.verbose("Initialize stack (resume wait)")
initializing = true
DispatchQueue.global(qos: .userInitiated).async {
log.verbose("Initialize stack: async start")
self.movePersistentStoreIfNeeded()
log.verbose("Initialize stack: load stores")
self.persistentContainer.loadPersistentStores { [weak self] (description, error) in
defer {
self?.initializationSemaphore.signal()
if self == nil {
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 {
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 {
public func load(completion: @escaping StorageCompletion) {
log.info("Load: start")
stack.getManagedObjectContext { [weak self] context in
context?.perform {
guard let self = self else { return }
guard let context = context else {
completion([], nil, nil)
return
}
context.perform {
guard let self = self else { return }
let completionWithErrorHandling: StorageCompletion = { [weak self] (locations, selectedIndex, error) in
if error != nil {
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