Get entity by name and create relationship using CoreData - ios

I'm trying to create a relation between two entities Mensagem and Categories, but i'm getting the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-many relationship: property = "mensagemParaCategorias"; desired type = NSSet; given type = __NSArrayI; ...
).'
What i tried to do was: Create a entity property (categoriaAtual), then try to get the entity by name and them set the entity to the fetched results and them create the relationship .
Here is my code.
createInitialDate.h
#import <Foundation/Foundation.h>
#import "Mensagens.h"
#import "Categorias.h"
#interface createInitialData : NSObject
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic) Categorias *categoriaAtual;
-(void)createMessage:(NSString *)mensagem ComAutor:(NSString *)autor;
CreateInitialData.m
-(void)createMessage:(NSString *)mensagem ComAutor:(NSString *)autor {
Mensagens *novaMensagem = (Mensagens *)[NSEntityDescription insertNewObjectForEntityForName:#"Mensagens" inManagedObjectContext:self.managedObjectContext];
[novaMensagem setMensagem:mensagem];
[novaMensagem setAutor:autor];
NSDate *agora = [NSDate date];
[novaMensagem setDataCriada:agora];
[self categoria:#"Posts"];
novaMensagem.mensagemParaCategorias = _categoriaAtual; // This is where i try to create the relation between the 2 entities.
NSError *error = nil;
NSManagedObjectContext *context = self.managedObjectContext;
if (![context save:&error]) {
NSLog(#"Error! %#", error);
}
}
-(void)categoria:(NSString *)nomeCategoria {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Categorias" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"categoria == '%#'", nomeCategoria];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [[self managedObjectContext]executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"Problem! %#", error);
} else {
NSLog(#"Objetos %#", fetchedObjects);
_categoriaAtual = fetchedObjects;
}
}

mensagemParaCategorias
is a to-many relationship but you are assigning a pointer to an object that is not an instance of NSSet. Xcode has an option to generate methods on your managed object subclasses for adding objects to a to-many relationship.

Related

How to save NSArray data in coredata?

I want to save my data in coredata from NSArray. How can I achieve this ?
NSArray *fetchdata = [Lockbox arrayForKey:#"fetch"];
Here, I am getting data in below form:
How can I save this data in NSManageobject?
Here is my try:
NSFetchRequest *fetchrequestforside=[NSFetchRequest fetchRequestWithEntityName:#"demo"];
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *newDevice1 = [NSEntityDescription
insertNewObjectForEntityForName:#"demo"
inManagedObjectContext:context];
NSArray *fetchdata = [Lockbox arrayForKey:#"fetch"]; //
for (int i =0;i<[fetchdata count];i++){
NSString *Name = [[fetchdata objectAtIndex:i] objectForKey:#"name"];
NSString *Websitename = [[fetchdata objectAtIndex:i] objectForKey:#"websitename"];
NSString *Feedlink = [[fetchdata objectAtIndex:i] objectForKey:#"feedurl"];
NSLog(#"value:%#,%#,%#",Name,Websitename,Feedlink);
[newDevice1 setValue:Name forKey:#"name"];
[newDevice1 setValue:Websitename forKey:#"websitename"];
[newDevice1 setValue:Feedlink forKey:#"feedurl"];
NSLog(#"value:%#,%#,%#",Name,Websitename,Feedlink);
// Save the context.
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
But with this technique I am getting an error. How can I resolve this problem?
EDIT:-
I am getting this error:
*** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[__NSCFString objectForKey:]:
unrecognized selector sent to instance 0x7ff262520660'
Your code treats the contents of the fetchdata array as instances of NSDictionary, but, LockBox only appears to support string contents:
-(NSArray *)arrayForKey:(NSString *)key
{
NSArray *array = nil;
NSString *components = [self objectForKey:key];
if (components)
array = [NSArray arrayWithArray:[components componentsSeparatedByString:kDelimiter]];
return array;
}
So, you need to deal with the conversion. You're probably part dealing with it already to store the original array...
But, that's why you get the error, the code expects a dictionary but actually gets a string
Do you have an object that looks like this?
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface demo : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * websitename;
#property (nonatomic, retain) NSString * feedurl;
#end
After creating an object like this (using insertNewObjectForEntityForName) try to fetch it like that:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
Btw, I would recommended you to stick to the naming conventions and call it Demo.
Cheers
I advice you to use the awesome library MagicalRecord
it save much time and very easy
here its tutorial
MagicalRecord Tutorial

One-to-Many Relationship: CoreData

I'm fairly new to coredata and have been stuck on an issue.
Any assistance will be greatly appreciated.
Information:
I have a core data app with to entities;
List and Task.
List and Task have a one-to-many relationship.
Task.h
#class List;
#interface Task : NSManagedObject
#property (nonatomic, retain) NSString * task;
#property (nonatomic, retain) NSString * note;
#property (nonatomic, retain) List *list;
#end
List.h
#class Task;
#interface List : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSDate * dateCreated;
#property (nonatomic, retain) NSNumber * sortOrder;
#property (nonatomic, retain) NSSet *task;
#end
#interface List (CoreDataGeneratedAccessors)
- (void)addTaskObject:(Task *)value;
- (void)removeTaskObject:(Task *)value;
- (void)addTask:(NSSet *)values;
- (void)removeTask:(NSSet *)values;
#end
I create lists using;
NSManagedObjectContext *context = [self managedObjectContext];
if ([self.listTextField.text length] == 0) { // Quit here if no text is entered
[self dismissViewControllerAnimated:YES completion:nil];
return;
}
// Create a new list.
// Create an NSManagedObject for our database entity.
list = [NSEntityDescription insertNewObjectForEntityForName:#"List" inManagedObjectContext:context];
// Add the new task to the object (which in turns adds to our database).
[list setValue:self.listTextField.text forKey:#"name"];
// Get current date and time.
NSDate *todayDate = [NSDate date];
// Add the date to the object (which in turns adds to our database).
[list setValue:todayDate forKey:#"dateCreated"];
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
I can update already created lists using the code below with cellaccessorybuttontapped;
NSManagedObjectContext *context = [self managedObjectContext];
if ([self.listTextField.text length] == 0) {
// Delete object from database.
[context deleteObject:self.list];
NSError *error = nil;
// Save the action to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
} else {
// Update existing task.
[self.list setValue:self.listTextField.text forKey:#"name"];
// Get current date and time.
NSDate *todayDate = [NSDate date];
// Add the date to the object (which in turns adds to our database).
[list setValue:todayDate forKey:#"dateCreated"];
NSError *error = nil;
// Save the action to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
[self dismissViewControllerAnimated:YES completion:nil];
I can then navigate into a list.
My question is how can I then create a task for the list I have just navigated into?
It's been 2 days and I've not been able to find anything on Google.
As advised I have added;
#property (strong, nonatomic) List *selectedList;
I now have this as my Save method
NSManagedObjectContext *context = [self managedObjectContext];
// Saving a new task.
Task *task = [NSEntityDescription insertNewObjectForEntityForName:#"Task" inManagedObjectContext:context];
task.task = self.taskText.text;
task.note = self.noteText.text;
task.list = self.selectedList;
NSLog(#"The selected list is: %#", [self.selectedList description]);
NSError *error = nil;
// Save the object to persistent store
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
[self.selectedList addTaskObject:task];
[self dismissViewControllerAnimated:YES completion:nil];
The new task is created but it is created in all lists.
Is it possible that this is working and I'm not fetching tasks based on their list?
This is my fetch request when I navigate into a list:
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Create a fetch request.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Create an entity so fetch the data from.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Task" inManagedObjectContext:self.managedObjectContext];
// Set the entity of the fetch request.
[fetchRequest setEntity:entity];
// Set the amount to be fetched at a time
[fetchRequest setFetchBatchSize:20];
// Create a sort descriptor.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"task" ascending:NO];
// Attach the sort descriptor to the fetch request.
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create a fetch result controller using the fetch request
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
theFetchedResultsController.delegate = self;
// Perform fetch.
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Handle error.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
return fetchedResultsController;
That is what these methods in your entity header are for:
- (void)addTaskObject:(Task *)value;
- (void)removeTaskObject:(Task *)value;
- (void)addTask:(NSSet *)values;
- (void)removeTask:(NSSet *)values;
You'll create a new task entity:
Task *t = [NSEntityDescription insertNewObjectForEntityForName:#"Task" inManagedObjectContext:context];
Then complete the fields in it as you did with your list object:
t.task = #"Whatever";
t.note = #"Whatever Note";
t.list = currentlySelectedListItem; // whatever that happens to be -- it will be a (List *)something
Then, you want to add the task object to the list:
[currentlySelectedListItem addTask:t];
Then save the context & you're done.
Key thing here is that you're effectively updating the List object by adding a task to the set of Task values contained in the NSSet. And t.list is going to contain a pointer to the parent List object.
Looks to me like you have it laid out just fine (I'm assuming the:
#property (nonatomic, retain) List *list;
Is a relationship to the parent List and not just another value you have defined; that looks to be the case).
You need to keep track of the list that you have navigated into. One way of doing it would be to create a property called as parentList like so
#property(nonatomic,strong) List *parentList
in the view controller you create a task in. And just before navigating to the view controller set this property.
In the task view controller you do a insert similar to the List object using Task *reqdTask = [NSEntityDescription insertNewObjectForEntityForName:#"Task" inManagedObjectContext:context]; and then set all the values once say Save button is pressed.
[parentList addTaskObject: reqdTask];
and ur done. This will create a Task in the task entity and map it to the List entity. Hope this helps.
**EDIT***
You need to do this [parentList addTaskObject: reqdTask]; before saving your context.
Add this in the NSFectResultsController
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"list = %#",self.parentList]];
so it will be something like this
// Create a fetch request.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Create an entity so fetch the data from.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Task" inManagedObjectContext:self.managedObjectContext];
// Set the entity of the fetch request.
[fetchRequest setEntity:entity];
// Set the amount to be fetched at a time
[fetchRequest setFetchBatchSize:20];
// Create a Predicate.
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"list = %#",self.parentList]];
//continue
this will bring the task associated with the selected list.

Core Data Fetch doesn't return the object it should

In Core Data, I have a Property entity, with an attribute currentPage of type Integer 16. It is associated with a class ATProperty with a scalar property:
#interface ATProperty : NSManagedObject
#property(nonatomic, assign) int16_t currentPage;
#end
#implementation ATProperty
#dynamic currentPage;
#end
Now I have the following code to perform a request:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Property"];
request.predicate = [NSPredicate predicateWithFormat:#"currentPage != 0"];
NSArray *results = [context executeFetchRequest:request error:nil];
Most of the times, it works as intended. But sometimes, I get the following weird results:
(lldb)po results
<__NSArrayI 0x17e61300>(
)
(lldb)po [context executeFetchRequest:[NSFetchRequest fetchRequestWithEntityName:#"Property"] error:nil]
<_PFArray 0x17e9f3e0>(
<ATProperty: 0x17ec6400> (entity: Property; id: 0x19178e20 <x-coredata:///Property/t88822504-5974-42F5-B8D2-A62325064AFF28> ; data: {
currentPage = 9;
})
)
(lldb)p (BOOL)[request.predicate evaluateWithObject:0x17ec6400]
(BOOL) $12 = YES
So there is one instance of the entity in the managed object context. When I execute my fetch request, Core Data doesn't return this object, it returns an empty array. However, if I test the same predicate on the object that exists in the context, the predicate evaluates to YES. Which means the object should have been in the results. How is that possible?
Can you please try your code simulation with below. It's really work for me and also helps us to restrict from crash and other result issues.
NSManagedObjectContext *managedObjectContext;
NSEntityDescription *entityDesc = [NSEntityDescription
entityForName:#"Property"
inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSPredicate *pred =
[NSPredicate predicateWithFormat:#"currentPage != 0"];
[request setPredicate:pred];
NSManagedObject *matches = nil;
NSError *error;
NSArray *objects = [context executeFetchRequest:request
error:&error];
if ([objects count] > 0) {
NSLog();
}
Hope it helps. Please let me know if we have to go with another solution.

NSManagedObject fault / cannot get 'primitive' object handled by NSManagedObject

I am struggling with CoreData. I'd like to fetch the following object:
#interface Email : NSManagedObject {
}
#property (nonatomic, copy) NSString *email;
#property (nonatomic, copy) NSString *contact;
#end
..and put the result inside a NSMutableArray, but the NSString contents, (not NSManagedObjects!). This is because I am using json-framework and that engine does not allow NSManagedObjects to be passed.
These lines fetch perfom the fetch from CoreData
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Emails" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:#"email"]];
NSError *error = nil;
NSMutableArray *fetchedObjects = [[NSMutableArray alloc] init];
fetchedObjects= [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
flight.emails=fetchedObjects;
The 'flight' object is declared as follows:
#interface Flight : NSObject {
NSMutableArray *_emails;
}
#property (nonatomic, retain) NSMutableArray *emails;
With this code I am getting CoreData faults. I tried some other implementation variants, but never actually managed to have NSString objects in flight.emails, but always NSManagedObjects. What I tried was to implement a dedicated getter function in the Email NSManagedObject that copies the fetched NSString and returns the copy.
I get the idea that this is kind of a common problem, however, my research has not led to a solution on this one here.
Thanks,
Peter
From experience, setPropertiesToFetch: only works when requesting the returned objects be dictionaries. So a couple of problems in your code:
You are asking for specific property (email), but have not set the return type to dictionary. Take a look at setResultType:.
You still need to take the result and extract the email objects from it. You cannot just assign the resulting array to your emails property.
Try this:
[fetchedRequest setResultType:NSDictionaryResultType];
NSArray* results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if(error != nil || results == nil) return;
flight.emails = [results valueForKeyPath:#"email"];

Keypath not found in entity error but I can insert into data store

I get this error:
core data data store missing getting error:'NSInvalidArgumentException',
reason: keypath flowData.flowAmount not found in entity <NSSQLEntity FlowData id=1>
when trying to load data from a core data store into a tableview.
The sqlite database is being re-created the next time I run the app after I remove the app from the Simulator after having changed the data model and reversioned it.
I am able to insert into the database. This dump shows the record I just entered:
INSERT INTO "ZFLOWDATA"
VALUES(1,1,1,NULL,8.0,376884478.384176,376884471.83175,8.0);
However when I try to read from the data store I get the above error.
Here's the relevant code for when I'm trying to load the data:
- (void)viewDidLoad
{
[super viewDidLoad];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
self.title = #"Flow Data Items";
}
and
- (NSFetchedResultsController *)fetchedResultsController {
jhsAppDelegate *appDelegate = (jhsAppDelegate *)[[UIApplication sharedApplication]delegate];
managedObjectContext = [appDelegate managedObjectContext];
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"FlowData" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"FlowData.flowAmount"
ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext sectionNameKeyPath:nil
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
self.fetchedResultsController.delegate = self;
return fetchedResultsController;
}
and here's my data model:
and finally, here's my data model class code for the entity:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface FlowData : NSManagedObject
#property (nonatomic, retain) NSNumber * flowAmount;
#property (nonatomic, retain) NSDate * flowEnteredDateTime;
#property (nonatomic, retain) NSDate * flowOccurrenceDateTime;
#property (nonatomic, retain) NSNumber * flowScheduledFrequencyMinutes;
#property (nonatomic, retain) NSNumber * flowUrgency;
#end
It must be something about how I am trying to read in the data since I am able to insert a row on another viewcontroller because I believe that FlowData.flowAmount does exist on the entity as it is being inserted into the entity.
Any ideas? Does the order in which the attributes are listed in the .h file have to match the order in the actual data model? Do my data types match?
The key for your sortDescriptor should be just flowAmount.
No need to also prepend the entity name.
PS: The order of the properties in the class is irrelevant. Your data types match (float and NSNumber).

Resources