Very basic iOS question. I have a table created (m_historytable) with four columns created in the form of subviews (counter, date, name, and result). Every time the app runs a new row is added to the top of the table. I need to read the most recent name added and pass it to a UILabel. I would expect the statement to be something like:
m_last_name.text = [NSString stringWithFormat:#"%d", ??? ];
My question is what I need to replace ??? with.
You need to be studying CoreData in more details. For getting data from your core data entries use the following code.
Create a function in your class to get context
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
Now to get data from core data entities
NSManagedObjectContext *context = [self managedObjectContext];
NSMutableArray *historyDataArray = [[NSMutableArray alloc]init];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"HistoryData"];
historyDataArray = [[context executeFetchRequest:fetchRequest error:nil] mutableCopy];
once you get the array, you can choose the most recent item like this
NSManagedObject *object = [historyDataArray objectAtIndex:historyDataArray.count-1];
Then you can pass any value to a UILabel as follows:
m_last_name.text = [[NSString stringWithFormat:#"%#",[object valueForKey:#"name"] ];
You can pass there current date with [NSString stringWithFormat:#"%#",[NSDate date]];
You need to replace with some integer value as %d indicates the integer. Refer below sample:-
int count=1;
m_last_name.text = [NSString stringWithFormat:#"%d",count];
For more refer this example
Related
I have a set of core data with entity called PeronalInfo with attributes name, age gender, and nationality.
E.g.
John, 25, Male, English
Sean, 65, Male, Indian
Jess, 46, Female, American
I need to store this core data as an array so I can do a check for anyone age over 30 and display all the attributes.
I have this so far:
- (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self preparePersonalInfo];
}
-(void)preparePersonalInfo
{
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"PersonalInfo"];
personalInfo = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
}
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *personalInfoArray = [[NSMutableArray alloc] init];
personalInfoArray = //This is where I need help to add the core data into this array
//then I need to check if age is over 30
//Then store the whole row of data as an array of an array to display it later
}
How do I do the commented section.
Thanks in advance!
If you only want a subset of the entries in Core Data, the right way to do it is to use a predicate on your fetch request so that you only fetch those entries. Something like
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"age > %#", 30];
fetchRequest.predicate = predicate;
That will filter the results of the fetch. You seem to already know how to convert the resulting NSArray into an NSMutableArray.
In my application, I'm downloading data from web service with pagination. Output is a json array of dictionaries.
Now, I am saving the output json array in core data. So, my problem is, every time calls the saveInCoreData: method with the result array, it creates duplicate objects in the data base. How can i check for an object and update or replace the object if its already exists?
myId is a uniq key.
// save in coredata
+ (void) saveInCoreData:(NSArray *)arr{
// get manageObjectContext
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
if(arr != nil && arr.count > 0) {
for(int i=0; i < arr.count; i++){
SomeEntity *anObj = [NSEntityDescription
insertNewObjectForEntityForName:#"SomeEntity"
inManagedObjectContext:context];
anObj.description = [[arr objectAtIndex:i] objectForKey:#"description"];
anObj.count = [NSNumber numberWithInteger:[[[arr objectAtIndex:i] objectForKey:#"count"] integerValue]];
// Relationship
OtherEntity *anOtherObject = [NSEntityDescription
insertNewObjectForEntityForName:#"OtherEntity"
inManagedObjectContext:context];
creatorDetails.companyName = [[[arrTopics objectAtIndex:i] objectForKey:#"creator"] objectForKey:#"companyName"];
}
}
The most efficient way to avoid duplicates is to fetch all the objects you already have, and avoid processing them when iterating over the results.
Get the topicIds from the results:
NSArray *topicIds = [results valueForKeyPath:#"topicId"];
Fetch existing topics with these topicIds:
NSFetchRequest *request = ...;
request.predicate = [NSPredicate predicateWithFormat:#"%K IN %#",
#"topicId", topicIds];
NSArray *existingTopics = [context executeFetchRequest:request error:NULL];
Get the existing topicIds:
NSArray *existingTopicIds = [existingTopics valueForKeyPath:#"topicId"];
Process the results:
for (NSDictionary *topic in results) {
if ([existingTopicIds containsObject:topic[#"topicId"]]) {
// Update the existing topic if you want, or just skip.
continue;
}
...
}
Attempting to fetch each existing topic individually, within the processing loop, will be very inefficient in terms of time. The tradeoff is more memory usage, but as you are only getting 20 objects at a time, this should be a complete non-issue.
I'm a bit confused about saving entities using Core Data. I'm making a screen that will allow users to save their settings (contact information), which can be changed later if they wish.
From what I understand, my code below will save multiple entities each time the 'save' button is pressed.
- (IBAction)saveSettings:(id)sender {
AppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSManagedObject *userSettings;
userSettings = [NSEntityDescription
insertNewObjectForEntityForName:#"UserSettings"
inManagedObjectContext:context];
[userSettings setValue: _firstName.text forKey:#"firstName"];
[userSettings setValue: _lastName.text forKey:#"lastName"];
[userSettings setValue: _userEmail.text forKey:#"userEmail"];
[userSettings setValue: _zipCode.text forKey:#"zipCode"];
}
What I don't understand how to do is save one entity, and then change the values of the attributes later on whenever the user types in new values in the appropriate text fields and presses 'save'.
Yes - because you use insertNewObjectForEntityForName:, a new UserSettings object is created each time that method is run. What you probably want to do is to fetch the existing settings from the database, update your textFields with that data, present the view and let the user amend the details as necessary, and then (when they press the save button), save that data back to the database.
I would add userSettings as a property:
#property (strong, nonatomic) NSManagedObject *userSettings;
and in your method delete the declaration of userSettings, and the line where you use insertNewObjectForEntityForName.
Then create a new method to handle fetching the data from the database and assigning it to your textFields, as follows:
-(void)loadSettings {
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"UserSettings"];
NSError *error;
NSArray *results = [context executeFetchRequest:fetch error:&error];
if (results == nil) {
// some error handler here
}
if ([results count] > 0) {
userSettings = [results lastObject];
_firstName.text = [userSettings valueForKey:#"firstName"];
_lastName.text = [userSettings valueForKey:#"lastName"];
_userEmail.text = [userSettings valueForKey:#"userEmail"];
_zipCode.text = [userSettings valueForKey:#"zipCode"];
} else {
// set your text fields to some defaults values??
}
}
Call this method when your view controller loads, in the viewDidLoad method. I've assumed that you will normally have only one UserSettings object (hence lastObject will be the only object!). If you could have many UserSettings objects, you would need to filter the fetch to get only the one you want. To do that you would need to set a predicate for the fetch - look at the documentation for NSPredicate.
You are actually overwriting those properties everytime you "set". The correct way to store individual properties is to assign them and save, like so:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *userSettings;
userSettings = [NSEntityDescription insertNewObjectForEntityForName:#"UserSettings"
inManagedObjectContext:context];
userSettings.firstName = _firstName.text;
userSettings.lastName = _lastName.text;
userSettings.userEmail = _userEmail.text;
userSettings.zipCode = _zipCode.text;
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Error Saving: %#", error);
}
I'm having trouble deleting records from Core Data SQLite file. I want to be able to delete the corresponding record from my file when I delete a row from my table view.
Here is what I am doing after fetching all records into allContacts array
NSManagedObject *contactRecord = [allContacts objectAtIndex:arc4random() % allContacts.count];
self.managedObjectID = [contactRecord objectID];
Then called my method that prepares my contacts and then display them on the tableview.
When I delete a row from the table, I call this method
-(void)deleteContactFromFile:(contact *)deletedContact
{
NSLog(#"deleted Contact %#",deletedContact.personID);
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext;
[context deleteObject:[context objectWithID:self.managedObjectID]];
[context save:nil];
}
The funny thing is I get a random record deleted from my core data file, but not the one I selected. I don't know how to deal with ObjectID thing for deleting a specific NSManagedObject.
If my question is not clear enough please tell me to clarify more.
You should be using an NSFetchedResultsController. It will help you to associate every index path of your table view with a specific managed object. You then do not need to fetch all data and filter through them.
For example, if you have the index path and a fetched results controller it is as easy as
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSManagedObjectContext *context = object.managedObjectContext;
[context deleteObject:object];
[context save:nil];
Note that you not need to go to your app delegate to get the managed object context.
Try this:
- (void)deleteContactFromFile:(contact *)deletedContact {
NSManagedObjectContext *context = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSFetchRequest *fetchRequest = [NSFetchRequest new];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"EntityName" inManagedObjectContext:context]];
NSError *error;
NSArray *rootArray = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *object in rootArray) {
if ([context objectWithID:self.managedObjectID]) {
[context deleteObject:object];
}
}
}
I am learning how to use Core Data. I have an app that fills out all the variable of an entity labeled User. I then take these users and load them to a table. At this point the users can be selected and pushed to a new view controller at which point I generate a PDF file of the user selected. So I think I am misunderstanding what it is I have to pass to the view controller for it to access the core data selected in the table. Here is what I have in my table view controller.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.spinner startAnimating];
ReportPDFViewController *reviewViewController = [[ReportPDFViewController alloc] init];
reviewViewController.userInfo = [[self.fetchedResultsController fetchedObjects] objectAtIndex:indexPath.row];
[self.navigationController pushViewController:reviewViewController animated:YES];
}
Then the next view states this
- (void)viewDidLoad
{
[super viewDidLoad];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithTitle:#"Email PDF"
style:UIBarButtonItemStylePlain
target:self
action:#selector(emailPDF)];
self.navigationItem.rightBarButtonItem = barButton;
TrafficSignalProAppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
context = [appDelegate managedObjectContext];
// Do any additional setup after loading the view.
NSEntityDescription *entitydesc = [NSEntityDescription entityForName:#"User" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entitydesc];
NSError *error;
NSArray *matchingData = [userInfo executeFetchRequest:request error:&error];
NSString *Intersection;
NSString *currentDay;
for (NSManagedObject *obj in matchingData) {
Intersection = sendIntersection;
currentDay = sendDate;
}
NSString* fileName = [self getPDFFileName];
[PDFRenderer drawPDF:fileName intersectionSearch:Intersection dateSearch:currentDay];
[self showPDFFile];
self.navigationItem.title = #"Report";
}
So I'm trying to pass the NSManagedObjectContext of the selected row to then load. I am really lost after that. I'm not sure if passing the managed object context is right and if it is I don't know what is wrong with the code in the ReportPDFViewController. I have looked through all the tutorials I can find. I have a limited programming background so any help would be greatly appreciated.
reviewViewController.userInfo = [[self.fetchedResultsController fetchedObjects] objectAtIndex:indexPath.row];
This sets userInfo to an object of type NSManagedObject (or a subclass).
NSArray *matchingData = [userInfo executeFetchRequest:request error:&error];
This is using userInfo as if it's a NSManagedObjectContext. I would imagine you get an invalid selector error here.
What is the actual type of the userInfo attribute? It should be NSManagedObject.
You do not need to do a fetch request in your viewDidLoad. Core Data is not a database. You do not always need to do a fetch request every time you want some information. Once you already have a managed object, you can get information related to it without a fetch request. If you've set up a custom class for it, you can treat it almost like it's an regular objective-C object.
for (NSManagedObject *obj in matchingData) {
Intersection = sendIntersection;
currentDay = sendDate;
}
This code just doesn't make sense. You're looping, but each time through you're assigning the same value to the variables. I don't know what that value is, since sendIntersection and sendDate are not referred to anywhere else in the code you posted. In any case you're not using the results of the fetch request at all.
I'm going to make a wild guess at what you need to do:
Intersection = [userInfo valueForKey:#"intersection"];
currentDay = [userInfo valueForKey:#"date"];
It's a total guess, because I don't know what your data model is. No loop is needed, since you only want and have one user object.