Xcode: Why is my NSLog getting called twice? - ios

I have an ID for a post from my server that I would like to access in two different view controllers. The first view controller is a table view that displays the posts. The user will select a row and the ID for that post will be passed to the next view controller to display details about the post. I use the code below.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
PFObject *objectId = [collectionArray objectAtIndex:indexPath.row];
selectedObjectID = [objectId objectId];
self.objectID = [objectId objectId];
[self performSegueWithIdentifier:#"details" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqual: #"details"]) {
PreviewDetailsViewController *details = (PreviewDetailsViewController *)[segue destinationViewController];
details.objectID = self.objectID;
}
}
"selectedObjectID" is an extern variable and "self.objectID" is a property I set for both classes and tried to set it in the prepareForSegue method. Both of these ways of doing it do the same thing. It passes the variable to the next view controller but when I
NSLog(#"object = %#", self.objectID);
or
NSLog(#"object = %#", selectedObjectID);
in the viewDidLoad of the next view controller, it passes the ID to the next view controller, but it logs out this
object = (null)
object = Zz81bHEeJD
It's like it is getting called twice and the call that matters when I try to query for "self.objectID" or "selectedObjectID" I get
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: 'Cannot do a comparison query for type: (null)'
which is causing me much headache.
Can anyone explain to me what is going on here and how I can rectify this?

viewDidLoad gets called when your view controller is loaded. When you are in prepareForSegue, you know that the view controller has already been loaded - because you are able to get a reference to it from destinationViewController. This means that viewDidLoad executes before you assign a value to details.objectId.
You should access the property in a method such as viewWillAppear, or trigger your query in a custom setter for the objectId property on your PreviewDetailsViewController.
And I assume that the extern was just for testing, but don't use externs

Related

Unable to pass data between a table view and another view controller

I'm having trouble transferring data between a table view controller and another view controller.
On my sending controller - tableviewcontroller.m - I have this under prepareForSegue:
FriendDetailViewController *fvc = (FriendDetailViewController *)segue.destinationViewController;
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
PFUser *u = self.friends[path.row];
fvc.nameLabel.text = u.username;
//friendViewController.m (the destination view controller)
- (void)viewWillLoad {
[super viewDidLoad];
if (self.user) {
self.nameLabel.text = self.user.username;
}else{
self.nameLabel.text = #"No data to display";
}
}
The label displays "No data to display".
I've read other posts and viewed tutorials but I cannot figure this out.
You're only setting the nameLabel in your prepareForSegue, not the actual user, so it is uninstantiated when your new VC loads.
Try adding this into your prepareForSegue:
fvc.user = u;
Assuming your user you want to pass is stored in u, that should do it (also assuming user is a public property of the new view controller.)
You can put an attribute in your .h file of FriendDetailViewController and the set it before calling:
fvc.attribute = u.username;
After this, you can call pushViewController for "fvc" inside didSelectRowAtIndexPath
You can't set the UI actions on prepareForSegue.
You could instead define the user variable on destination viewcontroller.
After that, on prepareForSegue, you can set the selected PFUser to the destination viewcontroller's user.
And when you'd like to show the user's name, use viewWillAppear instead of viewWillLoad.
If you can't understand, please let me know, I can show detailed code for all.

Passing a PFObject between views is causing values to be null

I'm new to Parse and iOS app development, so please excuse my question if it has an obvious answer.
In my app, the user needs to enter data across multiple views, and for resource efficiency, I am initiating the PFObject as a property in the first view and it is being handed via prepareForSegue to by each scene to its segue's destination view controller.
However, when checking the key-value pairs in the object, I noticed that they are not getting stored in the object. In the debugger, it shows the data in the "estimatedData" section. What is the cause of this? When I try to saveInBackground the object, it fails and says that the object is null.
Here is the code from the FirstViewController.h of the PFObject property declaration.
#property (strong, nonatomic) PFObject *freshChow;
I also call #synthesize freshChow; under the #implementation of the FirstViewController.m.
I later initialize the object in an IBAction when a button is tapped.
- (IBAction)StartCookingProcess:(id)sender {
freshChow = [PFObject objectWithClassName:#"FoodItems"];
[self performSegueWithIdentifier:#"Perform Init Segue" sender:self];
}
And the prepareForSegue method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"Start Cooking Process"]) {
Chow_Type_selection *vc = [segue destinationViewController];
vc.freshChow = freshChow;
}
}
This code, with the exception of the StartCookingProcess method is repeated on the subsequent views.
Thanks,
Siddharth

prepareForSegue destination controller property not being set

Here's my prepareForSegue:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqual:#"cameraToRollsSegue"]){
ALRollsTableViewController *rollsTableViewController = (ALRollsTableViewController *)[segue destinationViewController];
Camera *c = [self.fetchedResultsController objectAtIndexPath:[self.tableView indexPathForSelectedRow]];
NSLog(#"CAMERA FROM INSIDE PREPARE FOR SEQUE: %#", c);
rollsTableViewController.selectedCamera = c;
}
}
I verify that the camera is not null with NSLog:
CAMERA FROM INSIDE PREPARE FOR SEQUE: <Camera: 0x8dc1400> (entity: Camera; id: 0x8dafba0 <x-coredata://A415F856-5F21-4F08-9CAB-4B2A023B55C3/Camera/p1> ;
ALRollsTableViewController viewDidLoad:
- (void)viewDidLoad
{
NSLog (#"ROLLS TABLE VIEW CONTROLLER : viewDidLoad!");
NSLog(#"(selected camera = %#", self.selectedCamera);
}
results in:
ROLLS TABLE VIEW CONTROLLER : viewDidLoad!
(selected camera = (null)
What might I be doing wrong here that the property is not being set?
UPDATE
With matt's help I've determined that the instance of my destination view controller in my prepareForSeque does not match the actual destination view controller:
rollsTableViewController FROM SEGUE: <ALRollViewController: 0x8d90bf0>
rollsTableViewController FROM viewDidLoad in rollsTableViewController: <ALRollsTableViewController: 0x8c5ab00>
I don't know why this is the case or what to do to fix it.
Post-chat summary:
Well, it was complicated! But basically you were saying this:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqual:#"cameraToRollsSegue"]){
ALRollsTableViewController *rollsTableViewController = (ALRollsTableViewController *)[segue destinationViewController];
// ...
}
The problem was that [segue destinationViewController] was not an ALRollsTableViewController. Thus you were not talking to the instance you thought you were talking to, and you were not talking to an instance of the class you thought you were talking to.
The amazing thing is that your code didn't crash when it ran. You were saying this:
rollsTableViewController.selectedCamera = c;
But rollsTableViewController was not in fact an ALRollsTableViewController. You lied to the compiler when you cast incorrectly. Yet you didn't crash when that line ran. Why not? It's because you've got lots of classes with #property selectedCamera! So you were setting the property of a different class. But a property with that same name did exist in that class, so you didn't crash. Thus you didn't discover that this was the wrong class and the wrong instance.

NSFetchedResultsController object deleted from UITableView on update

Please be gentle, this is my first ever post.
When I update a subclassed NSManagedObject and perform a save using a fetched results controller, instead of calling NSFetchedResultsChangeUpdate in "didChangeObject" it calls NSFetchedResultsChangeDelete. The save works and it updates the objects, but then immediately deletes it from the UITableView. If I quit and return, the object re-appears in the tableview with the updates so I know it is saving it to the DB.
I am developing code for a simple task app using XCODE 5. I am using an iPhone 4S and not the simulator to test it. It was working fine until I made some mods to another part of the code (I thought unconnected and now it doesn't work.
I have a sort order and when I update an object and it changed in the sort order, there was a nice animation for the moving of the UITableViewCells....now I have to force a fetch again and do [self.tableView reloadData]. This is a hack as I do not get any animations, but it is the only way I can get it to update:
I have a prepare for segue method:
- (void) prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender
{
// configure the destination view controller:
if ( [segue.destinationViewController isKindOfClass: [ShowEditTaskTableViewController class]])
{
if ([sender isKindOfClass:[UITableViewCell class]] )
{
NSIndexPath *indexPath = [self.taskTableView indexPathForCell:sender];
[self.taskTableView deselectRowAtIndexPath:indexPath animated:YES];
// Pass the selected task to the new view controller.
ShowEditTaskTableViewController *detailViewController = segue.destinationViewController;
Task *info = [self.fetchedResultsController objectAtIndexPath:indexPath];
detailViewController.editedObject = info;
}
else
{
NSIndexPath *indexPath = [self.taskTableView indexPathForCell:sender];
[self.taskTableView deselectRowAtIndexPath:indexPath animated:YES];
ShowEditTaskTableViewController *detailViewController = segue.destinationViewController;
Task *info = (Task *)[NSEntityDescription insertNewObjectForEntityForName:#"Task" inManagedObjectContext:self.managedObjectContext];
detailViewController.editedObject = info;
}
}
}
There is nothing special here. "Task" is my NSManagedObject.
I have a rewind (EDIT: changed reverse to rewind after comment) segue and before it is called, I set the variables which will be set stored in the editedObject.:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"addNewTaskSave"]) {
// Note: This is an unwind segue to go back to the previous screen.
self.dueDate = self.taskDatePicker.date;
self.description = self.taskDescriptionTextView.text;
self.priority = self.taskPrioritySegment.selectedSegmentIndex;
}
}
and in the RootViewController it calls:
- (IBAction)addNewTaskSave:(UIStoryboardSegue *)segue
{
ShowEditTaskTableViewController *controller = segue.sourceViewController;
controller.editedObject.shortDesc = controller.description;
controller.editedObject.priority = #(controller.priority);
controller.editedObject.dueDate = controller.dueDate;
controller.editedObject.completed = #0;
NSError *error;
if (![self.managedObjectContext save:&error]) {
...
}
.....
}
It uses the standard didChangeObject delegate methods. This worked fine until I clearly changed something.
Now after the save, it sets the NSFetchedResultsChangeDelete option and deletes the table row.
The fix is to add:
self.fetchedResultsController = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
...
}
[self.taskTableView reloadData];
However, this means that what is actually observed is just a straight update of the Table View with no animation.
Without this code, you can observe an animated delete of the table row. If I quit and restart, my edited object is there.
I have scoured SO which has been my constant companion for the last 4 weeks (the time I have been coding for IOS) and cannot find anything on this behaviour.
Any help would be greatly appreciated.
EDIT:
before the save, controller.editedObject isDeleted = NO, isUpdated = YES, so I can't see why it is setting NSFetchedResultsControllerChangeDelete.
I'm just going to discuss one piece of your code; and this may not have anything to do with the fetch results controller issue described.
Your so-called unwind/reverse segue has me stumped. I don't think prepareForSegue:sender: is relevant for an official unwind segue. So maybe you're not really using an official unwind segue (which is created by control-dragging from a button to the "exit" icon beneath a storyboard scene).
Unless you're referring to an unwind segue, there's no such thing as a "reverse" segue that takes you back to a previous screen. A push segue, for example, doesn't have a separate counterpart called a "pop" segue.
I suspect that you have segues crisscrossing between two scenes in storyboard. In other words, you created a segue from scene A to scene B, and you created a segue from scene B to scene A. If that's really the case, I would avoid doing that because it's unconventional. Maybe spend some time reviewing segues.
Ok, so I'll briefly address the fetched results controller issue. If there are user-driven changes to the data in a table view populated by a fetched results controller, then you will need to set a flag and temporarily "turn off" the fetched results controller delegate methods. This issue is mentioned briefly in the docs for NSFetchedResultsControllerDelegate protocol under "User-Driven Updates".
It appears my scouring of SO was not very good. see here https://stackoverflow.com/a/18998335/3482632
I added a UISegmentControl to filter and used the int value of its segment index.
int index = self.filterSegment.selectedSegmentIndex;
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"filter == %d", index]];
[fetchRequest setPredicate:predicate];
"filter" in my NSManagedObject subclass is an NSNumber. when I changed the NSPredicate to
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"filter == %#", #(index)]];
everything works fine.

