I am experiencing problems with how I handle my Core Data NSManagedObjectContext.
I can create NSManagedObject in my NSManagedObjectContext, but I failed to save the value.
Here's what I got:
_lesson.title = _titleField.text;
int priority = [_priorityField.text intValue];
int difficulty = [_difficultyField.text intValue];
int time = [_timeField.text intValue];
int sortIndex = 0;
if ( time == 0 )
{
sortIndex = 101;
}
else
{
sortIndex = priority * ( difficulty / time );
}
_lesson.priority = [NSNumber numberWithInt:priority];
_lesson.difficulty = [NSNumber numberWithInt:difficulty];
_lesson.time = [NSNumber numberWithInt:time];
_lesson.sortIndex = [NSNumber numberWithInt:sortIndex];
NSError* error = nil;
[[(AppDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext] save:&error];
Everything before the saving is working perfectly, I used NSLog to verify if each value is really saved in _lesson.
And _lesson is sent from here:
if ( [[segue identifier] isEqualToString:#"addLesson"] )
{
LessonViewController* destination = [[LessonViewController alloc]init];
Lesson* lesson = (Lesson*)[NSEntityDescription insertNewObjectForEntityForName:#"Lesson" inManagedObjectContext:_managedObjectContext];
destination.lesson = lesson;
}
else if ( [[segue identifier] isEqualToString:#"editLesson"] )
{
LessonViewController* destination = [[LessonViewController alloc]init];
NSIndexPath* index = [_tableView indexPathForCell:(UITableViewCell*)sender];
[_managedObjectContext deleteObject:[_lessonArray objectAtIndex:index.row]];
Lesson* lesson = (Lesson*)[_lessonArray objectAtIndex:index.row];
destination.lesson = lesson;
}
After debugging for two hours, I cannot find my error. Please help!
I will include my full code below:
https://www.dropbox.com/sh/eu62ie9svbbqdmm/u1hYUICfjy
That is my full source code. (I copied and pasted and created a mess. So, Dropbox!)
Thanks in advance.
This line looks suspicious:
[_managedObjectContext deleteObject:[_lessonArray objectAtIndex:index.row]];
You delete the Lesson object before passing it to the LessonViewController, so that saving the context will delete that object from the store, and not save a (modified) object, as you probably intended.
It seems to me that you should just delete that line in your code.
ADDED: There is an error in your prepareForSegue method: You create a new view controller with
LessonViewController* destination = [[LessonViewController alloc]init];
Instead, you must use the destination view controller of the seque:
LessonViewController *destination = [segue destinationViewController];
Related
I am trying to delete the value of some Attributes in two linked Entities. Here is the code I am using:
+ (MeetPhoto *)deletePhotoForSelectedMeet:(MarksFromMeets *)specificMeet
inManagedObjectContext:(NSManagedObjectContext *)context;
{
MeetPhoto *results = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"MarksFromMeets"];
request.predicate = [NSPredicate predicateWithFormat:#"uniqueResultID = %#", specificMeet.uniqueResultID];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"uniqueResultID" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (!matches || [matches count] > 1) {
NSLog(#"There has been an error in matches in %#: \n%#.\n matches count = %lu",
NSStringFromClass([self class]),
NSStringFromSelector(_cmd),
(unsigned long)[matches count]);
} else if ([matches count] == 0){
NSLog(#"There were no matches in %#: \n%#.\n matches count = %lu",
NSStringFromClass([self class]),
NSStringFromSelector(_cmd),
(unsigned long)[matches count]);
} else {
if (debug == 1) NSLog(#"In %# and matches = %lu",NSStringFromClass([self class]),(unsigned long)[matches count]);
for (MarksFromMeets* meetEntity in matches) {
if (debug == 1) NSLog(#" MarksFromMeet = %# \n thumbnail = %# \n photo = %#",meetEntity.meetName,meetEntity.photoThumbNail,meetEntity.whichPhoto.photo);
[context deleteObject:meetEntity.photoThumbNail];
[context deleteObject:meetEntity.whichPhoto.photo];
NSError *mySavingError = nil;
[meetEntity.managedObjectContext save:&mySavingError];
if (mySavingError) NSLog(#"In %# and mySavingError is %#",NSStringFromClass([self class]), mySavingError);
}
}
return results;
}
Where whichPhoto is the title of the link between the two Core Data Entities.
Here is NSLog Statement:
2014-08-13 14:40:45.334 ITrackXC[12054:60b] thisMeetsPhoto should equal nil and equals (null)
2014-08-13 14:41:37.095 ITrackXC[12054:60b] In MeetPhotoViewController and executing imagePickerController:didFinishPickingImage:editingInfo:
2014-08-13 14:41:37.583 ITrackXC[12054:60b] In MeetPhoto and matches = 1
2014-08-13 14:41:37.589 ITrackXC[12054:60b] whichMeetPhotographed = <MarksFromMeets: 0x1700c5be0> (entity: MarksFromMeets; id: 0xd000000000300004 <x-coredata://ED56838C-9F27-451D-97CA-01AF0FC38830/MarksFromMeets/p12> ; data: {
athleteGrade = "12th Grade";
event = "5000 Meters";
eventPR = 1;
eventSB = 1;
markInEvent = "19:03";
meetDate = "2013-11-02 07:00:00 +0000";
meetID = 78103;
meetName = "OSAA 3A/2A/1A State Championships";
photoThumbNail = "<UIImage: 0x170281b80>";
placeInEvent = 2;
raceSorter = 5000;
seasonName = 2013;
sortMark = 1143;
standardOrMetric = nil;
uniqueResultID = 10032696;
whichPhoto = "0x178236d80 <x-coredata:///MeetPhoto/t9C5E481D-5911-47C9-AA8B-6015AAD1C23C2>";
whoRan = "0xd000000000080000 <x-coredata://ED56838C-9F27-451D-97CA-01AF0FC38830/AthleteInfo/p2>";
})
photo = <UIImage: 0x17009b440>
thumbnail = <UIImage: 0x170281b80>
2014-08-13 14:41:37.599 ITrackXC[12054:60b] NSManagedObjects did change.
2014-08-13 14:41:39.444 ITrackXC[12054:60b] NSManagedContext did save.
I do not get any errors. However when I look at the TableView where the data is displayed, the photos are still part of the record.
I can get rid of the thumbnail and associated photo by setting them to "nil". Is there any problem with this approach?
Setting attribute values to nil is how you're supposed to do this, so no, there's no problem with it. The deleteObject: method is only for when you want to delete a managed object. Using it in other cases might not crash, but it also won't have the effect you're aiming for.
In the case of the photo, you might need to delete an object, but it's hard to tell from your code. You seem to want to get rid of meetEntity.whichPhoto.photo. I don't know what whichPhoto is; if it's a managed object, you might want to call deleteObject on myEntity.whichPhoto to get rid of it. If nobody else is using it, that is.
how does my fetchedResultsController method look like, if I want to fetch all my attributes for an entity from core data? I only know and understand how to fetch data for a tableView and I think that is where all my confusion is coming from.
Here is my Core-Data setup:
I'm trying to fill an array with all the Attributes my Setting entity has and the show those values via NSLog output in my debug console.
Here is what I changed so far:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSManagedObject *newEntry = [NSEntityDescription insertNewObjectForEntityForName:#"Setting" inManagedObjectContext:context];
//NSManagedObject *newSetting = [NSEntityDescription insertNewObjectForEntityForName:#"Setting" inManagedObjectContext:context];
[newEntry setValue: #"StudiSoft" forKey:#"settingName"];
if (_overrideSysTimeSwitch.on) {
[newEntry setValue: #YES forKey:#"settingSysTimeOverride"];
//editSetting.settingSysTimeOverride = #YES;
NSLog(#"IF A");
} else {
//[newEntry setValue: #NO forKey:#"settingSysTimeOverride"];
//editSetting.settingSysTimeOverride = #NO;
NSLog(#"IF B");
}
if (_timeFormatSwitch.on) {
//[newEntry setValue: #YES forKey:#"settingTimeFormat"];
//editSetting.settingTimeFormat = #YES;
NSLog(#"IF C");
} else {
//[newEntry setValue: #NO forKey:#"settingTimeFormat"];
//editSetting.settingTimeFormat = #NO;
NSLog(#"IF D");
}
[self.settingsArray addObject:#"StudiSoft"];
NSError *error;
[context save:&error];
I'm using this code-snipped that and I'm able to modify the core data content.
However, every time I run this code, it of course adds a new object.
I've been looking for a way to update existing Attributes in my Entity, or modify them, but I could NOT find them.
Anyhow this is a good step into the right direction.
I created a completely new project, with just one view, once I have it working on the main view I'm going to experiment with segues....
But for now, how would I update or change existing attributes?
Thanks guys!!
This is my editSave Method to store some data in core data:
- (IBAction)editSave:(UIBarButtonItem *)sender
{
if ([_editSaveButton.title isEqualToString:#"Edit"])
{
[self setTitle:#"Edit Settings"];
//self.title = #"Edit Settings";
_overrideSysTimeSwitch.userInteractionEnabled = YES;
_timeFormatSwitch.userInteractionEnabled = YES;
_editSaveButton.title = #"Save";
} else if ([_editSaveButton.title isEqualToString:#"Save"])
{
[self setTitle:#"Settings"];
//self.title = #"Settings";
_overrideSysTimeSwitch.userInteractionEnabled = NO;
_timeFormatSwitch.userInteractionEnabled = NO;
_editSaveButton.title = #"Edit";
// #############################################################
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
//NSManagedObject *newEntry = [NSEntityDescription insertNewObjectForEntityForName:#"Setting" inManagedObjectContext:context];
//[newEntry setValue: #"StudiSoft" forKey:#"settingName"];
/*NSString *firstName = [anEmployee firstName];
Employee *manager = anEmployee.manager;
Setting *newSetting = [NSString #"Test"];
[newSetting setValue:#"Stig" forKey:#"settingName"];
[aDepartment setValue:[NSNumber numberWithInteger:100000] forKeyPath:#"manager.salary"];*/
//editSetting.settingName = #"Test";
if (_overrideSysTimeSwitch.on) {
//[newEntry setValue: #YES forKey:#"settingSysTimeOverride"];
editSetting.settingSysTimeOverride = #YES;
NSLog(#"IF A");
} else {
//[newEntry setValue: #NO forKey:#"settingSysTimeOverride"];
editSetting.settingSysTimeOverride = #NO;
NSLog(#"IF B");
}
if (_timeFormatSwitch.on) {
//[newEntry setValue: #YES forKey:#"settingTimeFormat"];
editSetting.settingTimeFormat = #YES;
NSLog(#"IF C");
} else {
//[newEntry setValue: #NO forKey:#"settingTimeFormat"];
editSetting.settingTimeFormat = #NO;
NSLog(#"IF D");
}
//[self.settingsArray addObject:#"StudiSoft"];
NSError *error = nil;
//if ([self.managedObjectContext hasChanges]) {
//NSLog(#"SAVE & DISMISS conetx has changed");
if (![context save:&error]) { // save failed
NSLog(#"Save failed: %#", [error localizedDescription]);
} else { // save succeeded
NSLog(#"Save Succeeded");
}
//}
//[self.tableView reloadData];
// #############################################################
}
}
Debug Output:
2014-06-10 19:09:29.881 SettingsCoreData[508:60b] Entry #5: <Setting: 0x8f983e0> (entity: Setting; id: 0x8f97030 <x-coredata://FA78AB86-3225-4B1E-97DD-3F31F5323A18/Setting/p6> ; data: {
settingName = StudiSoft;
settingSysTimeOverride = 0;
settingTimeFormat = 0;
})
2014-06-10 19:09:29.883 SettingsCoreData[508:60b] Entry #6: <Setting: 0x8f98430> (entity: Setting; id: 0x8f97040 <x-coredata://FA78AB86-3225-4B1E-97DD-3F31F5323A18/Setting/p7> ; data: {
settingName = StudiSoft;
settingSysTimeOverride = 1;
settingTimeFormat = 1;
})
Now I should be able to use something like this in my viewDidLoad, right?
if (editSetting.settingSysTimeOverride.boolValue == 0) {
_overrideSysTimeSwitch.on = NO;
} else {
_overrideSysTimeSwitch.on = YES;
}
But it doesn't work as I thought it will :-(
Next you need to call -performFetch: on the NSFetchedResultsController. Make sure you check the response and handle the error if the response is NO.
From there your NSFetchedResultsController is populated and ready to be used. You can then grab individual elements via -objectAtIndex: or you can grab them all with -fetchedObjects.
I would suggest just reviewing the documentation on the methods that are available as it has pretty strong and clear documentation.
Update
If you are not receiving any data then break it down. Take the NSFetchRequest that you created and call -executeFetchRequest:error: against your NSManagedObjectContext and see if you get any data back.
If you do then there is something wrong with your handling of the NSFetchedResultsController.
If you don't then there is something wrong with your NSFetchRequest or you don't have any data in your store.
Update
Sounds like you need to read a book on how Core Data works.
A NSFetchRequest is a query against Core Data so that objects can be returned from the store. You can pass a NSFetchRequest to a NSFetchedResultsController so that the NSFetchedResultsController can monitor the store for changes and let your view controller know when those changes occur.
A NSFetchRequest can also be executed directly against the NSManagedObjectContext and you can retrieve the results directly. You do that by calling -executeFetchRequest:error: against your NSManagedObjectContext and getting a NSArray back. You can then check that NSArray to see if you get any results.
If you do not understand that paragraph then you need to take a step back and read the tutorials on Core Data and/or read a book on Core Data. I can recommend an excellent book on the subject ;-)
i want to ask a question about core location and core data. i looked some questions but couldnt do that..
i have a application which stores some textfields, photos, date and time datas in UITableView With core data, i stored everything (photos, texts, date etc.) Now trying to store Location data i couldnt do.
this is some of my code here.
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
NSDateFormatter *myFormatter = [[NSDateFormatter alloc] init];
[myFormatter setDateFormat:#"MM-dd-yyyy HH:mm"];
[myFormatter setTimeZone:[NSTimeZone systemTimeZone]];
todaysDate = [myFormatter stringFromDate:[NSDate date]];
myDateLabel.text = todaysDate;
UIView *patternBg = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
patternBg.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background01.png"]];
self.tableView.backgroundView = patternBg;
// If we are editing an existing picture, then put the details from Core Data into the text fields for displaying
if (currentPicture)
{
[companyNameField setText:[currentPicture companyName]];
[myDateLabel setText:[currentPicture currentDate]];
if ([currentPicture photo])
[imageField setImage:[UIImage imageWithData:[currentPicture photo]]];
}
}
in the saveButton
- (IBAction)editSaveButtonPressed:(id)sender
{
// For both new and existing pictures, fill in the details from the form
[self.currentPicture setCompanyName:[companyNameField text]];
[self.currentPicture setCurrentDate:[myDateLabel text]];
[self.currentPicture setCurrentTime:[myTimeLabel text]];
[self.currentPicture setLatitudeData:[_latitudeLabel text]];
[self.currentPicture setLongtidueData:[_longtitudeLabel text]];
}
and last one, my locationManager's method..
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
_longtitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
_latitudeLabel.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
[self->locationManager stopUpdatingLocation];
}
}
i tried "[locationmanager stopUpdatingLocation];" many times, but when i entered the app, code starts to calculating latitude and longtitude data, i just want to take that data 1 time, and store..
Thanks!
If calling stopUpdatingLocation doesn't stop location updates, then most likely self->locationManager is nil. That would mean you're not really making the call.
It's hard to be sure exactly why this would happen, except that your code seem to make a point of not using any semantics implied by a #property declaration. Just assigning to location in viewDidLoad avoids any declaration, and looking up the manager using self->locationManager does as well. Assuming that location is a property, you should assign it to self.locationManager, and use that when looking it up as well.
A couple things:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
if (locationAge > 5) return; // ignore cached location, we want current loc
if (newLocation.horizontalAccuracy <= 0) return; // ignore invalid
// wait for GPS accuracy (will be < 400)
if (newLocation.horizontalAccuracy < 400) {
_longtitudeLabel.text = [NSString stringWithFormat:#"%.8f", newLocation.coordinate.longitude];
_latitudeLabel.text = [NSString stringWithFormat:#"%.8f", newLocation.coordinate.latitude];
[manager stopUpdatingLocation];
}
}
In your didUpdateToLocation do this code
(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
if (locationAge > 5) return;
// ignore cached location, we want current loc
if (newLocation.horizontalAccuracy <= 0) return; // ignore invalid
// wait for GPS accuracy (will be < 400)
if (newLocation.horizontalAccuracy < 400) {
_longtitudeLabel.text = [NSString stringWithFormat:#"%.8f", newLocation.coordinate.longitude];
_latitudeLabel.text = [NSString stringWithFormat:#"%.8f", newLocation.coordinate.latitude];
[manager stopUpdatingLocation];
//assign nil to locationManager object and delegate
locationManager.delegate = nil;
locationManager = nil;
}
}
Thanks.
I've got an issue that's been bothering me. I have to store information from a UI Action Sheet in iOS into an array provided in CoreData. Trouble is, the Action Sheet is in a different function than the one used to store the data.
First: Here's the relevant code for storing the data:
(... checking for all fields; working properly)
}
else
{
newContact = [NSEntityDescription insertNewObjectForEntityForName:#"Contacts" inManagedObjectContext:context];
[newContact setValue:Salutation.text forKey:#"Salutation"];
[newContact setValue:FirstName.text forKey:#"FirstName"];
[newContact setValue:LastName.text forKey:#"LastName"];
[newContact setValue:CompanyName.text forKey:#"CompanyName"];
[newContact setValue:EmailAddress.text forKey:#"EmailAddress"];
[newContact setValue:PhoneNumber.text forKey:#"PhoneNumber"];
newContact.Disinfector =[NSNumber numberWithBool:yesWasher];
newContact.Sterilizer =[NSNumber numberWithBool:yesSterilizer];
newContact.CoffeeMaker =[NSNumber numberWithBool:yesCoffeeMaker];
Salutation.text = #"";
FirstName.text = #"";
LastName.text = #"";
CompanyName.text = #"";
EmailAddress.text = #"";
PhoneNumber.text = #"";
yesWasher = YES;
[WasherTog setImage:[UIImage imageNamed:#"checked.png"] forState:UIControlStateNormal];
yesSterilizer = YES;
[SterilizerTog setImage:[UIImage imageNamed:#"checked.png"] forState:UIControlStateNormal];
yesCoffeeMaker = YES;
[CoffeeMakerTog setImage:[UIImage imageNamed:#"checked.png"] forState:UIControlStateNormal];
Second, here's the code for the Action Sheet and handling the input:
- (void)showSalutation:(id)sender
{
UIActionSheet *popUp = [[UIActionSheet alloc] initWithTitle:#"Choose Salutation" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:#"Cancel" otherButtonTitles:#"Mr.", #"Mrs.", #"Ms.", #"Dr.", nil];
popUp.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
[popUp showInView:self.view];
[popUp release];
}
- (void)showSalutation:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0)
{
Salutation.text = #"Mr.";
}
else if (buttonIndex == 1)
{
Salutation.text = #"Mrs.";
}
else if (buttonIndex == 2)
{
Salutation.text = #"Ms.";
}
else if (buttonIndex == 3)
{
Salutation.text = #"Dr.";
}
}
I feel like I'm making a lot of newbie mistakes, so please forgive me. I've been learning how to code all weekend and you guys have been my best friend for this stuff. I just haven't seen this particular issue on the net.
Thanks in advance for your help!
Chris
If you are jsut trying to select a string based on the button index use an instance variable. For example you could declare NSString* yourString in the header and use it as follows:
if (button.index == 0) {
yourString = #"Mr."
}
Just following that pattern should work and you can do whatever you want with yourString from there.
I am building an app that requres me to load all the contacts in the datasource of the table from the iPhone AddressBook. On running
Build and Analyze
for the following snippet
ABAddressBookRef addressBook = ABAddressBookCreate();
int nPeople = ABAddressBookGetPersonCount(addressBook);
CFRelease(addressBook);
for(int i=0; i < nPeople; i++ ){
//ABRecordRef person = [allPeople objectAtIndex:i];
NSString *name = #"";
if(ABRecordCopyValue([allPeople objectAtIndex:i], kABPersonFirstNameProperty) != NULL)
name = [[NSString stringWithFormat:#"%#", ABRecordCopyValue([allPeople objectAtIndex:i], kABPersonFirstNameProperty)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
[dataSource addObject: name];
}
[allPeople release];
I am getting a potential memory leak for the line
name = [[NSString stringWithFormat:#"%#", ABRecordCopyValue([allPeople objectAtIndex:i], kABPersonFirstNameProperty)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
I am really tired of fixing it but was unable to. Kindly help me out.
Any kind of help would be highly appriciated.
Thanks in advance!!
You aren't releasing the result of ABRecordCopyValue; try assigning it to a variable and release it and the end of the loop. Using a variable will also make your code a lot easier to read and highlight the cause of these issues better.
BTW, you are also calling ABRecordCopyValue twice with the same arguments, you should only do it once (using a variable as mentioned above).
I think you can do like below:
CFTypeRef copiedValue = ABRecordCopyValue([allPeople objectAtIndex:i], kABPersonFirstNameProperty);
name = [[NSString stringWithFormat:#"%#", copiedValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
CFRelease(copiedValue);
You can directly bridge to an NSString. It may be a little more clear:
CFTypeRef fn_typeref = ABRecordCopyValue(person, kABPersonFirstNameProperty);
CFTypeRef ln_typeref = ABRecordCopyValue(person, kABPersonLastNameProperty);
NSString * firstName = (__bridge NSString *) fn_typeref;
NSString * lastName = (__bridge NSString *) ln_typeref;
NSLog(#"Name:%# %#", firstName, lastName);
CFRelease(fn_typeref); // releasing CFTypeRef
CFRelease(ln_typeref);
// use firstName and lastName down here
NSLog(#"Name:%# %#", firstName, lastName);