Adding Objects to persistent store from user input - iphone

Ok, lemme start out by saying Im new to this! LOL I have done my due diligence of studying the topics (4 books and numerous videos to date) and searching for hours on end, and still havent found an answer.
I feel like I have a solid understanding of Core Data, or at least the back end DB side of it. I have my app built and I have my model built. My app has a Tabbar controller as well as a navigation controller for each separate tab.
My app will have an item table view which populates the name of those items from Core Data. Upon selecting the item, the navController pops to a detail view which loads the rest of the data for that item.
When a user clicks + to add an item, I need to pop to another View Controller with fields to add the name and details (which it does). However, I cant seem to get these details to save. I think I need to cast the user inputs as an NSSet, then bring that NSSet into the persistent store, but the method declaration for this is eluding me! Currently, my code looks like so...
- (IBAction) save:(id)sender {
NSLog(#"Save pressed");
if (itemName != nil) {
[itemName removeObject:itemName];
self.item = nil; //This will release our reference also
}
//Create a new item set for the new values
NSSet* newItem = [[NSSet alloc] initWithSet:newItem];
[self didChangeValueForKey:#"itemName"];
[self didChangeValueForKey:#"detailItem1"];
[self didChangeValueForKey:#"detailItem2"];
//Add it to the master item array and release our reference
[itemArray addObject:newItem];
[newItem release];
//Sort the array since the name might have changed with an existing item or a new one
NSSortDescriptor *nameSorter = [[NSSortDescriptor alloc] initWithKey:#"itemName" ascending:YES selector:nil];
[itemArray sortUsingDescriptors:[NSArray arrayWithObject:nameSorter]];
NSLog(#"Array sorter");
[nameSorter release];
//then pop the detailed view controller
[self dismissModalViewControllerAnimated:YES];
}
All of the documentation I have found on Core Data points more in the direction of populating an already existing database, not accepting user inputs. So if I am WAY off in my approach and the answer is more than just a simple one, please point me in the right direction!!
Also, Ive added items to my Core Data store that successfully persist. However, an sqlite DB hasn't been created in my app, which I thought happened automatically. So I may have more problems than I thought!
So far I have found this site to be a tremendous help, even though my reputation doesnt allow me to rate answers!
Anyway, thanks in advance for the help.

In most Core Data implementations, you don't deal with sets directly unless you are simultaneously adding multiple managed objects to a relationship. I'm not really sure what you are trying to do here.
In the code shown, you don't do anything related to Core Data. You do not have a context and you do not insert into the context a new managed object that you can populate with your new data. In fact, you don't seem to have any managed objects at all.
I suggest you look at the Navigation based project template in Xcode. It shows how to set up the Core Data stack and how to add and remove objects displayed in a tableview.

Related

Iphone Access rootviewcontroller from subviews

I have a navigation based application, from any of my subUIviewcontrollers when they click a button on the toolbar, I want to send rootviewcontroller the checked rowId or entered text. So I want to manage everything from Rootviewcontroller(e.g which page to show next)
But this code below does not hit the answeredValues in my rootcontroller, even gives a warning that it might not respond. why is that? and if there is a better way to this things like from delegete class?
in interface
-(void)answerValues:(NSMutableArray*)values;
in implementation
-(void) answerValues:(NSMutableArray*)values {
//get answer value
//edit insert xml with new answer
//make connection
//Get XML
//Parse and get the last page of questions
//Return a variable object filled with question and answers
}
RootViewController *root = (RootViewController*)[self.navigationController.viewControllers objectAtIndex:0] ;
[root answerValues:values];
As I commented above, you mispelled the name of the method. In the original question, you called a method named answeredValues, but in your updated question your method is named answerValues.

How do I create a Detailed View for my UITableView to allow me to edit data in Core Data?

I'm new to iOS, Objective-C and have only done a small ammount of OO programming before but enjoying the challenge of trying to create this app.
Summary of app:
I have a view which is just a form to enter information into the Core Data database eg. Name, Address & Telephone. Then another view which contains a Table View which lists all the names of people currently in database.
However this is where i'm stuck...
I have created a detailed view so that when a user selects a Table Cell the detailed view loads but i want to get data from Core Data back into the form they used to originally enter the data so it can be modified/editted and re-saved back to Core Data.
What are the best practicies of doing this?
I will need to pass some data forward to the detailed view, which know how to do, but what should I pass? how do I tell the detailed view which row of data in the Core Data database I wish to load?
Do I pass the tableview indexPath.row value? does this correspond to anything I can filter down in CoreData when loading the detailed view?
Thanks in advance to anyone who replies, would appreciate any help from methodology to online tuts
Matt
I think you can see Apple sample code here : CoreRecipes sample code
Picking out bits from CoreRecipes sample code as suggested by TheRonin I did the following.
Create an iVar of Core-Data DataModel object on the DetailedViewController
DataStore *enquiry;
On the tableview view under didSelectRowAtIndex setup Core-Data DataModel object and Detailed view controller,
DataStore *enquiry = (DataStore *)[_fetchedResultsController objectAtIndexPath:indexPath];
ShowEnquiryDetail *detailViewController = [[ShowEnquiryDetail alloc] initWithNibName:#"ShowEnquiryDetail" bundle:nil ];
set the Data Model object in DetailedViewController
detailViewController.enquiry = enquiry;
then push the DetailedViewController on to navigation stack.
[self.navigationController pushViewController:detailViewController animated:YES];
Then on the DetailedViewController you can access the data in the DataModel object from the selected row in Core-Data using enquiry.name, enquiry.email, etc...
This isn't a well written answer and may use incorrect terminology but hopefully it will help someone in the same position I was.

Going back to list in Drill-Down Navigation Based Application using Core Data on iPhone

I want to build a simple drill down app (similar to the Contacts app on the iPhone). I am using Xcode 4.0.
I start by making a new "Navigation Based Application", and also say that I will be using Core Data for storage. I then go and add 'New File' and select UIViewController, and a subclass of UIViewController.
In my RootViewControler (which was made in the template) at didSelectRowAtIndexPath, I do the following:
NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
TrackerDetailViewController *trackerView = [[TrackerDetailViewController alloc] initWithNibName:#"TrackerDetailViewController" bundle:nil];
trackerView.title = [managedObject valueForKey:#"trackerName"];
trackerView.referringObject = managedObject;
[self.navigationController pushViewController:trackerView animated:YES];
[trackerView release];
And this works - I can load up some details in TrackerDetailViewController.
What I cant seem to figure out is how to go back! Most places I am reading online say that this should be happening automatically. I can't seem to get that to happen. If I download a few samples and compile them, they do have a back button - but I dont see how it was added or managed, and can't find what I am missing to not have it.
Check if [managedObject valueForKey:#"trackerName"] is actually returning anything. If not, then there's no title, and no back button will be created.
Oops, just re-read your code. It's the parent that needs a title assigned. Add something like self.title = #"myName; and myName should then appear as your back button in trackerView.

why does this code use presentModalViewController? (not pushViewController)

Anyone understand why in the CoreDataBooks example code that:
(a) method for controller swapping difference
Whilst the click an item and go to detailed view uses what seems to be the standard UINavigationController concept of "pushViewController", that when when you click on the "Add" a new record button it launches the new view to add the record via "presentModalViewController" approach? That is, couldn't the approach have been the same in both cases, just using a pushViewController approach?
Are there actually any advantages to using each approach for where it's been used? I can't quite see. I'd guess there must have been something for Apple to choose these different approaches for different scenarios. For example:
any differences to the user (i.e.
UI differences or functional
differences) that they would see?
any differences for the developer
(or advantages/disadvantages)
For example, if you were to consider using pushViewController approach instead of the presentModalViewController approach for the for the "Add" scenario...
(b) data sharing approach difference
the approach to how they share the common data object seems to be different - so again just wondering why the approaches weren't the same? (i.e. in both cases the main controller is passing off to another view temporarily and there is some shared data between them - i.e. that the child view needs to pass back to the parent)
Code Extract for Convenience
That is for "Edit":
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Create and push a detail view controller.
DetailViewController *detailViewController = [[DetailViewController alloc] initWithStyle:UITableViewStyleGrouped];
Book *selectedBook = (Book *)[[self fetchedResultsController] objectAtIndexPath:indexPath];
// Pass the selected book to the new view controller.
detailViewController.book = selectedBook;
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
But for "Add"
- (IBAction)addBook {
AddViewController *addViewController = [[AddViewController alloc] initWithStyle:UITableViewStyleGrouped];
addViewController.delegate = self;
// Create a new managed object context for the new book -- set its persistent store coordinator to the same as that from the fetched results controller's context.
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
self.addingManagedObjectContext = addingContext;
[addingContext release];
[addingManagedObjectContext setPersistentStoreCoordinator:[[fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
addViewController.book = (Book *)[NSEntityDescription insertNewObjectForEntityForName:#"Book" inManagedObjectContext:addingContext];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addViewController];
[self.navigationController presentModalViewController:navController animated:YES];
[addViewController release];
[navController release];
}
thanks
You use modal view controllers to focus the user's attention on a Task. When you push, the user is in some kind of navigation flow, but still has the total application at their fingertips. They might decide to go forward or backward, switch to a different tab in the middle, whatever. When they get a modal view controller, they can't do any of that until the task is completed or canceled out of (the modal view is dismissed)
[Warning: this answer applies more to the updated code of the CoreDataBooks, which has changed to use the new-in-iOS5 setParentContext method of NSManagedObjectContext instead of messing with the persistentStoreCoordinator[
Your 2nd question about data sharing is also answered by the modal Add vs modeless Edit approach. Run the app in the simulator and notice that:
if you click on Add your next view has both Save and Cancel buttons
if you click on Edit your next view has only a Done button.
(Now, in this particular project, you have to edit each field at a time and the field editing is done in yet another view, and that one has a Cancel button, but ignore that for now, because
a. this only applies to the field. E.g. If you edit a Title and hit Save, you're back at the Edit view with the Done button, now there's no cancel to undo that change, you can only hit Done. As far as this view is concern, you've edited the Book modeLESSly
b. What a lame UI! Come on Apple, make the CoreDataBooks into a decent, albeit simple app that follows your own conventions. At least put the editing in the cells.)
Where were we? Oh yeah, "Edit"-ing an existing Book is modeLESS, so it passes the original Book in the same MOC (NSManagedObjectContext) and you can't cancel your edits to it in the Edit view.
"Add"-ing a Book, on the other hand is MODAL: It creates a new Book for to be edited in the detail view, and wants to discard it if the user hits cancel. To achieve this it has to use a second MOC, which is a child of the first. If the user Cancels, it simply ignores the new child MOC, effectively discarding the new Book; if the user Saves, it saves the child MOC, which pushes the new Book and its properties up into the parent MOC, then saves the parent MOC.
This child-MOC approach, btw, is detailed in the WWDC 2011 presentation 303 "What's new in Core Data on iOS".
There are other approaches discussed elsewhere in SO, including
Creating a new managed object with a nil MOC, and only inserting it in the parent MOC when the user hits save
Not using a managed object but a different data structure for the temporary object (the new Book that we're not sure we want to save yet), such as an NSDictionary, or just a set of different variables
and more... ?
I kind of prefer the parent-child approach because Apple favours it and because it makes use of the data model objects instead of creating parallel data structures for temporary objects.
The nil-context approach also has that benefit, and the added benefit of (apparently) better performance and simplicity (read my lips: no new MOCs). But I'm not convinced that managed objects without managed object contexts are kosher.
By the way, CoreDataBooks doesn't exactly follow the convention laid down in the aforementioned presentation, because it doesn't save the parent context in a performBlock block.
Also I'm not sure why it sets the new managed context as a property on the AddViewController and doesnt use it.

Adding object to NSMutableArray in another xib

Im trying to add an object to a NSMutableArray in another xib. But seems isnt working. What im doing wrong?
Thanks!
-(void) buy {
CartViewController *carrinho = [[CartViewController alloc] initWithNibName:#"CartViewController" bundle:[NSBundle mainBundle]];
carrinho.produtoCodigo = [[NSMutableArray alloc]init];
[carrinho.produtoCodigo addObject:#"aa"];
[carrinho release];
NSLog(#"did");
}
Your code looks fine so far. Make sure the connections in InterfaceBuilder and the File's owner in the XIB is set correctly.
Ok, several things. First you don't need to pass in [NSBundle mainBundle]. nil works fine if you want the main bundle. Second issue is, produtoCodigo should be a property set to retain and as such you should pass in an autoreleased NSMutableArray i.e. [NSMutableArray array].
Thirdly, I would question why you would want to do this. It seems like a bad design. Ideally the mutable array should be an internal ivar in CartViewController. You should then have a method on CartViewController to handle the item. You should not care about how it is stored internally, only that you want to add an object to the controller.
If you want to pass in multiple objects you should have a method that takes an array of objects and pass that in.
Now finally, nibs don't really hold arrays, the class does. As such it shouldn't be an issue with your nib. The issue should therefore be with the class. Where are you checking whether the array is being updated and finding that it isn't?
you declare and create carrinho as a view controller, which should allocate and init the carrinho.produtoCodigo as well, if you have it synthesized. Then you alloc it again, which may be a memory leak. After adding the aa, you release it. Therefore, overall, you haven't accomplished anything. That mutable array comes into being, is modified, and then destroyed.
You mention "another xib" and from the name CartController and method name "buy" it sounds like you want to update a shopping cart that is being held by some other class. Therefore, the view or class with the cart (and mutable array) needs to be modified. It's like if you and a friend go shopping, and you delegate the job of managing the cart to him. He is the delegate, and only he can put stuff in the cart. Just because you want to buy something, you have to give it to him first so that he can put it in the cart. Right now, your code is like you grab something off the rack, but then put it back on the rack. It never makes it into the cart.
What you want to do is create a shopping protocol with a message addToCart which is what this code would instead do. It would send the message to the delegate to add the item to the cart. The other xib code has a method -(void)addToCart:(id)item; which is what is invoked when this code chunk does to call to the delegate. Look up protocols and delegates; they are simple to create, and the only way to get multiple controllers talking to one another.
Maybe you inserted this below code in the second XIB:
-(void) viewDidLoad {
produtoCodigo = [[NSMutableArray alloc] init];
}
because if you make allocate an array again, the previous objects in it will be removed.