refactor: clean up persistence related code

This commit is contained in:
dscyrescotti
2024-05-11 22:57:26 +07:00
parent 10e7350511
commit de9deb7faf
10 changed files with 95 additions and 63 deletions

View File

@@ -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 = "<group>"; };
EC3565532BEFC6AD00A4E0BF /* View++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View++.swift"; sourceTree = "<group>"; };
EC3565552BEFC7B300A4E0BF /* NSManagedObject++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObject++.swift"; sourceTree = "<group>"; };
EC4538882BEBCAE000A86FEC /* Quad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Quad.swift; sourceTree = "<group>"; };
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 = "<group>"; };
@@ -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 = "<group>";
@@ -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 */,

View File

@@ -12,7 +12,7 @@ struct MemolaApp: App {
var body: some Scene {
WindowGroup {
MemosView()
.environment(\.managedObjectContext, Persistence.context)
.persistence(\.viewContext)
}
}
}

View File

@@ -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..<currentStroke.quads.count])
Persistence.backgroundContext.perform { [currentStroke, quads] in
withPersistence(\.backgroundContext) { [currentStroke, quads] context in
currentStroke.saveQuads(for: quads)
Persistence.saveIfNeededInBackground()
try context.saveIfNeeded()
if let stroke = currentStroke.object {
currentStroke.quads.removeAll()
Persistence.backgroundContext.refresh(stroke, mergeChanges: false)
context.refresh(stroke, mergeChanges: false)
}
}
previousStroke = currentStroke
@@ -141,12 +141,12 @@ extension GraphicContext {
func cancelStroke() {
if !strokes.isEmpty {
let stroke = strokes.removeLast()
Persistence.backgroundContext.perform { [graphicContext = object, _stroke = stroke] in
withPersistence(\.backgroundContext) { [graphicContext = object, _stroke = stroke] context in
if let stroke = _stroke.object {
graphicContext?.strokes.remove(stroke)
Persistence.backgroundContext.delete(stroke)
context.delete(stroke)
}
Persistence.saveIfNeededInBackground()
try context.saveIfNeeded()
}
}
currentStroke = nil

View File

@@ -44,17 +44,17 @@ final class Canvas: ObservableObject, Identifiable, @unchecked Sendable {
// MARK: - Actions
extension Canvas {
func load() {
Persistence.backgroundContext.perform { [weak self, canvasID] in
withPersistence(\.backgroundContext) { [weak self, canvasID] context in
DispatchQueue.main.async { [weak self] in
self?.state = .loading
}
guard let canvas = Persistence.backgroundContext.object(with: canvasID) as? CanvasObject else {
guard let canvas = context.object(with: canvasID) as? CanvasObject else {
return
}
let graphicContext = canvas.graphicContext
self?.graphicContext.object = graphicContext
self?.graphicContext.load()
Persistence.backgroundContext.refresh(canvas, mergeChanges: false)
context.refresh(canvas, mergeChanges: false)
DispatchQueue.main.async { [weak self] in
self?.state = .loaded
}

View File

@@ -104,14 +104,14 @@ final class Stroke: @unchecked Sendable {
quads.removeLast(dropCount)
let quads = Array(quads[batchIndex..<index])
batchIndex = index
Persistence.backgroundContext.perform { [weak self, quads] in
withPersistence(\.backgroundContext) { [weak self, quads] context in
self?.saveQuads(for: quads)
}
}
func saveQuads(for quads: [Quad]) {
for _quad in quads {
let quad = QuadObject(context: Persistence.backgroundContext)
let quad = QuadObject(\.backgroundContext)
quad.originX = _quad.originX
quad.originY = _quad.originY
quad.size = _quad.size

View File

@@ -52,14 +52,14 @@ class History: ObservableObject {
for event in redoStack {
switch event {
case .stroke(let _stroke):
Persistence.backgroundContext.perform {
withPersistence(\.backgroundContext) { context in
if let stroke = _stroke.object {
Persistence.backgroundContext.delete(stroke)
context.delete(stroke)
}
try context.saveIfNeeded()
}
}
}
Persistence.saveIfNeeded()
redoStack.removeAll()
}

View File

@@ -0,0 +1,15 @@
//
// NSManagedObject++.swift
// Memola
//
// Created by Dscyre Scotti on 5/11/24.
//
import CoreData
import Foundation
extension NSManagedObject {
convenience init(_ keyPath: KeyPath<Persistence, NSManagedObjectContext>) {
self.init(context: Persistence.shared[keyPath: keyPath])
}
}

View File

@@ -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()
}
}
}

View File

@@ -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<Persistence, NSManagedObjectContext>) -> some View {
environment(\.managedObjectContext, Persistence.shared[keyPath: keyPath])
}
}

View File

@@ -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<Persistence, NSManagedObjectContext>, _ 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)")
}
}
}