Passing an object between 2 view controllers not working - iphone

I'm trying to pass 3 objects to another VC but they are showing up as Null in the destination VC.
VC1:
- (void)specificExerciseTableViewController:(SpecificExerciseTableViewController *)specificExerciseTableViewController didSelectSpecificExerciseWithURL:(NSString *)exerciseURL muscleName:(NSString *)muscleName muscleURL:(NSString *)muscleURL;
{
[self addExercise];
}
-(void)addExercise
{
PFObject *exerciseInRoutine = [[PFObject alloc] initWithClassName:#"exerciseInRoutine"];
[exerciseInRoutine setObject:self.selectedExercise forKey:#"name"];
[exerciseInRoutine setObject:self.muscleName forKey:#"muscle"];
[exerciseInRoutine setObject:self.muscleURL forKey:#"picture"];
[exerciseInRoutine saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
[self.tableView reloadData];
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
The class that is passing the objects to VC1:
if ([self.delegate respondsToSelector:#selector(specificExerciseTableViewController:didSelectSpecificExerciseWithURL:muscleName:muscleURL:)])
{
NSString *exerciseName = [[self.exerciseArray objectAtIndex:selectedRowIndex.row] objectForKey:#"exerciseName"];
[self.delegate specificExerciseTableViewController:self didSelectSpecificExerciseWithURL:exerciseName muscleName:self.muscleName muscleURL:self.muscleURL];
[self dismissModalViewControllerAnimated:YES];
}
Edit:
I've updated my method to set the objects to the VC's properties, but have same problem:
- (void)specificExerciseTableViewController:(SpecificExerciseTableViewController *)specificExerciseTableViewController didSelectSpecificExerciseWithURL:(NSString *)exerciseURL muscleName:(NSString *)muscleName muscleURL:(NSString *)muscleURL;
{
self.muscleName = exerciseURL;
self.muscleName = muscleName;
self.muscleURL = muscleURL;
[self addExercise];
}

What do you expect to happen when you execute the code shown above?
You second code block calls -specificExerciseTableViewController:didSelectSpecificExerciseWithURL:muscleName:muscleURL: on some delegate but the implementation of that method in your "VC1" example ignores the arguments passed to that method. You don't seem to be doing anything with the data you are given.

The problem is you are passing arguments to the delgate object.but inside the delegate method you didnt either pass the arguments to the function addExercise or assign the parameters to your properties before you call the metho.

Related

Two Independent Delegate Methods in a Class

I have two independent delegate methods in a class.
- (void)delegateMethod1:(id)data {
self.data = data;
}
- (void)delegateMethod2 {
[someClass sendData:self.data];
}
Now, this works fine sometimes but the other times, delegateMethod2 gets called before delegateMethod1.
I need to know how to manage this elegantly so that the line: [someClass sendData:self.data]; gets called only when both delegateMethod1 and delegateMethod2 have been called.
I know I can do it by using a variable to set to something on each delegate call but there has to be an elegant way to do this.
Any help?
Remembering which delegate has been called seems the easiest and cleanest solution to me.
But you can make it symmetric by moving the check to a separate method, so that
is does not matter which delegate is called first:
- (void)checkIfDataCanBeSent {
if (self.method1called && self.method2called) {
[someClass sendData:self.data];
}
}
- (void)delegateMethod1:(id)data {
self.method1called = YES;
// ...
[self checkIfDataCanBeSent];
}
- (void)delegateMethod2 {
self.method2called = YES;
// ...
[self checkIfDataCanBeSent];
}
(I have assumed that all delegate methods are called on the main thread, otherwise
one would have to add some synchronization.)
I believe, using a indicative variable to be the most elegant way to get over this. But this variable has to be kept in the delegate caller object.
Pseudo-type explanation
#interface DelegateCaller
{
BOOL hasCalled1stMethod;
}
#property(nonatomic,weak) id delegate;
#end
#implementation DelegateCaller
-(void)in_some_process_1
{
[self.delegate delegateMethod1]; //call
hasCalled1stMethod = YES; //set indicator
}
-(void)in_some_process_2
{
if(hasCalled1stMethod)
{
[self.delegate delegateMethod2]; //call
hasCalled1stMethod = NO; //reset indicator for reuse, if required.
}
}
#end
This way you'll not have to maintain any variable in the delegate itself, because the regulation of calling is maintained in the caller-object itself.
Another case:
If the delegateMethod1 is called from some object1 and the delegateMethod2 is called from some other object2, then again the indicative variable method is the most elegant way (in this limited scenario)
Pseudo-type explanation:
#interface ClassDelegateObject //aka the callee
{
BOOL hasCalledMethod1;
}
#end
#implementation ClassDelegateObject
-(void)delegateMethod1:(NSData*)data
{
self.data = data;
hasCalledMethod1 = YES; //set the indicator.
}
-(void)delegateMethod2
{
//here relying on the self.data!=nil will not be fruitful
//in case the self.data is not nil and hold some previous garbage data then
//this logic will fail.
if(hasCalledMethod1)
{
[someClass sendData:self.data];
hasCalledMethod1 = NO; //reset the variable for reuse if required.
}
}
#end
I would suggest that you rethink how the code works. Maybe you can check if there is no data and if so send it once it is ready:
- (void)delegateMethod1:(id)data {
self.data = data;
if (self.dataShouldBeSentWhenReady) {
[self sendData];
}
}
- (void)delegateMethod2 {
if (self.data) {
[self sendData];
} else {
[self setDataShouldBeSentWhenReady:YES];
}
}
- (void)sendData {
[self setDataShouldBeSentWhenReady:NO];
[someClass sendData:self.data];
}

Segue crashing - debugger doesn't display any useful error

UPDATE:
Ok, I managed to log the exception:
[<TweetDetailViewController 0x685f9f0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key toolbar.
I have a toolbar in the target controller view. Could that be causing the problem?
I have no idea what the heck is going on. I have not changed segues at all and they worked perfectly few minutes ago. I was changing some unrelated things in a detail view controller and now the app crashes during segue from the root to the view controller.
I am trying to debug this. Here is where the code crashes:
Step over:
Step over:
Step over:
Ok, here is the setter method which is being called in prepareForSegue:
- (void)setTweet:(Tweet *)newTweet
{
if (_tweet != newTweet) {
_tweet = newTweet;
}
}
I call a configuration method when entering the detail controller:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self configureView];
}
- (void)configureView
{
// Update the user interface for the tweet detail page
if (_tweet) {
self.tweetUserName.text = _tweet.userName;
self.tweetCreatedAt.text = _tweet.createdAt;
self.tweetText.contentInset = UIEdgeInsetsMake(-4,-8,0,0);
self.tweetText.text = _tweet.text;
if (_tweet.retweeted == YES) {
[self.retweetButton setTintColor:[UIColor grayColor]];
[self.retweetButton setEnabled:NO];
}
if (!_tweet.userProfileImage) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *imageUrl = _tweet.userProfileImageUrl;
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
dispatch_async(dispatch_get_main_queue(), ^{
self.tweetUserProfileImage.image = [UIImage imageWithData:data];
});
});
}
else {
self.tweetUserProfileImage.image = _tweet.userProfileImage;
}
}
}
Copied from comments, as an answer once the actual exception was revealed:
Most likely the TweetDetailViewController in your storyboard has some connection that links to an outlet called "toolbar" but the code for that class doesn't have a property with that name. That's the usual reason at least for that kind of error when a view controller is involved.
Try enabling control over the Zombie, to see if the debug gives you more details about the problem.
On edit scheme under the heading "Envirorment Variables" add NSZombieEnabled = YES
Please try and see if you have more details

Dropbox Api in iphone

I am trying to integrate dropbox with iphone and done with login option.I want to load folders and files on root path in table view just after login.
code for this is
- (void)viewWillAppear:(BOOL)animated {
[self.restClient loadMetadata:#"/"];
}
This loadMetadata is method in DBRestClient.m class.I managed to print json value on console
Code is
- (void)parseMetadataWithRequest:(DBRequest*)request {
NSAutoreleasePool* pool = [NSAutoreleasePool new];
NSDictionary* result = (NSDictionary*)[request resultJSON];
DBMetadata* metadata = [[[DBMetadata alloc] initWithDictionary:result] autorelease];
[self performSelectorOnMainThread:#selector(didParseMetadata:) withObject:metadata waitUntilDone:NO];
NSLog(#"Meta data is :%#",result);
[pool drain];
}
How could I use this result value in my view controller to display this in tableview?
Get the contents of a directory and put them into an array.
- (void)restClient:(DBRestClient*)client loadedMetadata:(DBMetadata*)metadata
{
for (DBMetadata* child in metadata.contents)
{
if (child.isDirectory)
{
[self.restClient loadMetadata:child.path withHash:hash];
}
else
{
if ( [self.directory isEqualToString:#"a directory"] ) {
/* child.path is the file, so put it into an array */
}
}
}
}
Than load your array into your UITableView as you would any other NSArray / NSMutableArray

UIProgressView update fail with UINotification

I try to solve this problem for several days now I have to ask you...
I've got a View (and a ViewController) with a UITableview. There is a TableViewController for that table which is generated in the ViewController. The TableViewController calls a DataSyncManager sharedInstant object (which is obviously in a separate class) which starts to sync data with the server.
I do it this way (first the refresh method):
-(void) refresh{
[serverQueueProgressView setProgress:0.0];
[syncingLabel setAlpha:0.5];
[serverQueueProgressView setAlpha:1];
[self performSelector:#selector(reloadTableViewDataSource) withObject:nil afterDelay:1.0];
}
Then the method reloadTableViewDataSource (of TableViewController) is called:
- (void)reloadTableViewDataSource
{
[dataSyncManager getEntriesFromServer];
}
dataSyncManager is my sharedInstance.
In the getEntriesFromServer method of dataSyncManager I do the loop with different sync items and call everytime
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ServerQueueProgress"
object:progress];
with the proper progress as NSNumber (that part works well). The message is now sent and catched by my ViewController (it works, I checked with a breakpoint, it also gets the right progress-NSNumber and converts it to float):
- (void)serverQueueProgress:(NSNotification *)notification {
if(![NSThread isMainThread])
{
[self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO];
return;
}
[queueProgressView setProgress:[[notification object] floatValue]];
}
This is one solution which I found here on stackoverflow. But the if is always skipped because obviously I'm on main thread.
Unfortunately the UIProgressview doesn't get updated, it just hangs around, but I connected it well in Interface Builder (I checked that by setting the progress in another method of ViewController.
I also tried to catch the Notification with my TableViewController and put in some other solutions, but no chance, the UIProgressView doesn't get updated live. Only after the sync is done.
Here is the mentioned code in TableViewController which also gets executed without errors (I also stepped it to make sure every line gehts executed well):
This is the method called when received a the notification:
- (void)serverQueueProgress:(NSNotification *)notification {
[self performSelectorOnMainThread:#selector(updateProgress:) withObject:[notification object] waitUntilDone:NO];
[serverQueueProgressView setProgress:[[notification object] floatValue]];
}
Which also calls updateProgress: of the same class:
- (void)updateProgress:(NSNumber *)newProgressValue {
[serverQueueProgressView setProgress:[newProgressValue floatValue]];
}
No chance. I tried many ways and implemented some in parallel as you see, but the ProgressView won't get updated live. Only at the end of syncing. What am I doing wrong??
EDIT: Here is my getEntriesFromServer and some other stuff in DataSyncManager:
- (void)getEntriesFromServer
{
[[NSNotificationCenter defaultCenter]
postNotificationName:#"SynchingStarted"
object:nil];
[self completeServerQueue];
...
}
and completeServerQueue is the function which sends messages to my ViewController with the proper progress float value (it's only a dummy for loop, which gets executed properly... I've checked it):
- (void)completeServerQueue {
NSNumber *progress = [[NSNumber alloc] init];
for (int i=0; i<15; i++) {
progress = [[NSNumber alloc] initWithFloat:(100/15*i) ];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ServerQueueProgress"
object:progress];
sleep(1);
}
}
also, when you're having trouble, break the problem down a bit. Instead of:
[serverQueueProgressView setProgress:[[notification object] floatValue]];
do this;
float prog = [notification object] floatValue];
[serverQueueProgressView setProgress:prog];
then debugging would give a clue that this isn't working.
my guess is the problem isn't the code you've shown here, but other code in getEntriesFromServer. Are you using NSURLConnection? Something like:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
then you will get callbacks asynchronously that you can use to update your progress view.

If I wanted to write my own KVO-compliant setter method, would it look something like this?

- (void)setFirstName:(NSString*)firstNameValue {
[self willChangeValueForKey:#"firstName"];
[firstName release];
firstName = firstNameValue;
[firstName retain];
[self didChangeValueForKey:#"firstName"];
}
Is that right? So the willChange... foobar didChange... block causes an KVO notification to fire?
No, your implementation is not 100% correct. Think what happens if the firstName is currently set to a NSString-instance and the setter is called with that very same instance. First you will release the instance, than you will set the instance variable, which in this case dosen't change anything and than you try the retain the instance, but by that time it could very well been dealloced already.
It should be:
- (void)setFirstName:(NSString*)firstNameValue {
[self willChangeValueForKey:#"firstName"];
[firstNameValue retain];
[firstName release];
firstName = firstNameValue;
[self didChangeValueForKey:#"firstName"];
}
or:
- (void)setFirstName:(NSString*)firstNameValue {
if (firstNameValue != firstName) {
[self willChangeValueForKey:#"firstName"];
[firstName release];
firstName = firstNameValue;
[firstName retain];
[self didChangeValueForKey:#"firstName"];
}
}
The latter version has the additional advantage of not sending oberserver-notifications if the value is not really changed.
Yes - calling didChangeValueForKey: will notify all the observers that the value has changed.