Different ways to instantiate an NSManagedObject instance - core-data

As a CoreData beginner, I have so far found three different ways to instantiate an instance of an NSManagedObject:
1:
let myEntity = NSEntityDescription.entityForName("MyEntity", inManagedObjectContext: managedContext)!
let instance = MyEntity(entity: myEntity, insertIntoManagedObjectContext: managedContext)
instance.someProperty = someValue
try! managedContext.save()
2:
let myEntity = NSEntityDescription.entityForName("MyEntity",
inManagedObjectContext:managedContext)
let instance = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext)
instance.setValue(someValue, forKey: "someProperty")
try! managedContext.save()
3:
let instance = NSEntityDescription.insertNewObjectForEntityForName("MyEntity", inManagedObjectContext: managedContext) as! MyEntity
instance.somePropery = someValue
try! managedContext.save()
Are there any notable difference between these methods ?

First two methods identical. In both cases you using same NSManagedObject init method.
Documentation of NSEntityDescription.insertNewObjectForEntityForName(_ entityName: String, inManagedObjectContext context: NSManagedObjectContext) says that this method is "conceptually similar to" NSManagedObject.init(entity entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?)
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSEntityDescription_Class/#//apple_ref/occ/clm/NSEntityDescription/insertNewObjectForEntityForName:inManagedObjectContext:
So, you can use any method that convenience for you.

Related

CoreData:Relationship:Save:Query

I have EmployeeExample and Deptt as entities.
EmployeeExample one to one relationship with Dept
Dept has more than one relationship with EmployeeExample
I want to save data through relationship. I have dept entity object, with that I want to save employeeexample entity
I achieved this, but I want to know whether it is the optimal way. Any optimal way? I would like to know how the relationship works.
My code :
import UIKit
import CoreData
class ViewController: UIViewController {
var container: NSPersistentContainer? = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer
let appDelegate = UIApplication.shared.delegate as! AppDelegate
var empSet = NSSet()
var empS = Set<EmployeeExample>()
override func viewDidLoad() {
super.viewDidLoad()
var context:NSManagedObjectContext = (container?.viewContext)!
let dept = NSEntityDescription.insertNewObject(forEntityName: "Deptt", into: context) as! Deptt
let emp = NSEntityDescription.insertNewObject(forEntityName: "EmployeeExample", into: (container?.viewContext)!) as! EmployeeExample
emp.firstName = "YYYY"
emp.lastName = "HHHHHHH"
empS.insert(emp)
print("Count of Emp SSSS Set == \(empS.count)")
let emp1 = NSEntityDescription.insertNewObject(forEntityName: "EmployeeExample", into: (container?.viewContext)!) as! EmployeeExample
emp1.firstName = "RRRRR"
emp1.lastName = "YYYYY"
empS.insert(emp1)
empSet.addingObjects(from: empS)
dept.deptName = "CCC"
print("Count of Emp SSSS Set == \(empS.count)")
print("Count of Emp Set == \(empSet.count)")
dept.addToEmp(empSet)
do {
try appDelegate.saveContext()
print("Saved -------------")
}catch {}
}
}
Do I have to create an Employee instance each time?
Do I have to create an Employee instance each time?
Well, do you have a new employee each time? If you have new information, you need to create a new Employee record. If you're using existing information, you look up an existing Employee from your persistent store by using NSFetchRequest. You create new instances when you have new data to save. Whether you need to do that is something only you can answer. If you have new data, yes. Otherwise, no.

Ambiguous use of 'subscript'

