Passing a PFObject between views is causing values to be null - ios

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

Related

Unable to pass delegate object to another class (Modal View) via IBAction

I have a UIBarButtonItem on my UINavigationBar named selectRoleButton. Within this class (AEFeedsViewController) I have a protocol:
#protocol AERoleSelectProtocol <NSObject>
- (void)presentLoginViewController;
#end
The selectRoleButton is hooked up to AERoleSelectViewController by a target action IBAction, which holds a container that contains AERoleSelectTableViewController.
Here is a picture of my Storyboard
Below is the IBAction for selectRoleButton:
- (IBAction)selectRole:(UIBarButtonItem *)sender
{
NSLog(#"Sender: %#", sender); // Unable to breakpoint, NSLog won't log...?
AERoleSelectTableViewController *roleViewControler = [self.storyboard instantiateViewControllerWithIdentifier:#"RoleTableVC"];
roleViewControler.delegate = self; // delegate is not being passed/set
[self presentViewController:roleViewControler animated:YES completion:nil];
}
So when tapping the button, it presents AERoleSelectViewController, with AERoleSelectTableViewController in a container. AERoleSelectTableViewController has a delegate property of
AERoleSelectTableViewController.h
#property (weak, nonatomic) id <AERoleSelectProtocol> delegate;
The IBAction should be passing and setting the delegate object, but it is nil. Breakpoints do not seem to work, it won't break at all on the method. I've even tried writing my own method and wiring the button to that, but the breakpoint still won't catch it. I also cannot print anything using NSLog method for some reason.
In AERoleSelectTableViewController.m, my didSelectRowAtIndexPath: has a switch statement, one case uses the delegate and calls a method on AEFeedsViewController which conforms to the AERoleSelectProtocol.
AERoleSelectTableViewController.m
...
if (self.delegate) {
[self.delegate presentLoginViewController];
}
But since the delegate is nil, it obviously never gets called.
Edit:
I have tried disabling the target action IBAction on the selectRole button and using a segue to present it modally.
My segue method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showRoleView"])
{
NSLog(#"Sender: %#", sender);
AERoleSelectTableViewController *roleViewController = (AERoleSelectTableViewController *)[segue destinationViewController];
roleViewController.delegate = self;
}
}
The method is being called, I can breakpoint and it is crashing on
roleViewController.delegate = self;
With the crash log:
[AERoleSelectViewController setDelegate:]: unrecognized selector sent to instance
As mentioned above, I have a delegate property on AERoleSelectTableViewController.h so it should be passing no problem. But it crashes instantly, viewDidLoad doesn't even get called on AERoleSelectTableViewController.
Is this something to do with how I'm setting it on a class within a container? I tried setting the delegate on the container class (AERoleSelectTableViewController) on [segue destinationViewController] and when viewDidLoad gets called, the delegate object is set, but only the container view is displayed (the just the tableView, no background etc, RoleSelectViewController doesn't present at all, which is expected given this code.
When setting the[segue destinationViewController] to the class which holds the container (RoleSelectViewController), the containers' class viewDidLoad is called first. So the delegate is not set. Then viewDidLoad gets called on the class holding the container, with the delegate set successfully. Am I able to pass the delegate from the class which holds the container, to the container even though the container gets called first? Is using a container even the correct approach? Is a container needed or can I just put a tableView straight in there?

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.

Is it safe to import a view controller to another controller for segueing?

I am currently going through the iTunes U Stanford iOS dev. course and I am trying to utilize segues.
In my prepareForSegue method I am trying to update the data on the transitioning VC and this is my code:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"changeToScreen2"])
{
if([segue.destinationViewController isKindOfClass:[Screen2ViewController class]])
{
"Code to be implemented"
}
}
}
But my Screen2ViewController isn't recognized. Is it safe and proper coding technique to import a view controller to another view controller for segueing purposes or is there another method I should implement?
-------------------------------------------------------------------------------------
I have a new problem now
When I set the values of a UILabel and UITextView with the aforementioned prepareForSegue method and change to Screen2ViewController the labels and text views have not be updated with the values that I have added.
Screen2ViewController *S2VC = (Screen2ViewController *)segue.destinationViewController;
S2VC.myLabel.text = #"Screen 2 is now being viewed";
S2VC.uneditableText.text = #"Why aren't you showing up when I push you";
But these values don't get updated.
Yes it is safe to import view controllers. There are a few caveats however,
Do not import 2 headers into each other, this will cause non-obvious error.
Screen1ViewController.h
#import "Screen2ViewController.h"
Screen2ViewController.h
#import "Screen1ViewController.h"
Import in the .m file instead
Screen1ViewController.h
#import "Screen2ViewController.h"
Screen2ViewController.h
//No imports
Screen2ViewController.m
#import "Screen1ViewController.h"
As a general rule I try to put all the imports in the .m file: both for encapsulation and the above reason. You can also foreword declare a class if you need to use both classes in both header files.
About your new problem: you can only update instances from another view controller if they're made public (in other words, they're declared in its header file). So, with the provided code, you'd need to make myLabel and uneditableText public. However, during prepareForSegue: execution they were not yet allocated. As all you need from those objects is editing their text, it would be better to define two NSString's in the second view controller and then, inside that VC's implementation, you assign them to the objects. Example:
First View Controller
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"changeToScreen2"])
{
if([segue.destinationViewController isKindOfClass:[Screen2ViewController class]])
{
Screen2ViewController *S2VC = (Screen2ViewController *)segue.destinationViewController;
S2VC.labelText = #"Screen 2 is now being viewed";
S2VC.textViewText = #"Why aren't you showing up when I push you";
}
}
}
Second View Controller's Header
...
#property (nonatomic, strong) NSString *labelText;
#property (nonatomic, strong) NSString *textViewText;
...
Second View Controller's Implementation File
...
- (void)viewDidLoad
{
[super viewDidLoad];
self.myLabel.text = self.labelText;
self.uneditableText.text = self.textViewText;
}
...
Needless to say you must have previously used the Interface Builder to add myLabel and uneditableText as #property's of your Second View Controller.

