Populating an Array from Core Data in Xcode - ios

I am trying to populate an NSArray with a collection of data I get from CoreData. But my array doesnt seem to be populating with the data. I have the following code to retrieve the data:
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]
initWithEntityName:#"WeightLog"];
self.contactarray = [[managedObjectContext executeFetchRequest:fetchRequest
error:nil] mutableCopy];
And I am using the following for loop to populate the NSArray with the data I collect from WeightLog for a particular field.
for (int i =0; i<=self.contactarray.count; i++) {
NSManagedObject *device = [self.contactarray objectAtIndex:i];
[titleNames addObject:device];
}
Just so you know contactarray is a property in my .h file of the following format:
#property (strong) NSMutableArray *contactarray;
Can you tell me where I am going wrong please, I am fairly new to iOS Development, if it doesn't show.
Thank you in advance

Initialise titleNames array before use. Try this,
titleNames = [[NSMutableArray alloc] init];
for (int i =0; i<=self.contactarray.count; i++) {
NSManagedObject *device = [self.contactarray objectAtIndex:i];
[titleNames addObject:device];
}

Just call this user-defined method. for ex -
self.titleNames = [self selectAllRowInEntity:#"WeightLog"];
-(NSArray *) selectAllRowInEntity:(NSString *) entityName
{
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fRequest;
NSEntityDescription *eDesc;
NSArray *arr;
fRequest = [[NSFetchRequest alloc] init];
eDesc = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
[fRequest setEntity:eDesc];
arr = [managedObjectContext executeFetchRequest:fRequest error:nil];
return arr;
}

This line here:
self.contactarray = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
This is a cardinal sin in Core Data - to not use the provided error parameters.
NSError *error;
self.contactarray = [[managedObjectContext executeFetchRequest:fetchRequest
&error] mutableCopy];
if (!self.contactArray) {
// Fetch Requests return a nil on error, in which case you should check the error.
NSLog(#"Error occurred: %#", error);
} else {
// do whatever you want with the array
}
Now run your code and look at the console and you might see the reason for the error.
Edited to add
Following a comment:
You should always check that the return of the method is nil before evaluating the error object. For Cocoa (and Cocoa-Touch) methods this is the only time that the error parameter is guaranteed to be valid.
This is taken from the Error Handling Programming Guide

Related

Try fetch record from multiple entity

i will try to work with multiple entity with core data.
core data in two entity name is Student and Detail both have inverse relationship. relationship name is Student -> Detail:detail Detail -> Student:student.
try to fetch record from both entity and display in table view. try multiple code but can't get result.
first code:
Detail *d;
NSMutableArray *arrObj = [[NSMutableArray alloc]init];
for(Student *tblObj in [d student]){
[arrObj addObject:tblObj];
}
NSLog(#"Your records related with tableA = %#",arrObj);
can't load data in array.
second code:
NSArray* fetchResults = [_mainContext executeFetchRequest:fetchRequest error:nil];
for (int i; i > 1; i++) {
Student *tableAObject = fetchResults[i];
NSString * itemcode = tableAObject.detail.email;
NSLog(#"%#",itemcode);
}
can't display record.
third way:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *aEntity = [NSEntityDescription entityForName:#"Student" inManagedObjectContext:_mainContext];
[fetchRequest setEntity:aEntity];
NSString *relationshipKeyPath = #"detail"; // Set this to the name of the relationship on "SP" that points to the "Code" objects;
NSArray *keyPaths = [NSArray arrayWithObject:relationshipKeyPath];
[fetchRequest setRelationshipKeyPathsForPrefetching:keyPaths];
NSMutableArray *arrObj = [[NSMutableArray alloc]init];
for(Student *spObj in arrObj)
{
NSLog(#"description is %#",spObj.name);
Detail *codObj = spObj.detail;
NSLog(#"it has value %#",codObj);
NSLog(#" unique name is %#",codObj.email);
}
Code for insert data in core dta:
- (IBAction)submitData:(id)sender {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate manageObjectContext];
Student *student = (Student *)[NSEntityDescription insertNewObjectForEntityForName:#"Student" inManagedObjectContext:context];
student.name = _nameText.text;
student.number = _noText.text;
student.study = _studyText.text;
Detail *detail = (Detail *)[NSEntityDescription insertNewObjectForEntityForName:#"Detail" inManagedObjectContext:context];
detail.number = _noText.text;
detail.email = _emailText.text;
detail.address = _addressText.text;
detail.contact = _contactText.text;
student.detail = detail;
detail.student = student;
NSError *error = nil;
[context save:&error];
if (![context save:&error]) {
NSLog(#"error in adding data %#, %#", error, [error userInfo]);
abort();
}
}
if you need more details let me know. Thank you.
The first two ways you have tried won't work as there is no code that related to fetch the data from core data. In the third way problem is you are not executing your fetch request.
Following is the way to fetch data from core data.
NSFetchRequest *req = [[NSFetchRequest alloc] initWithEntityName:#"Student"];
NSError *error = nil;
NSArray *students = [context executeFetchRequest:req error:&error];
for (Student *stdObj in students)
{
//Student object
NSLog(#"Name %#",stdObj.name);
NSLog(#"Number %#",stdObj.number);
//Detail object related to student
Detail *detailObj = stdObj.detail;
NSLog(#"Email %#",detailObj.email);
NSLog(#"Address %#",detailObj.address);
}

How to get single attribute Value of entity in Coredata?

I am having Entity Called EY_Appliances with Attributes applianceId ,applianceName,watts,amountPerWatts.Also i am Having Arrays like this:
Prefrence.h
#define APPLIANCENAME_ARRAY #[#"Fridge",#"Microwave",#"Mixie",#"Roti Maker",#"Coffee Machine",#"Dish Washer",#"Wet Grinder",#"Electric Stove",#"Ceiling Fan",#"TV",#"Table Fan",#"Tubelight",#"Bulb",#"AC",#"Vacuum Cleaner",#"CFL",#"LED",#"Washing Machine",#"Toaster",#"Room Heater",#"Iron",#"Motor",#"Water Heater",#"Inverter / UPS",#"Air Cooler",#"Steamer / Air Fryer",#"Hair Dryer",#"Laptop",#"PC",#"Tablet",#"Router / Modem",#"Home Theatre",#"Projector",#"PS3/PS4/XBOX"]
#define WATTS_ARRAY #[#"120",#"1000",#"700",#"30",#"167",#"810",#"180",#"150",#"75",#"120",#"75",#"40",#"60",#"1200",#"1400",#"20",#"6",#"300",#"1000",#"1600",#"1400",#"2400",#"1000",#"67",#"173",#"585",#"1026",#"15",#"150",#"4",#"4",#"17",#"240",#"10"]
DataAccessHandler.m
-(void)storeApplianceDetailsToEntity
{
NSManagedObjectContext *context = [self managedObjectContext];
for (int i=0; i<APPLIANCENAME_ARRAY.count; i++)
{
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:#"EY_Appliances" inManagedObjectContext:context];
[object setValue: APPLIANCENAME_ARRAY[i] forKey:#"applianceId"];
[object setValue: APPLIANCENAME_ARRAY[i] forKey:#"applianceName"];
[object setValue: WATTS_ARRAY[i] forKey:#"watts"];
[object setValue: WATTS_ARRAY[i] forKey:#"amountPerWatts"];
}
NSError *error = nil;
if (![context save:&error])
{
NSLog(#"Saving Failed with error %#", error);
}
NSLog(#"entityValue==>%#",context);
}
-(NSArray *) fetchApplianceDetailsFromEntity:(NSString *) entityName
{
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest;
NSEntityDescription *entityDescription;
NSArray *array;
fetchRequest = [[NSFetchRequest alloc] init];
entityDescription = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entityDescription];
array = [managedObjectContext executeFetchRequest:fetchRequest error:nil];
NSLog(#"arr==>>%#",array);
return array;
}
ViewController.m
-(void)viewDidLoad
{
DataAccessHandler *dataAccess=[[DataAccessHandler alloc]init];
[dataAccess storeApplianceDetailsToEntity];
NSArray *applianceData = [dataAccess fetchApplianceDetailsFromEntity:#"EY_Appliances"];
//How to print the applianceName,If i write Like this applianceData.applianceName,it shows error…
}
1.How to print the applianceName?
2.How to set the applianceId primaryKey and store the value for applianceId 1 to 34?
NSDictionary *applianceData = [dataAccess fetchApplianceDetailsFromEntity:#"EY_Appliances"];
use keyword get back value
applianceData is an array, so you need to access it through an index
Your code just now is using the appliance name as ID - if all you need is an identifier, you can use your index i
The load returns everything into the array - if you only want to load part of the dataset, you need to add a fetch predicate in fetchApplianceDetailsFromEntity
[fetch setPredicate:[NSPredicate predicateWithFormat:#"(applianceId == %#)",idString]];

Core Data Lazy Loading with NSPrivateQueueConcurrencyType and custom setter not working

Problem: Fetching a managed object using a background thread does not lazy load the NSManaged object relationship correctly when the NSManaged object that is related has a custom setter. Doing fetch on main thread with main concurrency type works without a problem. Why is this?
Work Around: If I create a custom getter on the relationship object and check for nil, I can force the NSManaged object to load by calling other variables that don't have custom setter methods.
Background
The core data layout is pretty simple. I have a Game managed object and a Turn managed object. The turn object is a one to one relationship with the game object. I always fetch the game object in order to access the turn object. TurnImp and GameImp are implementation classes that inherit from the Game and Turn object so I don't put getter/setter methods in auto generated code.
Code
The Fetch
//
//Stick command on background
//
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
//
//Load Game
//
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
CoreDataHelper *coreDataHelper = appDelegate.coreDataHelper;
NSManagedObjectContext *childMOC = [coreDataHelper createChildManagedObjectContext];
//the request
NSFetchRequest *fetchRequest = [NSFetchRequest new];
//the object entity we want
NSEntityDescription *entity = [NSEntityDescription entityForName:GAMEIMP_GAME inManagedObjectContext:childMOC];
[fetchRequest setEntity:entity];
//the predicate rules, the what
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"gameId == %#", #"1404110671234567"];
[fetchRequest setPredicate:predicate];
//the sorting rules
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:GAMEIMP_OBJECT_ID ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
//Fetch results
NSFetchedResultsController *resultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:childMOC sectionNameKeyPath:nil cacheName:nil];
NSError *error;
BOOL success = [resultsController performFetch:&error];
GameImp *game;
if (success) {
game = [resultsController.fetchedObjects objectAtIndex:0];
} else {
NSLog(#"Unable to get game. Error: %#", error);
}
TurnImp *turnImp = game.turn;
//Issue is here!!! Should be 3, instead 0 because lastRoundReward is nil.
int lastRoundReward = [turnImp.lastRoundReward intValue];
//Work around, call custom getter method. Now 3 is returned.
lastRoundReward = [turnImp getLastRoundReward];
}
This childMOC creation
-(NSManagedObjectContext*) createChildManagedObjectContext {
NSManagedObjectContext *childMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childMOC.parentContext = self.mainManagedObjectContext;
return childMOC;
}
TurnImp Header
#interface TurnImp : Turn
#property(atomic) BOOL isValid;
- (void) setLastRoundReward: (int) lastRoundReward;
- (int) getLastRoundReward;
#end
TurnImp M
#implementation TurnImp
#synthesize isValid;
#synthesize lastRoundReward = _lastRoundReward;
/**
* Set the last round reward
* #param -
* #return -
*/
- (void) setLastRoundReward: (int) lastRoundReward {
_lastRoundReward = [NSNumber numberWithInt:lastRoundReward];
}
/**
* Get the int value of lastRoundReward
*/
- (int) getLastRoundReward {
//Note - HACK! Lazy loading not working, try another member
if (self.lastRoundReward == nil) {
//Force load
NSString *objectId = self.objectId;
}
return [self.lastRoundReward intValue];
}
Change childMoc to mainMoc and it works. MainMoc Code
//create the main MOC
_mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
More After Fixed Concurrency issue
[childMOC performBlock:^{
// Execute the fetch on the childMOC and do your other work.
NSError *error;
NSArray *results = [childMOC executeFetchRequest:fetchRequest error:&error];
if (results == nil) {
// Handle error
} else if (results.count == 1) {
GameImp *game = [results firstObject];
TurnImp *turnImp = game.turn;
//Issue is here!!! Should be 3, instead 0 because lastRoundReward is nil.
int lastRoundReward = [turnImp.lastRoundReward intValue];
//Work around, call variable objectId (not same as ObjectId)
NSString *objectId = turnImp.objectId;
//not it's 3...
lastRoundReward = [turnImp.lastRoundReward intValue];
}
}];
Work Around
I removed the following from TurnImp and it works as expected with the relationships.
#synthesize lastRoundReward = _lastRoundReward;
First, I have to confess that I have no idea what your problem statement means - what is lazy loading of a relationship supposed to do anyway?
However, a quick glance at your code reveals that you are creating a MOC with NSPrivateQueueConcurrencyType yet you are not properly wrapping its use inside an appropriate performBlock invocation.
When you clearly violate the Core Data Concurrency guidelines, you are playing in dangerous waters and will get undefined behavior.
Also, why create an instance of NSFetchedResultsController just to perform a fetch? That's overkill. Simply use a fetch request. Like so...
[childMOC performBlock:^{
// Execute the fetch on the childMOC and do your other work.
NSError *error;
NSArray *results = [childMOC executeFetchRequest:fetchRequest error:&error];
if (result == nil) {
// Handle error
} else if (results.count == 1) {
GameImp *game = [results firstObject];
TurnImp *turnImp = game.turn;
int lastRoundReward = [turn2.lastRoundReward intValue];
}
}];

fetch coredata relationship

I would like to know how to fetch the items from my coredata relation ship. I guess it should be a dictionary or arrays or something that gets returned so that you can have your one to many thing.
I am quite lost at this I know how to write/read single objects but this relationship stuff is abit confusing.
I think I have sucsessfully written a relationship to coredata however now I would like to be able to read it to see if I have it right.. I have started writing the method for this but have no idea what to actually do to get all of the information out.
this is the code i have so far.. for both read and write
- (void)writeFin:(NSArray *)recivedProjectData ItemsData:(NSArray *)itemsData {
// WRITE TO CORE DATA
NSManagedObjectContext *context = [self managedObjectContext];
for (NSDictionary *dict in recivedProjectData) {
project = [NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:context];
project.proNumber = [dict valueForKey:#"ProNumber"];
project.projectDescription = [dict valueForKey:#"Description"];
// project.items = [dict valueForKey:#""]; // this is the relationship for project
}
for (NSDictionary *dict in itemsData) {
items = [NSEntityDescription insertNewObjectForEntityForName:#"Items" inManagedObjectContext:context];
items.Number = [dict valueForKey:#"Number"];
items.Description = [dict valueForKey:#"Description"];
items.comment = [dict valueForKey:#"Comment"];
items.project = project; // this is the relationship for items
[project addItemsObject:items];
}
NSError *error = nil;
if (![__managedObjectContext save:&error]) {
NSLog(#"There was an error! %#", error);
}
else {
NSLog(#"created");
}
}
- (NSMutableArray *)readFin {
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray *projectDictionaryArray = [[NSMutableArray alloc] init];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (ProjectList *projectList in fetchedObjects) {
NSMutableDictionary *tempProjectDictionaryArray = [[ NSMutableDictionary alloc] init];
[tempProjectDictionaryArray setObject:project.proNumber forKey:#"ProNumber"];
[tempProjectDictionaryArray setObject:project.description forKey:#"Description"];
[tempProjectDictionaryArray setObject:project.projectID forKey:#"ProjectID"];
[projectDictionaryArray addObject:tempProjectDictionaryArray];
}
return projectDictionaryArray;
}
So just o reiterate, I would like to know A, is my write method look okay? B, how do you fetch/read the relationship objects from core data.
any help would be greatly appreciated.
A relationship in Core Data isn't an object, its a property which happens to correspond to another object in your model rather than just being a dead end. You're already most of the way there as far as checking whether your relationships are ok as far as I can see -- what you need to do is add one more line in your projectList
[tempProjectDictionaryArray setObject: project.items forKey:#"items"];
the object that you will have added will be an NSSet. You can then check that things are as they should be with a loop like this after you've finished setting things up
NSSet itemsForProject = projectDictionaryArray[someIndex][#"items"]
for (Item* currItem in [itemsForProject allObjects]) {
//access some property of the current item to make sure you have the right ones -- \
description for example
NSLog(#"%#", item.description);
}

Managing core data objects with a data manager singleton

I have a set of items in a plist. When my app starts, I read in the plist and save it as an array in my DataManager singleton, like this:
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *itemDatapath = [path stringByAppendingPathComponent:#"ItemData.plist"];
NSDictionary *itemData = [NSDictionary dictionaryWithContentsOfFile:itemDatapath];
dataManager.items = [itemData objectForKey:#"Items"];
I also want to store the core data objects that are associated with this data in the DataManger, so I attempted this:
-(void)setItems:(NSArray *)_items //causes EXC_BAD_ACCESS error
{
self.items = _items;
NSManagedObjectContext *context = [self managedObjectContext];
for (NSDictionary *item in self.items)
{
NSManagedObject *itemObject = [NSEntityDescription
insertNewObjectForEntityForName:#"Item"
inManagedObjectContext:context];
[itemObject setValue:[NSNumber numberWithInteger:[[item valueForKey:#"id"] intValue]] forKey:#"identifier"];
[itemObject setValue:[UIImage imageNamed:[item valueForKey:#"image"]] forKey:#"image"];
...
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
The point being that anywhere in my app I can access the objects from this method:
-(NSArray*)fetchItems
{
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Item" inManagedObjectContext:managedObjectContext];
NSError *error2;
NSFetchRequest *itemFetchRequest = [[NSFetchRequest alloc] init];
[itemFetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"order"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[itemFetchRequest setSortDescriptors:sortDescriptors];
NSArray *fetchedItems = [managedObjectContext executeFetchRequest:itemFetchRequest error:&error2];
return fetchedItems;
}
The problem is the EXC_BAD_ACCESS error noted above. I would also like to know if there is a better way of going about this. I have the feeling storing the core data objects here is not the best practice. But even if I fetch the data when I need it in other view controllers, how can I manage updating the core data objects if they change? I have an external plist that may change, and the core data objects need to update based on that.
You are causing infinite recursion when you put self.items = _items inside the setItems: method. self.items is exactly the same as calling setItems - they invoke the same method. What you need to do instead is set the value of whatever your instance variable is - presumably items. So the first line of setItems: should be items = _items. That, in and of itself, is also confusing, as the convention is to have _ before variables indicate an instance variable.

Resources