xcode started complaining about the following code (worked fine w/o any issues for quite a few months):
let request = NSFetchRequest(entityName: entity)
request.returnsDistinctResults = true
let nsArr: NSArray = ["int_field", "date_field"]
request.propertiesToFetch = nsArr as [AnyObject]
request.resultType = .DictionaryResultType
let predicate = NSPredicate(format: "(val1 == %d) AND (val2 == %#)", argumentArray: [anIntegerValue, aStringValue])
request.predicate = predicate
do {
let results: NSArray = try moc.executeFetchRequest(request)
for result in results {
Up to this point all is fine. But when I try to extract data from a result, I get an 'Ambiguous use of 'subscript' error on the following:
let val1 = result["int_field"] as! Int
let val2 = result["date_field"] as! NSDate
Not sure what is happening, but if I replace the above syntax with:
let val1 = (result as! NSDictionary) ["int_field"] as! Int
let val2 = (result as! NSDictionary)["date_field"] as! NSDate
it works. What I am not clear about is why declaring the following is not sufficient (or what happened in ios 9.2 to cause this):
request.resultType = .DictionaryResultType
Thoughts?
Please use Swift native collection types, they are so much easier to maintain.
First of all, assign the array directly to propertiesToFetch. No NSArray, no [AnyObject]
request.propertiesToFetch = ["int_field", "date_field"]
Your result is an array of dictionaries which have String keys and Int / NSDate values. Multiple types must be represented by AnyObject.
do {
let results = try moc.executeFetchRequest(request) as! [[String:AnyObject]]
Now, since the compiler knows that the array contains dictionaries this will smoothly compile
for result in results {
let val1 = result["int_field"] as! Int
let val2 = result["date_field"] as! NSDate
}
}
Foundation collection types don't contain the type information and can cause those Ambiguous use messages.
After a close look, the proposed solution by Vadian did not work for me.
The only thing that worked was my original solution:
let val1 = (result as! NSDictionary) ["int_field"] as! Int
let val2 = (result as! NSDictionary)["date_field"] as! NSDate
It seems that the trigger was importing Realm framework (to address another dependency). All I can say is that when I removed Realm, the error/issue goes away.
This occurs despite me not actually using Realm at the moment.

CoreData insertNewObjectForEntityForName Causing Crash

I've been chasing a bug for a while now and I can't figure it out. I have a class that takes a bunch of parsed data and then calls a method to create new core data "Article" object for each element created from the parsed data. I've shown how I declare the NSManagedObjectContext below.
You will also see the method
Article.createFLOArticleWithStructure(element, inManagedObjectContext: self.articleContext)
I placed this method in an extension to clean up the code. The method is show below.
import Foundation
class FLODataHandler : NSObject, FLOConnectionHandlerDelegate, FLOParserHandlerDelegate, TriathleteParserHandlerDelegate, VeloNewsParserHandlerDelegate, CyclingNewsParserHandlerDelegate, RoadBikeActionParserHandlerDelegate, IronmanParserHandlerDelegate
{
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
lazy var articleContext: NSManagedObjectContext = self.appDelegate.managedObjectContext!
func floParserHandlerDidFinishParsing(parserHandler : FLOParserHandler)
{
for element in self.floParserHandler!.XMLParsedDataArray!
{
// The pubDate, tilte and link and indicator have been added to the titleLinkArray. I will now add the data to a Core Data Entity
// in the Article+NewsFeedArticle class.
Article.createFLOArticleWithStructure(element, inManagedObjectContext: self.articleContext)
}
}
Extension Code
extension Article
{
class func createFLOArticleWithStructure(articleStructure: DateTitleLink, inManagedObjectContext context: NSManagedObjectContext) -> (Article)
{
// Core data is much simpler in Swift. I have not commented this code since I do not know if it's working yet.
var article = Article()
//var entity = NSEntityDescription("Article", inManagedObjectContext: context)
let fetchRequest = NSFetchRequest(entityName: "Article")
let predicate = NSPredicate(format: "feed == 'FLO Cycling' AND title == %#", articleStructure.title!)
let sortDescriptor = NSSortDescriptor(key: "pubDate", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
// Set up NSError
var fetchError : NSError?
// When you perform a fetch the returned object is an array of the Atricle Entities.
let fetchedObjects = context.executeFetchRequest(fetchRequest, error: &fetchError) as! [Article]
if fetchedObjects.count > 1
{
println("Error! in Article+NewFeedArticle.swift")
}
else if fetchedObjects.count == 0
{
article = NSEntityDescription.insertNewObjectForEntityForName("Article", inManagedObjectContext: context) as! Article
article.feed = "FLO Cycling"
article.pubDate = articleStructure.date!
article.title = articleStructure.title!
article.link = articleStructure.link!
article.theNewArticle = NSNumber(int: 1)
var error : NSError?
if(context.save(&error))
{
println(error!.localizedDescription)
}
}
else if fetchedObjects.count == 1
{
article = fetchedObjects[fetchedObjects.endIndex - 1]
}
return article
}
When I run the code I get stopped on the following line of the extension code and receive the following errors.
article = NSEntityDescription.insertNewObjectForEntityForName("Article", inManagedObjectContext: context) as! Article
CoreData: error: Failed to call designated initializer on
NSManagedObject class 'FLOCycling1_1_1.Article' CoreData: warning:
Unable to load class named 'Article' for entity 'Article'. Class not
found, using default NSManagedObject instead. Could not cast value of
type 'NSManagedObject_Article_' (0x7fe59c3515e0) to
'FLOCycling1_1_1.Article' (0x106139f70).
I've read online about using a prefix in the data model. I've added this to no avail. If you have any idea how I can fix this error I would appreciate the help.
ADDED****
Here is the Article.swift file on request.
import Foundation
import CoreData
#objc class Article: NSManagedObject
{
#NSManaged var feed: String
#NSManaged var link: String
#NSManaged var pubDate: NSDate
#NSManaged var theNewArticle: NSNumber
#NSManaged var title: String
}
Take care,
Jon
The issue had to do with my declaration of Article in this line here.
var article = Article()
In the Swift version calling the line of code above allocates and initializes memory for the Article. In the Objective-C version of my code I called the following.
Article *article = nil;
While I am not sure why, allocating and initializing the Article is the issue. I found this post here about a similar error.
Failed to call designated initializer on NSManagedObject class 'ClassName'
To fix this I changed the code to
var article = Article?()
To be clear, I also changed the Article class to ProjectName.Article.
I hope this helps someone else.
Take care,
Jon

Fetching the most recent item from CoreData in Swift

I'm new to CoreData, have read a few tutorials but have come up empty-handed. Most tutorials have centered around fetching from CoreData to populate a tableview, which is not what I'm doing at the moment.
Here's how I'm setting the data in CoreData. All types are Double except for date which is an NSDate
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let managedObjectContext = appDelegate.managedObjectContext
let entityDescription = NSEntityDescription.entityForName("Meditation", inManagedObjectContext: managedObjectContext!)
let meditation = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext!)
meditation.setValue(settings.minutes, forKey: "length")
meditation.setValue(settings.warmup, forKey: "warmup")
meditation.setValue(settings.cooldown, forKey: "cooldown")
meditation.setValue(NSDate(), forKey: "date")
// fetch stuff from CoreData
var request = NSFetchRequest(entityName: "Meditation")
var error:NSError? = nil
var results:NSArray = managedObjectContext!.executeFetchRequest(request, error: &error)!
for res in results {
println(res)
}
Here's what I'm trying to do to get the results, but I'm not able to access things like .minutes, .date, etc. I know I'm also not properly getting the last item either, I was just trying to print out attributes on the object first.
I'd love help on how to fetch only the most recent object as well as show it's attributes
Thanks!
First, create a "Meditation.swift" file in Xcode with "Editor -> Create NSManagedObject
Subclass ...". The generated file should look like
import Foundation
import CoreData
class Meditation: NSManagedObject {
#NSManaged var length: NSNumber
#NSManaged var warmup: NSNumber
#NSManaged var cooldown: NSNumber
#NSManaged var date: NSDate
}
Now you can use the properties directly instead of Key-Value Coding
and create the object as
let meditation = NSEntityDescription.insertNewObjectForEntityForName("Meditation", inManagedObjectContext: managedObjectContext) as Meditation
meditation.length = ...
meditation.warmup = ...
meditation.cooldown = ...
meditation.date = NSDate()
var error : NSError?
if !managedObjectContext.save(&error) {
println("save failed: \(error?.localizedDescription)")
}
When fetching the object, cast the result of
executeFetchRequest() to [Meditation]:
let request = NSFetchRequest(entityName: "Meditation")
var error : NSError?
let result = managedObjectContext.executeFetchRequest(request, error: &error)
if let objects = result as? [Meditation] {
for meditation in objects {
println(meditation.length)
println(meditation.date)
// ...
}
} else {
println("fetch failed: \(error?.localizedDescription)")
}
Finally, to fetch only the latest object, add a sort descriptor to
sort the results by date in descending order, and limit the number of results to one:
let request = NSFetchRequest(entityName: "Meditation")
request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
request.fetchLimit = 1
// ...
Then the objects array contains at most one object, which is the most recent one.

SWIFT - 'AnyObject' does not have a member name 'fooBar'

I am trying to fetch some data from Core Data and have run into a slight problem. I can fetch the data with no problem. The moment I try to grab a specific piece of data (i.e. data.fooBar), it throws up an error:
"'AnyObject' does not have a member name 'fooBar'
If I println(data) it will show that fooBar does exist with data stored in it.
I am not really sure why it is doing this. I have tried to search for an answer and tried a bunch of different things but none have seemed to work. Any help would be great. Thanks. :)
var results : Array<AnyObject> = []
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
//get the data for that storedItem
var appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
var context:NSManagedObjectContext = appDel.managedObjectContext!
let req = NSFetchRequest(entityName: "storedItems")
let name:String = results[indexPath.row].name
req.predicate = NSPredicate(format: "name == %#", name)
req.returnsObjectsAsFaults = false
var tapResults = context.executeFetchRequest(req, error: nil)!
for item in tapResults {
println(item) //works, shows all data correctly(including subText)
println(item.name) //works, the only one that does for some reason???
println(item.subText) //Error 'AnyObject' does not have a member name 'subText'
}
Here is the result for: println(item)
println(item) <NSManagedObject: 0x7f04be60> (entity: storedItems; id: 0x7f041de0 <x-coredata://DD4F8E68-2234-46B5-B1D8-AE2F75245C63/storedItems/p1> ; data: {
alarmSound = default;
isDefault = 0;
name = "test";
sliderHours = 0;
sliderMinutes = 0;
sliderSeconds = 0;
subText = "00:00:00";
UPDATE: Based on discussion over vacawama answer (Thank you Aaron). For correct solution please see the answer I accepted.
my itemObj class
#objc(itemObj)
class itemObj: NSManagedObject {
#NSManaged var name:String!
#NSManaged var sliderHours:NSNumber
#NSManaged var sliderMinutes:NSNumber
#NSManaged var sliderSeconds:NSNumber
#NSManaged var subText:String!
#NSManaged var alarmSound:String!
#NSManaged var isDefault:NSNumber
}
my AddItem VC:
var tResults = (context.executeFetchRequest(req, error: nil))
for item in tResults as [itemObj!] {
println(item.name)
println(item.subText)
}
executeFetchRequest returns an optional array of AnyObject. You shouldn't force-unwrap it (this can cause a crash). So optionally unwrap it and do an optional cast (as?) to make sure the type is correct:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext!
let req = NSFetchRequest(entityName: "storedItems")
let name:String = results[indexPath.row].name
req.predicate = NSPredicate(format: "name == %#", name)
req.returnsObjectsAsFaults = false
let tapResults = context.executeFetchRequest(req, error: nil)
if let presentResults = tapResults {
if let castedResults = presentResults as? [MyManagedObjectSubclass] {
for item in castedResults {
println(item)
println(item.name)
println(item.subText)
}
}
}
}
I also changed all of your vars to lets since they don't need to be mutable.
Just replace MyManagedObjectSubclass with whatever your NSManagedObject subclass is.

Resources