[Feature] Persistence (#5)
This PR contains the work done to implement some useful protocols, classes and extensions to use to setup a persistence layer in an application. To provide further details about the work done: - [x] declared the `Persistence` target in the Package file; - [x] forgot to declare the `Communications` and `Persistence` target to the `SwiftLibs` library in the `Package` file; - [x] defined the `Service` public protocol; - [x] implemented the `Fetcher` generic class; - [x] implemented the `bitBucket` static property in the `URL+Devices` public extension; - [x] updated the `README` file. Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// NSFetchRequest+TestEntity.swift
|
||||
// PersistenceTests
|
||||
//
|
||||
// Created by Javier Cicchelli on 17/04/2023.
|
||||
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
|
||||
extension NSFetchRequest where ResultType == TestEntity {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
static func allTestEntities() -> NSFetchRequest<TestEntity> {
|
||||
let request = TestEntity.fetchRequest()
|
||||
|
||||
request.sortDescriptors = []
|
||||
request.resultType = .managedObjectResultType
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21754" systemVersion="22E261" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="TestEntity" representedClassName="TestEntity" syncable="YES" codeGenerationType="class"/>
|
||||
</model>
|
||||
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// TestPersistenceService.swift
|
||||
// PersistenceTests
|
||||
//
|
||||
// Created by Javier Cicchelli on 17/04/2023.
|
||||
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import Persistence
|
||||
|
||||
struct TestPersistenceService {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
static let shared = TestPersistenceService()
|
||||
|
||||
private let container: NSPersistentContainer
|
||||
|
||||
var viewContext: NSManagedObjectContext {
|
||||
container.viewContext
|
||||
}
|
||||
|
||||
// MARK: Initialisers
|
||||
|
||||
init() {
|
||||
guard
|
||||
let modelURL = Bundle.module.url(forResource: .Model.name, withExtension: .Model.extension),
|
||||
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL)
|
||||
else {
|
||||
fatalError("Could not load the Core Data model from the module.")
|
||||
}
|
||||
|
||||
self.container = .init(
|
||||
name: .Model.name,
|
||||
managedObjectModel: managedObjectModel
|
||||
)
|
||||
|
||||
setContainer()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Service
|
||||
|
||||
extension TestPersistenceService: Service {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
func makeTaskContext() -> NSManagedObjectContext {
|
||||
let taskContext = container.newBackgroundContext()
|
||||
|
||||
taskContext.automaticallyMergesChangesFromParent = true
|
||||
taskContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
||||
|
||||
return taskContext
|
||||
}
|
||||
|
||||
func makeChildContext() -> NSManagedObjectContext {
|
||||
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
|
||||
|
||||
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
||||
context.parent = container.viewContext
|
||||
context.automaticallyMergesChangesFromParent = true
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
func save(context: NSManagedObjectContext) throws {
|
||||
guard context.hasChanges else {
|
||||
return
|
||||
}
|
||||
|
||||
try context.save()
|
||||
}
|
||||
|
||||
func save(childContext context: NSManagedObjectContext) throws {
|
||||
guard context.hasChanges else {
|
||||
return
|
||||
}
|
||||
|
||||
try context.save()
|
||||
|
||||
guard
|
||||
let parent = context.parent,
|
||||
parent == container.viewContext
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
try parent.performAndWait {
|
||||
try parent.save()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private extension TestPersistenceService {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
func setContainer() {
|
||||
container.persistentStoreDescriptions = [
|
||||
.init(url: .bitBucket)
|
||||
]
|
||||
|
||||
container.loadPersistentStores { _, error in
|
||||
if let error = error as NSError? {
|
||||
fatalError("Unresolved error \(error), \(error.userInfo)")
|
||||
}
|
||||
}
|
||||
|
||||
container.viewContext.automaticallyMergesChangesFromParent = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - String+Constants
|
||||
|
||||
private extension String {
|
||||
enum Model {
|
||||
static let name = "TestModel"
|
||||
static let `extension` = "momd"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user