Commit 0b1f2688 by Demid Merzlyakov

DelayedStorage to optimize data saving

parent b24d371c
...@@ -209,6 +209,7 @@ ...@@ -209,6 +209,7 @@
CEDE4E8925EEFFEF007457E9 /* WdtDayNight.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDE4E8825EEFFEF007457E9 /* WdtDayNight.swift */; }; CEDE4E8925EEFFEF007457E9 /* WdtDayNight.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDE4E8825EEFFEF007457E9 /* WdtDayNight.swift */; };
CEDE4F0B25EFA3A7007457E9 /* UpdatableModelObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDE4F0A25EFA3A7007457E9 /* UpdatableModelObject.swift */; }; CEDE4F0B25EFA3A7007457E9 /* UpdatableModelObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDE4F0A25EFA3A7007457E9 /* UpdatableModelObject.swift */; };
CEDE4F0F25EFA3B4007457E9 /* UpdatableModelObjectInTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDE4F0E25EFA3B4007457E9 /* UpdatableModelObjectInTime.swift */; }; CEDE4F0F25EFA3B4007457E9 /* UpdatableModelObjectInTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDE4F0E25EFA3B4007457E9 /* UpdatableModelObjectInTime.swift */; };
CEE0A179262FA9650044C257 /* DelayedSaveStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE0A178262FA9650044C257 /* DelayedSaveStorage.swift */; };
CEF959652600C2F900975FAA /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF959642600C2F900975FAA /* AnalyticsService.swift */; }; CEF959652600C2F900975FAA /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF959642600C2F900975FAA /* AnalyticsService.swift */; };
CEF959692600C30500975FAA /* Global.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF959682600C30500975FAA /* Global.swift */; }; CEF959692600C30500975FAA /* Global.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF959682600C30500975FAA /* Global.swift */; };
CEF9596C2600C32E00975FAA /* AnalyticsEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF9596B2600C32E00975FAA /* AnalyticsEvent.swift */; }; CEF9596C2600C32E00975FAA /* AnalyticsEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF9596B2600C32E00975FAA /* AnalyticsEvent.swift */; };
...@@ -443,6 +444,7 @@ ...@@ -443,6 +444,7 @@
CEDE4E8825EEFFEF007457E9 /* WdtDayNight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WdtDayNight.swift; sourceTree = "<group>"; }; CEDE4E8825EEFFEF007457E9 /* WdtDayNight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WdtDayNight.swift; sourceTree = "<group>"; };
CEDE4F0A25EFA3A7007457E9 /* UpdatableModelObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdatableModelObject.swift; sourceTree = "<group>"; }; CEDE4F0A25EFA3A7007457E9 /* UpdatableModelObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdatableModelObject.swift; sourceTree = "<group>"; };
CEDE4F0E25EFA3B4007457E9 /* UpdatableModelObjectInTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdatableModelObjectInTime.swift; sourceTree = "<group>"; }; CEDE4F0E25EFA3B4007457E9 /* UpdatableModelObjectInTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdatableModelObjectInTime.swift; sourceTree = "<group>"; };
CEE0A178262FA9650044C257 /* DelayedSaveStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelayedSaveStorage.swift; sourceTree = "<group>"; };
CEF959642600C2F900975FAA /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = "<group>"; }; CEF959642600C2F900975FAA /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = "<group>"; };
CEF959682600C30500975FAA /* Global.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Global.swift; sourceTree = "<group>"; }; CEF959682600C30500975FAA /* Global.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Global.swift; sourceTree = "<group>"; };
CEF9596B2600C32E00975FAA /* AnalyticsEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsEvent.swift; sourceTree = "<group>"; }; CEF9596B2600C32E00975FAA /* AnalyticsEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsEvent.swift; sourceTree = "<group>"; };
...@@ -1236,6 +1238,7 @@ ...@@ -1236,6 +1238,7 @@
children = ( children = (
CEFB857126174F7A00C5CDD2 /* Storage.swift */, CEFB857126174F7A00C5CDD2 /* Storage.swift */,
CEFB8577261750DF00C5CDD2 /* CoreData */, CEFB8577261750DF00C5CDD2 /* CoreData */,
CEE0A178262FA9650044C257 /* DelayedSaveStorage.swift */,
); );
path = Storage; path = Storage;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -1593,6 +1596,7 @@ ...@@ -1593,6 +1596,7 @@
CD593BD32608BC3F00C93428 /* ForecastDayCell.swift in Sources */, CD593BD32608BC3F00C93428 /* ForecastDayCell.swift in Sources */,
CD4742D0261200500061AC95 /* TodayAlertCell.swift in Sources */, CD4742D0261200500061AC95 /* TodayAlertCell.swift in Sources */,
CD15DB4225DA806C00024727 /* TodayForecastTimePeriodCell.swift in Sources */, CD15DB4225DA806C00024727 /* TodayForecastTimePeriodCell.swift in Sources */,
CEE0A179262FA9650044C257 /* DelayedSaveStorage.swift in Sources */,
CE0456242629C04C003D252B /* NWSAlertsManager.swift in Sources */, CE0456242629C04C003D252B /* NWSAlertsManager.swift in Sources */,
CE8962AB26175DF500CA274A /* CorePollutant.swift in Sources */, CE8962AB26175DF500CA274A /* CorePollutant.swift in Sources */,
CEC5276025E92DDA00DA58A5 /* WdtHourlySummary.swift in Sources */, CEC5276025E92DDA00DA58A5 /* WdtHourlySummary.swift in Sources */,
......
...@@ -150,7 +150,12 @@ public class LocationManager { ...@@ -150,7 +150,12 @@ public class LocationManager {
} }
} }
public static let shared = LocationManager(weatherUpdateSource: WdtWeatherSource(), healthSource: BlendHealthSource(), nwsAlertsManager: NWSAlertsManager(), storage: CoreDataStorage()) public static let shared = LocationManager(
weatherUpdateSource: WdtWeatherSource(),
healthSource: BlendHealthSource(),
nwsAlertsManager: NWSAlertsManager(),
storage: DelayedSaveStorage(storage: CoreDataStorage(), delay: 2))
public let maxLocationsCount = 12 public let maxLocationsCount = 12
public init(weatherUpdateSource: WeatherSource, healthSource: HealthSource, nwsAlertsManager: NWSAlertsManager, storage: Storage) { public init(weatherUpdateSource: WeatherSource, healthSource: HealthSource, nwsAlertsManager: NWSAlertsManager, storage: Storage) {
......
...@@ -74,6 +74,7 @@ public struct Location { ...@@ -74,6 +74,7 @@ public struct Location {
guard self == other else { return false } guard self == other else { return false }
guard self.lastWeatherUpdateDate == other.lastWeatherUpdateDate else { return false } guard self.lastWeatherUpdateDate == other.lastWeatherUpdateDate else { return false }
guard self.health?.lastUpdateTime == other.health?.lastUpdateTime else { return false } guard self.health?.lastUpdateTime == other.health?.lastUpdateTime else { return false }
guard self.notifications?.updatedAt == other.notifications?.updatedAt else { return false }
return true return true
} }
} }
......
...@@ -103,10 +103,8 @@ public class CoreDataStorage: Storage { ...@@ -103,10 +103,8 @@ public class CoreDataStorage: Storage {
} }
private func save(context: NSManagedObjectContext) throws { private func save(context: NSManagedObjectContext) throws {
log.info("Context save: start")
if context.hasChanges { if context.hasChanges {
try context.save() try context.save()
log.info("Context save: success")
} }
else { else {
log.info("Context save: no need") log.info("Context save: no need")
......
//
// DelayedSaveStorage.swift
// 1Weather
//
// Created by Demid Merzlyakov on 21.04.2021.
//
import Foundation
/// A decorator Storage that adds delay before saving to the nested storage.
public class DelayedSaveStorage: Storage {
private let delay: TimeInterval
private let storage: Storage
private let savingQueue: OperationQueue = {
let queue = OperationQueue()
queue.name = "DelayedSaveStorageSavingQueue"
queue.maxConcurrentOperationCount = 1
return queue
}()
/// A "cache" to allow for loading after saving without waiting.
private var latestKnownAppData: AppData?
private let latestKnownAppDataSynchronizationQueue: OperationQueue = {
let queue = OperationQueue()
queue.name = "DelayedSaveStorageVariableAccessQueue"
queue.maxConcurrentOperationCount = 1
return queue
}()
public init(storage: Storage, delay: TimeInterval) {
self.delay = delay
self.storage = storage
}
public func save(locations: [Location], selectedIndex: Int?) {
latestKnownAppDataSynchronizationQueue.addOperation {
self.latestKnownAppData = AppData(selectedIndex: selectedIndex, locations: locations)
}
savingQueue.cancelAllOperations()
let saveOperation = BlockOperation()
saveOperation.addExecutionBlock { [weak saveOperation, weak self] in
guard saveOperation?.isCancelled == false else {
return
}
if let delay = self?.delay, delay > 0 {
Thread.sleep(forTimeInterval: delay)
}
guard saveOperation?.isCancelled == false else {
return
}
self?.storage.save(locations: locations, selectedIndex: selectedIndex)
}
savingQueue.addOperation(saveOperation)
}
public func load(completion: @escaping StorageCompletion) {
latestKnownAppDataSynchronizationQueue.addOperation { [weak self] in
if let lastKnownData = self?.latestKnownAppData {
completion(lastKnownData.locations, lastKnownData.selectedIndex, nil)
return
}
else {
self?.storage.load(completion: completion)
}
}
}
}
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
import Foundation import Foundation
/// locations list, selectedIndex, error
public typealias StorageCompletion = ([Location]?, Int?, Error?) -> () public typealias StorageCompletion = ([Location]?, Int?, Error?) -> ()
public protocol Storage { public protocol Storage {
......
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