Passing NSArray makes array empty

i'm trying to pass an array from a view to another using the PrepareForSegue method.
In the first view i got a button called "Submit" that, if pressed, reads a textView and store the text in a NSArray, and then should pass this array to another view (push segue), but when the array arrives is empty.
Here is the code
//.h
#property (strong, nonatomic) NSArray *words;
//.m
- (IBAction)Submit:(id)sender{
//read textView
_words = [self.myTextView.text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//if Submit is pressed go to SecondViewController
if ([segue.identifier isEqualToString:#"secondSegue"]) {
SecondViewController *vc = [segue destinationViewController];
vc.array = _words;
}
}
So here is the code of the SecondViewController that receives the array
- (void)viewDidLoad
{
[self Calculate];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(void)Calculate
{
int size = [array count];
NSLog(#"size is %d",size);
}
the log always says "size is 0".
Did i make some mistake?
Could it be that the Segue happens before i can read the TextView and fill the array so it's always empty?
thanks in advance!
EDIT:
I tried to NSLog the Submit action and i discovered that the program never accesses to it, so it never reads! (p.s. yes i connected the button)
So the segue happens before the Submit action
How can i solve? can i copy the PrepareForSegue code in the Submit action?
Try copying the array when you send it, Could be getting freed also are you sure -(IBAction)submit.. is being called?
Also the submit button I assume has the action that performs the segue . I'm not sure on the order of execution. Try calling the method Submit from prepare for segue and not from the button. That way you can guarantee it is being called first.
Remove the #synthesize, the compiler will automatically synthesize
the ivar to be _words
Use self.words when assigning and accessing the 'words' array. This ensures the ivar gets set properly as well, if you plan to refer to it directly (which you won't in most cases).
Your problem is that #synthesize creates an ivar called 'words', not '_words'.

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