How do I access and use an object using Core Data

It's time to use Basic Data , but the open documentation and manuals there spend a lot of time talking about the general setup and nitty gritty "backstage". These things are important, but I would like a quick, clean source to show how to actually use the information stored in the Core Data model.

Scenario

In my simple example, I have one type of entity:

Job - salary [Double] - dateCreated [Date] 

This is a Swift iOS application running on bulletin boards with the default AppDelegate.swift created, which handles the generation of my managed entity.

Question

How to use Job instances in my application?

Bonus points if you can also talk about these elements:

  • As someone who is used to the MVC design pattern, how can I avoid including dirty data access inside my controllers without using the best iOS development practices?
  • How can I access objects from Core Data by following DRY ?
  • How to transfer managed objects between methods and controllers while maintaining their type?

The Core Data documentation provides some snippets for retrieving records. This question essentially asks where this logic belongs in the iOS app, and how to actually interact with the extracted records after they are received.

Example

This question should not be a broad, broad question, so I will describe it in an example with an attempt to use Core Data. In my example, I have a single UIViewController that has a label. I want this label to show the salary from work.

 import UIKit import CoreData class JobViewController: UIViewController { @IBOutlet var salaryLabel: UILabel! let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext func updateLabel() { var job = getCurrentJob() salaryLabel.text = job.salary // ERRORS } func getCurrentJob()->(???) { var error: NSError? if let fetchedResults = managedObjectContext!.executeFetchRequest(NSFetchRequest(entityName:"Job"), error: &error) { return fetchedResults[0] } } override func viewDidLoad() { super.viewDidLoad() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } } 

This example will not compile for two reasons:

  • I did not specify a return type for getCurrentJob, since I was not sure which type returned
  • The ERRORS line that is trying to access the salary attribute will be an error because there is no way to find out that the salary is the actual attribute of the job.

How to get around and use the task object?

+5
source share
3 answers

The key missing part of the above example is the NSManagedObject subclasses , and in Swift, the @NSManaged Swift annotation. NSManagedObject is a general class that, in its simplest form, can be extended to simply provide access to the attributes of a Core Data object, but in fact this is where the traditional model logic should exist.

Subclassing NSManagedObject

You can automatically generate these objects by looking at the Core Data model and using the menu command: Editor->Create NSManagedObject Subclass .

This will generate Job.swift (or any other name for your object)

 import Foundation import CoreData class Job: NSManagedObject { @NSManaged var dateCreated: NSDate @NSManaged var salary: NSNumber } 

Using NSManagedObject subclasses

Now your new class is available for use, and you can automatically output the result! To complete here, an updated version of a previously broken example

 import UIKit import CoreData class JobViewController: UIViewController { @IBOutlet var salaryLabel: UILabel! let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext func updateLabel() { var job:Job = getCurrentJob() salaryLabel.text = job.salary // ERRORS } func getCurrentJob()->Job { var error: NSError? if let fetchedResults = managedObjectContext!.executeFetchRequest(NSFetchRequest(entityName:"Job"), error: &error) { return fetchedResults[0] } } override func viewDidLoad() { super.viewDidLoad() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } } 
+5
source

For example, create a Core Data model as follows:

enter image description here

Now create the Swift source code (with Editor | Create NSManagedObject Subclass ). This will allow you to compile the next version of JobViewController (which currently lacks error handling, etc.):

 import UIKit import CoreData class JobViewController: UIViewController { @IBOutlet var salaryLabel: UILabel! let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext var jobs: [Job] = [] override func viewDidLoad() { super.viewDidLoad(); jobs = managedObjectContext.executeFetchRequest(NSFetchRequest(entityName:"Job"), error: nil) as [Job]; } func updateLabel() { salaryLabel.text = "\(jobs[0].salary) $" } } 
+2
source

Although I know that some hardcore OOP proponents will be unhappy with this decision, I suggest using singleton classes to manage master data in the application.

I would advise creating a global CoreDataManager that can be accessed through a shared instance. Now you have global access to your search, update, delete, etc. methods, and your global variable remains confidential.

 private var sharedCoreDataManager: CoreDataManager! class CoreDataManager { let managedContext: NSManagedObjectContext class var shared: CoreDataManager { return sharedCoreDataManager } class func initialize(context: NSManagedObjectContext) { sharedCoreDataManager = CoreDataManager(context: context) } private init(context: NSManagedObjectContext) { managedContext = context } func delete(entity: String, index: Int) -> Bool { var data = fetch(entity) if data != nil { managedContext.deleteObject(data![index]) data!.removeAtIndex(index) managedContext.save(nil) return true } return false } func fetch(entity: String) -> [NSManagedObject]? { var request = NSFetchRequest(entityName: entity) var error: NSError? if let entities = managedContext.executeFetchRequest(request, error: &error) as? [NSManagedObject] { if entities.count > 0 { return entities } } return nil } func save(entity: String, _ attributes: [String: AnyObject]) -> NSManagedObject? { var entity = NSEntityDescription.entityForName(entity, inManagedObjectContext: managedContext) let object = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: self.managedContext) for (key, attr) in attributes { object.setValue(attr, forKey: key) } var error: NSError? if !managedContext.save(&error) { return nil } return object } } 

This can be initialized inside your function AppDelegate didFinishingLaunchingWithOptions

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { CoreDataManager.initialize(self.managedObjectContext!) return true } 

You can configure NSManagedObject by clicking YourProject.xcdatamodeld in the navigator. In your case, you add a Job object with the attributes salary (double) and date (date). From the main menu, choose Editor> CreateNSManagedObjectSubclass to automatically generate a subclass of Work. While you are still in the xcdatmodel editor, open the right-most panel - you should see text fields for "Name" and "Class". Be sure to change your class to "ProjectName.Name" - in your case, "ProjectName.Job" - or you will not be able to instantiate the new NSManagedObject class.

Your NSManagedObject class should be automatically created for you and available for verification in the project navigator. It will look like this:

 import Foundation import CoreData @objc class Job: NSManagedObject { @NSManaged var salary: NSNumber @NSManaged var date: NSDate } 

To limit access to your managed objects, you must create intermediary classes with the get- and set-style variables. Although Swift does not have a β€œsecure” access level, you can keep your NSManagedObjects private and allow access through object intermediaries by grouping them into a single class file:

 class ManagedObjectMediator<T: NSManagedObject> { private var managedObject: T! init?(_ type: String, attributes: [String: AnyObject]) { if let newManagedObject = CoreDataManager.shared.save(type, attributes) { managedObject = newManagedObject as T } else { return nil } } } class JobMediator<T: Job>: ManagedObjectMediator<Job> { var date: NSDate { return managedObject.date } var salary: NSNumber { return managedObject.salary } init?(attributes: [String:AnyObject]) { super.init("Job", attributes: attributes) } } 
+1
source

Source: https://habr.com/ru/post/1214901/


All Articles