Custom setter for segue not seeing the object I'm sending to it in iPhone Master/Detail storyboard

Basically, I'm trying to send an object from the master view to the detail view by way of a custom setter. However, as soon as the setter gets called in the detail view, the app immediately crashes. I'm not too good at debugging, but I think it's because the object isn't making it to the setter -- it's seeing it as nil. Could be wrong about that, though.
Here's the code for the segue in the master view:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
CCNewsItem *theNewsItem = self.listOfNewsItems[indexPath.row];
NSLog(#"Preparing to pass: %#", theNewsItem);
[[segue destinationViewController] setSelectedNewsItem:theNewsItem];
}
}
And here it is for the setter in the detail view:
- (void)setSelectedNewsItem:(CCNewsItem *)newItem
{
if (self.selectedNewsItem != newItem) {
self.selectedNewsItem = newItem;
// Update the view.
[self configureView];
}
}
It crashes exactly on the - (void)setSelectedNewsItem:(CCNewsItem *)newItem line. I've made super-sure that the object being passed is valid (I log it to double check, and all seems well), but it seems that it either isn't making it to the setter or it's somehow exploding when it hits it.
Any ideas? Thanks!
As requested, here's the logs/exception
This is what my NSLog shows on the object I'm trying to pass:
2013-11-02 14:18:23.660 Curtis Consulting[14862:60b] Preparing to pass: <CCNewsItem: 0x14e70580>
When the app stops on the setter's first line (I'm not sure why; I have all breakpoints disabled), it shows the values of newItem (the passed object) as:
newItem CCNewsItem * nil 0x00000000
NSObject NSObject
_headline NSString * nil
_body NSString * nil
_url NSURL * nil
Edit 2
Since, as near as I can tell, there is no way to copy the error it's showing (in a green bar at the right, but I don't have any breakpoints turned on), here's my transcription of it:
Thread 1: EXC_BAD_ACCESS (code=2, address=0x27c84ff8)
In your setter method change:
if (self.selectedNewsItem != newItem) {
self.selectedNewsItem = newItem;
to
if (_selectedNewsItem != newItem) {
_selectedNewsItem = newItem;
Because your current code has an infinite loop calling the accessor method. Be sure you know what using self. means...

Resources