From de9deb7fafce3a250a53bf82522c783bd3509b9a Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Sat, 11 May 2024 22:57:26 +0700 Subject: [PATCH] refactor: clean up persistence related code --- Memola.xcodeproj/project.pbxproj | 12 ++++ Memola/App/MemolaApp.swift | 2 +- Memola/Canvas/Contexts/GraphicContext.swift | 24 ++++---- Memola/Canvas/Core/Canvas.swift | 6 +- Memola/Canvas/Geometries/Stroke/Stroke.swift | 4 +- Memola/Canvas/History/History.swift | 6 +- Memola/Extensions/NSManagedObject++.swift | 15 +++++ .../Extensions/NSManagedObjectContext++.swift | 16 ++++++ Memola/Extensions/View++.swift | 16 ++++++ Memola/Persistence/Core/Persistence.swift | 57 +++++-------------- 10 files changed, 95 insertions(+), 63 deletions(-) create mode 100644 Memola/Extensions/NSManagedObject++.swift create mode 100644 Memola/Extensions/NSManagedObjectContext++.swift create mode 100644 Memola/Extensions/View++.swift diff --git a/Memola.xcodeproj/project.pbxproj b/Memola.xcodeproj/project.pbxproj index cf282ed..2dc59b4 100644 --- a/Memola.xcodeproj/project.pbxproj +++ b/Memola.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + EC3565522BEFC65F00A4E0BF /* NSManagedObjectContext++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3565512BEFC65F00A4E0BF /* NSManagedObjectContext++.swift */; }; + EC3565542BEFC6AD00A4E0BF /* View++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3565532BEFC6AD00A4E0BF /* View++.swift */; }; + EC3565562BEFC7B300A4E0BF /* NSManagedObject++.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC3565552BEFC7B300A4E0BF /* NSManagedObject++.swift */; }; EC4538892BEBCAE000A86FEC /* Quad.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC4538882BEBCAE000A86FEC /* Quad.swift */; }; EC7F6BEC2BE5E6E300A34A7B /* MemolaApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */; }; EC7F6BF02BE5E6E400A34A7B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7F6BEF2BE5E6E400A34A7B /* Assets.xcassets */; }; @@ -74,6 +77,9 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + EC3565512BEFC65F00A4E0BF /* NSManagedObjectContext++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext++.swift"; sourceTree = ""; }; + EC3565532BEFC6AD00A4E0BF /* View++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View++.swift"; sourceTree = ""; }; + EC3565552BEFC7B300A4E0BF /* NSManagedObject++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObject++.swift"; sourceTree = ""; }; EC4538882BEBCAE000A86FEC /* Quad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Quad.swift; sourceTree = ""; }; EC7F6BE82BE5E6E300A34A7B /* Memola.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Memola.app; sourceTree = BUILT_PRODUCTS_DIR; }; EC7F6BEB2BE5E6E300A34A7B /* MemolaApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemolaApp.swift; sourceTree = ""; }; @@ -339,6 +345,9 @@ ECA738E72BE6120F00A4542E /* Color++.swift */, ECA738F52BE612B700A4542E /* MTLDevice++.swift */, ECA738ED2BE6125D00A4542E /* simd_float4x4++.swift */, + EC3565512BEFC65F00A4E0BF /* NSManagedObjectContext++.swift */, + EC3565532BEFC6AD00A4E0BF /* View++.swift */, + EC3565552BEFC7B300A4E0BF /* NSManagedObject++.swift */, ); path = Extensions; sourceTree = ""; @@ -594,6 +603,7 @@ ECA738FC2BE61C5200A4542E /* Persistence.swift in Sources */, ECA7387A2BE5EF0400A4542E /* MemosView.swift in Sources */, ECA738BA2BE60DEF00A4542E /* HistoryAction.swift in Sources */, + EC3565522BEFC65F00A4E0BF /* NSManagedObjectContext++.swift in Sources */, ECFA15222BEF21F500455818 /* CanvasObject.swift in Sources */, ECA738AA2BE6026D00A4542E /* Uniforms.swift in Sources */, ECA7387D2BE5EF4B00A4542E /* MemoView.swift in Sources */, @@ -604,6 +614,7 @@ ECA738EA2BE6122E00A4542E /* CGPoint++.swift in Sources */, ECA738A62BE6023F00A4542E /* GridUniforms.swift in Sources */, ECA738D72BE60FC100A4542E /* SolidPointStrokeGenerator.swift in Sources */, + EC3565542BEFC6AD00A4E0BF /* View++.swift in Sources */, ECA738832BE5FEFE00A4542E /* RenderPass.swift in Sources */, ECEC01A82BEE11BA006DA24C /* QuadShape.swift in Sources */, ECA738862BE5FF2500A4542E /* Canvas.swift in Sources */, @@ -618,6 +629,7 @@ ECA739012BE61D9C00A4542E /* MemolaModel.xcdatamodeld in Sources */, ECA738F02BE6127700A4542E /* CGSize++.swift in Sources */, ECFA15242BEF223300455818 /* GraphicContextObject.swift in Sources */, + EC3565562BEFC7B300A4E0BF /* NSManagedObject++.swift in Sources */, ECA738EC2BE6124E00A4542E /* CGAffineTransform++.swift in Sources */, ECA738E22BE610D000A4542E /* GraphicRenderPass.swift in Sources */, ECA738DC2BE6108D00A4542E /* StrokeRenderPass.swift in Sources */, diff --git a/Memola/App/MemolaApp.swift b/Memola/App/MemolaApp.swift index 9f81d25..68edd1f 100644 --- a/Memola/App/MemolaApp.swift +++ b/Memola/App/MemolaApp.swift @@ -12,7 +12,7 @@ struct MemolaApp: App { var body: some Scene { WindowGroup { MemosView() - .environment(\.managedObjectContext, Persistence.context) + .persistence(\.viewContext) } } } diff --git a/Memola/Canvas/Contexts/GraphicContext.swift b/Memola/Canvas/Contexts/GraphicContext.swift index 5e5b67a..3a0705f 100644 --- a/Memola/Canvas/Contexts/GraphicContext.swift +++ b/Memola/Canvas/Contexts/GraphicContext.swift @@ -39,9 +39,9 @@ final class GraphicContext: @unchecked Sendable { func undoGraphic() { guard !strokes.isEmpty else { return } let stroke = strokes.removeLast() - Persistence.backgroundContext.perform { + withPersistence(\.backgroundContext) { [stroke] context in stroke.object?.graphicContext = nil - Persistence.saveIfNeededInBackground() + try context.saveIfNeeded() } previousStroke = nil } @@ -50,9 +50,9 @@ final class GraphicContext: @unchecked Sendable { switch event { case .stroke(let stroke): strokes.append(stroke) - Persistence.backgroundContext.perform { [weak self] in + withPersistence(\.backgroundContext) { [weak self, stroke] context in stroke.object?.graphicContext = self?.object - Persistence.saveIfNeededInBackground() + try context.saveIfNeeded() } previousStroke = nil } @@ -95,8 +95,8 @@ extension GraphicContext { createdAt: .now, thickness: pen.thickness ) - Persistence.backgroundContext.perform { [graphicContext = object, _stroke = stroke] in - let stroke = StrokeObject(context: Persistence.backgroundContext) + withPersistence(\.backgroundContext) { [graphicContext = object, _stroke = stroke] context in + let stroke = StrokeObject(\.backgroundContext) stroke.color = _stroke.color stroke.style = _stroke.style stroke.thickness = _stroke.thickness @@ -125,12 +125,12 @@ extension GraphicContext { currentStroke.finish(at: point) let saveIndex = currentStroke.batchIndex let quads = Array(currentStroke.quads[saveIndex..) { + self.init(context: Persistence.shared[keyPath: keyPath]) + } +} diff --git a/Memola/Extensions/NSManagedObjectContext++.swift b/Memola/Extensions/NSManagedObjectContext++.swift new file mode 100644 index 0000000..946da37 --- /dev/null +++ b/Memola/Extensions/NSManagedObjectContext++.swift @@ -0,0 +1,16 @@ +// +// NSManagedObjectContext++.swift +// Memola +// +// Created by Dscyre Scotti on 5/11/24. +// + +import CoreData + +extension NSManagedObjectContext { + func saveIfNeeded() throws { + if hasChanges { + try save() + } + } +} diff --git a/Memola/Extensions/View++.swift b/Memola/Extensions/View++.swift new file mode 100644 index 0000000..732a860 --- /dev/null +++ b/Memola/Extensions/View++.swift @@ -0,0 +1,16 @@ +// +// View++.swift +// Memola +// +// Created by Dscyre Scotti on 5/11/24. +// + +import SwiftUI +import CoreData +import Foundation + +extension View { + func persistence(_ keyPath: KeyPath) -> some View { + environment(\.managedObjectContext, Persistence.shared[keyPath: keyPath]) + } +} diff --git a/Memola/Persistence/Core/Persistence.swift b/Memola/Persistence/Core/Persistence.swift index e87f914..4cb4a9a 100644 --- a/Memola/Persistence/Core/Persistence.swift +++ b/Memola/Persistence/Core/Persistence.swift @@ -8,29 +8,24 @@ import CoreData import Foundation -class Persistence { +final class Persistence { private let modelName = "MemolaModel" static let shared: Persistence = Persistence() private init() { } - static var context: NSManagedObjectContext = { - shared.persistentContainer.viewContext - }() - - static var backgroundContext: NSManagedObjectContext = { - let context = shared.persistentContainer.newBackgroundContext() - context.undoManager = nil - -// context.automaticallyMergesC hangesFromParent = true - return context - }() - - private lazy var viewContext: NSManagedObjectContext = { + lazy var viewContext: NSManagedObjectContext = { persistentContainer.viewContext }() + lazy var backgroundContext: NSManagedObjectContext = { + let context = persistentContainer.newBackgroundContext() + context.undoManager = nil + context.automaticallyMergesChangesFromParent = true + return context + }() + lazy var persistentContainer: NSPersistentContainer = { let persistentStore = NSPersistentStoreDescription() persistentStore.shouldMigrateStoreAutomatically = true @@ -75,38 +70,16 @@ class Persistence { fatalError("[Memola]: \(error.localizedDescription)") } }() - - static func performe(_ action: (NSManagedObjectContext) -> Void) { - action(shared.viewContext) - } - - static func saveIfNeeded() { - if context.hasChanges { - do { - try context.save() - } catch { - NSLog("[Memola] - \(error.localizedDescription)") - } - } - } - - static func saveIfNeededInBackground() { - if backgroundContext.hasChanges { - do { - try backgroundContext.save() - } catch { - NSLog("[Memola] - \(error.localizedDescription)") - } - } - } } -extension Persistence { - static func background(_ task: @escaping (NSManagedObjectContext) async throws -> Void, errorHandler: ((Error) async -> Void)? = nil) async { +// MARK: - Global Method +func withPersistence(_ keypath: KeyPath, _ task: @escaping (NSManagedObjectContext) throws -> Void) { + let context = Persistence.shared[keyPath: keypath] + context.perform { do { - try await task(backgroundContext) + try task(context) } catch { - await errorHandler?(error) + NSLog("[Memola] - \(error.localizedDescription)") } } }