CoreData Using Generics and The Singleton Design Pattern - by Gianluca Annina - Medium
CoreData Using Generics and The Singleton Design Pattern - by Gianluca Annina - Medium
516
What is, then, the most efficient way to use CoreData? Let’s take a look at
what Xcode suggests us to do when creating a new project importing
CoreData.
Checking this box will create a whole project ready to be used with examples
of how to use CoreData with different useful functions already implemented.
Most important it will generate the “Data Model” file and the
“PersistenceController” struct. Let's take a deeper look at this struct.
1 import CoreData
2
3 struct PersistenceController {
4
5 static let shared = PersistenceController()
6
7 static var preview: PersistenceController = {
8 let result = PersistenceController(inMemory: true)
9 let viewContext = result.container.viewContext
10 for _ in 0..<10 {
11 let newItem = Item(context: viewContext)
12 newItem.timestamp = Date()
13 }
14 do {
15 try viewContext.save()
16 } catch {
17 // Replace this implementation with code to handle the error appropriately.
18 // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful dur
19 let nsError = error as NSError
20 fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
21 }
22 return result
23 }()
24
25 let container: NSPersistentContainer
26
27 init(inMemory: Bool = false) {
28 container = NSPersistentContainer(name: "CoreDataTutorial")
29 if inMemory {
30 container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
31 }
32 container.loadPersistentStores(completionHandler: { (storeDescription, error) in
33 if let error = error as NSError? {
34 // Replace this implementation with code to handle the error appropriately.
35 // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful
36
37 /*
38 Typical reasons for an error here include:
39 * The parent directory does not exist, cannot be created, or disallows writing.
40 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
41 * The device is out of space.
42 * The store could not be migrated to the current model version.
43 Check the error message to determine what the actual problem was.
44 */
45 fatalError("Unresolved error \(error), \(error.userInfo)")
46 }
47 })
48 container.viewContext.automaticallyMergesChangesFromParent = true
49 }
50 }
1 import SwiftUI
2
3 @main
4 struct CoreDataTutorialApp: App {
5 let persistenceController = PersistenceController.shared
6
7 var body: some Scene {
8 WindowGroup {
9 ContentView()
10 .environment(\.managedObjectContext, persistenceController.container.viewContext)
11 }
12 }
13 }
This kind of approach doesn’t take full advantage of the Singleton design
pattern and makes the code messy.
1 class PersinstanceController{
2
3 static let shared = PersinstanceController()
4
5
6 let container = NSPersistentContainer(name: "ContainerName")
7
8 private init(){
9
10 container.loadPersistentStores{ description, error in
11
12 if let error = error {
13
14 print("Core Data failed to load: \(error.localizedDescription)")
15
16 //Handle the error correctly
17
18 }
19 }
20 }
21 }
Now we can create the functions that implement the generics, in detail
“fetch” and “delete”
The “fetch” function is simple, it takes as a parameter the name of the entity
and the type, using them it retrieves all the values stored in the table.
1
2 func fetch<T:NSFetchRequestResult>(entityName:String) throws -> [T] {
3
4 let request = NSFetchRequest<T>(entityName: entityName)
5
6 let entities = try container.viewContext.fetch(request)
7
8 return entities
9
10 }
11
12 let foodArray:[FoodEntity] = try PersistenceController.shared.fetch(entityName: "FoodEntity")
Be careful: when using the generics in this way since you always have to
declare the type of your variable, otherwise the function will not understand
from what table you need to fetch data.
1
2 let foodArray:[FoodEntity] = try PersistenceController.shared.fetch(entityName: "FoodEntity") // Works because you pointed out the type
3
4 let foodArray = try PersistenceController.shared.fetch(entityName: "FoodEntity") // Doesn't work since it doesn't know the type you need to use
The “delete” function is even simpler and just needs, as a parameter, the
element you want to delete.
The “saveContext” function it’s used to save the changes made when
deleting/adding/editing an object.
Now you are probably asking yourself: “What are Product and the
copyInEntity functions?”
Product is the struct that encapsulates all the pieces of information retrieved
from CoreData and handles them inside the view.
1 import Foundation
2
3 struct Product: Identifiable, Hashable {
4 var id = UUID()
5 var name: String
6 var dateCreated = Date()
7 var expiryDate: Date
8 var isOpened: Bool
9 var category: Category
10
11 init(name: String, dateCreated: Date = Date(), expiryDate: Date, isOpened: Bool, category: Category) {
12 self.name = name
13 self.dateCreated = dateCreated
14 self.expiryDate = expiryDate
15 self.isOpened = isOpened
16 self.category = category
17 }
18
19 init(food:FoodEntity) {
20 self.name = food.name ?? ""
21 self.dateCreated = food.dateCreated ?? Date()
22 self.expiryDate = food.expiryDate ?? Date()
23 self.isOpened = food.isOpened
24 self.category = Category.getCategory(category: food.category ?? "")
25 self.id = food.id ?? UUID()
26 }
27
28 func copyInEntity(food:FoodEntity){
29 food.name = self.name
30 food.category = self.category.name
31 food.isOpened = self.isOpened
32 food.expiryDate = self.expiryDate
33 food.dateCreated = self.dateCreated
34 food.id = self.id
35 }
36 }
37
38 enum Category: CaseIterable {
39 case meat
40 case fish
41 case vegetables
42 case fruit
43 case others
44
45 var name: String {
46 switch self {
47 case .meat:
48 return "Meat"
49 case .fish:
50 return "Fish"
51 case .vegetables:
52 return "Vegetables"
53 case .fruit:
54 return "Fruit"
55 case .others:
56 return "Others"
57 }
58 }
59 static func getCategory(category:String)->Category{
60 if category == "Meat"{
61 return .meat
62 }else if category == "Fish"{
63 return .fish
64 }else if category == "Vegetables"{
65 return .vegetables
66 }else if category == "Fruit"{
67 return.fruit
68 }else if category == "Others"{
69 return .others
70 }
71 return .others
72 }
73 }
74
75
76
77
Inside the struct are defined two functions that help to exchange data
between the swift object and the CoreData object:
the custom init takes as a parameter a CoreData object and assigns all the
values to the swift one;
These two functions are useful in order to clean the code and handle the
code in a faster way.
If you want to check the original project here is the GitHub link:
https://github.com/SidusProxy/CoreDataTutorial
516
68 Followers
API calls using Swift async/await How to link your GitHub account to
and error handling Xcode
Are you trying to create a new application To link your GitHub account first of all you
using some API calls in Swift but you don’t… need to configure it on your Mac. Open the…
know where to start? This tutorial guides terminal (cmd + spacebar and type
3 min read · Feb 14
you… 3 min read · and…
“terminal”) Feb 5
324 306
Use Swift Actor to handle APIs Build a Basic Server Side Swift
calls Application With Vapor
In this tutorial we will explain what an actor is Design a university’s exam system
and how to use it to handle APIs calls.
310 2 311
14 1
Lists
Staff Picks
541 stories · 559 saves
Modelling Schema with SwiftData: “SwiftUI and Core Data”. How Core
A Comprehensive Guide Data can be used with SwiftUI to…
Introduced at WWDC23, SwiftData is a store data
Today, let’spersistently
delve into an impactful
powerful and expressive persistence… combination for app developers—SwiftUI a…
framework built for Swift. It allows developers Core Data. This merging of a versatile…
4
tomin · Jul 6
read their…
model 2 min read · Nov 25
10 1 2
219 1 3
Help Status About Careers Blog Privacy Terms Text to speech Teams