Commit a760d66e by Demid Merzlyakov

Storage: CoreData: CoreDataStorage implementation.

parent ea42db8c
......@@ -150,6 +150,7 @@
CE9F01BB261B31A6009BA500 /* CoreDataError.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9F01BA261B31A6009BA500 /* CoreDataError.swift */; };
CE9F01BE261B34C0009BA500 /* CoreDataAppModelConvertable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9F01BD261B34C0009BA500 /* CoreDataAppModelConvertable.swift */; };
CE9F01C1261B3776009BA500 /* CoreDataUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9F01C0261B3776009BA500 /* CoreDataUtils.swift */; };
CE9F01CC261C9A6E009BA500 /* AppData.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9F01CB261C9A6D009BA500 /* AppData.swift */; };
CEAD00A12577B2D5003596AD /* StuffThatIsPresentInTheMainProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEAD00A02577B2D5003596AD /* StuffThatIsPresentInTheMainProject.swift */; };
CEAFF08325DFC67F00DF4EBF /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEAFF08225DFC67F00DF4EBF /* Location.swift */; };
CEAFF08925DFC6B200DF4EBF /* CurrentWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEAFF08825DFC6B200DF4EBF /* CurrentWeather.swift */; };
......@@ -332,6 +333,7 @@
CE9F01BA261B31A6009BA500 /* CoreDataError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataError.swift; sourceTree = "<group>"; };
CE9F01BD261B34C0009BA500 /* CoreDataAppModelConvertable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataAppModelConvertable.swift; sourceTree = "<group>"; };
CE9F01C0261B3776009BA500 /* CoreDataUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataUtils.swift; sourceTree = "<group>"; };
CE9F01CB261C9A6D009BA500 /* AppData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppData.swift; sourceTree = "<group>"; };
CEAD00A02577B2D5003596AD /* StuffThatIsPresentInTheMainProject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StuffThatIsPresentInTheMainProject.swift; sourceTree = "<group>"; };
CEAFF08225DFC67F00DF4EBF /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
CEAFF08825DFC6B200DF4EBF /* CurrentWeather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentWeather.swift; sourceTree = "<group>"; };
......@@ -860,6 +862,7 @@
CE89628F26175DF400CA274A /* Objects */ = {
isa = PBXGroup;
children = (
CE9F01CB261C9A6D009BA500 /* AppData.swift */,
CE89629926175DF500CA274A /* Human */,
CE89629026175DF400CA274A /* Machine */,
);
......@@ -1227,6 +1230,7 @@
CD593BDC2608CDF100C93428 /* Date+Now.swift in Sources */,
CD17C60225D15C8500EE884E /* CoordinatorProtocol.swift in Sources */,
CDA5542825EF734200A2E08C /* TodayCellFactory.swift in Sources */,
CE9F01CC261C9A6E009BA500 /* AppData.swift in Sources */,
CEF959742600C3A400975FAA /* FlurryAnalyticsService.swift in Sources */,
CE2847602615A8AD006C8DC5 /* BlendHealthSource.swift in Sources */,
CD86C22225F0DCCB00F38A16 /* PrecipitationView.swift in Sources */,
......
......@@ -6,7 +6,7 @@
<relationship name="health" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CoreHealth" inverseName="airQuality" inverseEntity="CoreHealth"/>
</entity>
<entity name="CoreAppData" representedClassName="CoreAppData" syncable="YES">
<attribute name="selectedIndex" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="selectedIndex" optional="YES" attributeType="Decimal" defaultValueString="0"/>
<relationship name="locations" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="CoreLocation" inverseName="appData" inverseEntity="CoreLocation"/>
</entity>
<entity name="CoreCurrentWeather" representedClassName="CoreCurrentWeather" syncable="YES">
......
......@@ -29,34 +29,63 @@ public class CoreDataStorage: Storage {
guard let context = self.managedContext else {
return
}
self.deleteAll(in: context)
do {
try self.deleteAll(in: context)
let appData = AppData(selectedIndex: selectedIndex, locations: locations)
if let coreAppData = try CoreAppData(context: context, appModel: appData) {
context.insert(coreAppData)
try self.save(context: context)
}
}
catch {
self.log.error("Error during saving: \(error)")
}
}
}
public func load(completion: StorageCompletion) {
managedContext?.perform {
public func load(completion: @escaping StorageCompletion) {
managedContext?.perform { [weak self] in
guard let self = self else { return }
guard let context = self.managedContext else {
return
}
let completionOnMain: StorageCompletion = { (locations, selectedIndex, error) in
DispatchQueue.main.async {
completion(locations, selectedIndex, error)
}
}
do {
let fetchRequest: NSFetchRequest<CoreAppData> = CoreAppData.fetchRequest()
fetchRequest.fetchLimit = 1
let results = try context.fetch(fetchRequest)
guard let coreAppData = results.first else {
completionOnMain([], nil, nil)
return
}
let appData: AppData = try coreAppData.toAppModel()
completionOnMain(appData.locations, appData.selectedIndex, nil)
}
catch {
self.log.error("Error during load: \(error)")
completionOnMain(nil, nil, error)
}
}
}
private func deleteAll(in context: NSManagedObjectContext) {
#warning("Not implemented!")
//TODO: implement
private func deleteAll(in context: NSManagedObjectContext) throws {
let fetchRequest: NSFetchRequest<CoreAppData> = CoreAppData.fetchRequest()
let appDataObjects = try context.fetch(fetchRequest)
if appDataObjects.count > 1 {
log.warning("Somehow we ended up with more than 1 CoreAppData objects in the DB... deleting them all.")
}
for appData in appDataObjects {
context.delete(appData)
}
}
private func saveContext() {
guard let context = managedContext else {
log.warning("saveContext: no context.")
return
}
private func save(context: NSManagedObjectContext) throws {
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
log.error("Error saving: \(nserror), \(nserror.userInfo)")
}
try context.save()
}
}
}
//
// AppData.swift
// 1Weather
//
// Created by Demid Merzlyakov on 06.04.2021.
//
import Foundation
/// A helper structure, so that we could work with CoreAppData the same way we work with everything else.
public struct AppData {
public let selectedIndex: Int?
public let locations: [Location]
}
import Foundation
import CoreData
@objc(CoreAppData)
open class CoreAppData: _CoreAppData {
open class CoreAppData: _CoreAppData, CoreDataAppModelConvertable {
typealias AppModel = AppData
func toAppModel() throws -> AppData {
var appModelLocations = [Location]()
try CoreDataUtils.foreach(in: self.locations, of: self, attributeName: "locations") { (coreLocation: CoreLocation) in
appModelLocations.append(try coreLocation.toAppModel())
}
let result = AppModel(selectedIndex: self.selectedIndex?.intValue, locations: appModelLocations)
return result
}
/// This is here just so that we could inherit the generated init(managedObjectContext) convenience initializer.
public override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
super.init(entity: entity, insertInto: context)
}
required public init?(context: NSManagedObjectContext, appModel: AppData?) throws {
guard let appModel = appModel else {
return nil
}
self.init(managedObjectContext: context)
if let selectedIndex = appModel.selectedIndex {
self.selectedIndex = NSDecimalNumber(value: selectedIndex)
}
self.locations = NSOrderedSet(array: try appModel.locations.compactMap { try CoreLocation(context: context, appModel: $0)})
}
}
......@@ -43,7 +43,7 @@ open class _CoreAppData: NSManagedObject {
// MARK: - Properties
@NSManaged open
var selectedIndex: Int16 // Optional scalars not supported
var selectedIndex: NSDecimalNumber?
// MARK: - Relationships
......
......@@ -7,9 +7,9 @@
import Foundation
public typealias StorageCompletion = ([Location]?, selectedIndex: Int, Error?)
public typealias StorageCompletion = ([Location]?, Int?, Error?) -> ()
public protocol Storage {
func save(locations: [Location], selectedIndex: Int)
func load(completion: StorageCompletion)
func load(completion: @escaping StorageCompletion)
}
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