Nightmare problem. Same code; works on one view, not on the other: in the SAME APP
This is just weird. I have an app that uses an IBAction to login and post to facebook wall. I was having problems where it would crash on me after loggin in on one view, but the other view would log in just fine. I thought maybe I made a typo somewhere so I completely copied and pasted the code from the working view into the one that wasn't working. They are completely identical, except for me changing the import header file names and the implementation names to be for the correct view. It STILL messed up. Exactly the same code in 2 views, one works one doesn't. How can this be?
Code:
#import "FirstViewController.h"
#import <QuartzCore/QuartzCore.h>
@implementation FirstViewController
@synthesize activity;
@synthesize postentry;
@synthesize currentURL;
@synthesize title;
@synthesize session = _session;
@synthesize postGradesButton = _postGradesButton;
@synthesize logoutButton = _logoutButton;
@synthesize loginDialog = _loginDialog;
@synthesize facebookName = _facebookName;
@synthesize posting = _posting;
@synthesize text;
- (void)viewDidLoad {
[steve loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://resurrectedliving.wordpress.com"]]];
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:@selector(tick) userInfo:nil repeats:YES];
// Set these values from your application page on http://www.facebook.com/developers
// Keep in mind that this method is not as secure as using the sessionForApplication:getSessionProxy:delegate method!
// These values are from a dummy facebook app I made called MyGrades - feel free to play around!
static NSString* kApiKey = @"REMOVED ON PURPOSE";
static NSString* kApiSecret = @"REMOVED ON PURPOSE";
_session = [[FBSession sessionForApplication:kApiKey secret:kApiSecret delegate:self] retain];
// Load a previous session from disk if available. Note this will call session:didLogin if a valid session exists.
[_session resume];
[super viewDidLoad];
}
-(void)tick {
if(!steve.loading)
[activity stopAnimating];
else
[activity startAnimating];
}
- (IBAction)postGradesTapped:(id)sender {
_posting = YES;
// If we're not logged in, log in first...
if (![_session isConnected]) {
self.loginDialog = nil;
_loginDialog = [[FBLoginDialog alloc] initWithSession:_session];
[_loginDialog show];
}
// If we have a session and a name, post to the wall!
else if (_facebookName != nil) {
[self postToWall];
}
// Otherwise, we don't have a name yet, just wait for that to come through.
}
- (IBAction)logoutButtonTapped:(id)sender {
[_session logout];
}
#pragma mark FBSessionDelegate methods
- (void)session:(FBSession*)session didLogin:(FBUID)uid {
[self getFacebookName];
}
- (void)session:(FBSession*)session willLogout:(FBUID)uid {
_logoutButton.hidden = YES;
_facebookName = nil;
}
#pragma mark Get Facebook Name Helper
- (void)getFacebookName {
NSString* fql = [NSString stringWithFormat:
@"select uid,name from user where uid == %lld", _session.uid];
NSDictionary* params = [NSDictionary dictionaryWithObject:fql forKey:@"query"];
[[FBRequest requestWithDelegate:self] call:@"facebook.fql.query" params:params];
}
#pragma mark FBRequestDelegate methods
- (void)request:(FBRequest*)request didLoad:(id)result {
if ([request.method isEqualToString:@"facebook.fql.query"]) {
NSArray* users = result;
NSDictionary* user = [users objectAtIndex:0];
NSString* name = [user objectForKey:@"name"];
self.facebookName = name;
_logoutButton.hidden = NO;
[_logoutButton setTitle:[NSString stringWithFormat:@"Facebook: Logout as %@", name] forState:UIControlStateNormal];
if (_posting) {
[self postToWall];
_posting = NO;
}
}
}
#pragma mark Post to Wall Helper
- (void)postToWall {
FBStreamDialog* dialog = [[[FBStreamDialog alloc] init] autorelease];
dialog.userMessagePrompt = @"Enter your message:";
dialog.attachment = [NSString stringWithFormat:@"{\"name\":\"%@\",\"href\":\"%@\",\"caption\":\"Check out this article by Scott Elliott\",\"description\":\"\",\"media\":[{\"type\":\"image\",\"src\":\"http://www.316apps.com/lagrange.jpg\",\"href\":\"http://resurrectedliving.wordpress.com\"}]}",
self.title, self.currentURL];
[dialog show];
}
#pragma mark Memory Cleanup
- (void)viewDidUnload {
self.postGradesButton = nil;
self.logoutButton = nil;
}
- (void)dealloc {
[_session release];
_session = nil;
[_postGradesButton release];
_postGradesButton = nil;
[_logoutButton release];
_logoutButton = nil;
[_loginDialog release];
_loginDialog = nil;
[_facebookName release];
_facebookName = nil;
[super dealloc];
}
@end
Th tutorial I used had them. Any ideas why it works in one view and not the other?
Usually code that seems perfect, but doesn't work, is due to a broken link in IB. Check all your outlet and action connections to the view controller that doesn't work. I bet you have one or more broken outlet links.
Failing that, step through the code in the debugger and check the values of your variables and the way the code branches.
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.
I've checked everything in IB and it is fine, nothing is broken. Also, when I click the button that IB is linked to, it starts off fine, pulls up the FBConnect window, lets me login, then says redirecting like it does right before pulling up the publish to wall window, then it crashes. Here is the debug console after I run it.
I've checked everything in IB and it is fine, nothing is broken. Also, when I click the button that IB is linked to, it starts off fine, pulls up the FBConnect window, lets me login, then says redirecting like it does right before pulling up the publish to wall window, then it crashes. Here is the debug console after I run it.
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFDictionary setObject:forKey:]: attempt to insert nil value (key: session_key)'
You're passing a nil value for "session_key" in a dictionary setObject call. Now run the app in the debugger and find the offending line.
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.
Here is the full code for the IBAction I am using to connect.
Code:
- (IBAction)postGradesTapped:(id)sender {
_posting = YES;
// If we're not logged in, log in first...
if (![_session isConnected]) {
self.loginDialog = nil;
_loginDialog = [[FBLoginDialog alloc] initWithSession:_session];
[_loginDialog show];
}
// If we have a session and a name, post to the wall!
else if (_facebookName != nil) {
[self postToWall];
}
// Otherwise, we don't have a name yet, just wait for that to come through.
}
Here is the full code for the IBAction I am using to connect.
Code:
- (IBAction)postGradesTapped:(id)sender {
_posting = YES;
// If we're not logged in, log in first...
if (![_session isConnected]) {
self.loginDialog = nil;
_loginDialog = [[FBLoginDialog alloc] initWithSession:_session];
[_loginDialog show];
}
// If we have a session and a name, post to the wall!
else if (_facebookName != nil) {
[self postToWall];
}
// Otherwise, we don't have a name yet, just wait for that to come through.
}
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.
I haven't had a lot of experience with debugging. Could you let me know how to trace the code in the debugger to figure out why, or point me to something that will explain how to do this?
I haven't had a lot of experience with debugging. Could you let me know how to trace the code in the debugger to figure out why, or point me to something that will explain how to do this?
There are quite few books that explain debugging. I recommend XCode 3 unleashed. It starts out simple and gets into more in-depth debugging tricks.
It's a deep subject, but here are few points to get you started.
Make sure you are set to build a debug version of the project (in the pop-up at the top left of the project window.)
In editing preferences, make sure you have "Show Gutter" and "Show Line numbers" selected.
Select the file who's code you want to debug. Click on the line number where you want the app to break. It should put a blue pointer over the line number. That adds a breakpoint at that line number.
Run your program. When it gets to a line number where you've added a breakpoint, it will stop and display a message "Stopped at breakpoint ..." At this point you should select "Debugger" from the run menu. That will show the current source file, with an arrow pointing to the line of code that is about to be executed. There will be a section with headers "variable", "Value" and "summary" The variable column will have entires for "Arguments", "Locals", "Globals," "Registers," etc. You'll mostly be interested in "Arguments" and "Locals". Locals shows the local variables defined at the current scope (the current method, and the current set of braces inside that method). "Arguments" lists arguments passed in to a method, as well as having a disclosure triangle for "self", which lets you see the current object's instance variables. When a variable contains a structure, or an object with properties, that variable will also have a disclosure triangle that lets you view the contents.
At the top of the debugger window are buttons to step into the next call, step out of the current method/function and return to the caller, continue running, etc. There is also a "show" item in the run menu that will let you view a list of breakpoints, and disable/add/delete breakpoints, as well as add debugger commands or log statements to your breakpoints.
There's much more to it than that, but that much is very powerful, and will let you examine variables and trace through your code.
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.
There are quite few books that explain debugging. I recommend XCode 3 unleashed. It starts out simple and gets into more in-depth debugging tricks.
It's a deep subject, but here are few points to get you started.
Make sure you are set to build a debug version of the project (in the pop-up at the top left of the project window.)
In editing preferences, make sure you have "Show Gutter" and "Show Line numbers" selected.
Select the file who's code you want to debug. Click on the line number where you want the app to break. It should put a blue pointer over the line number. That adds a breakpoint at that line number.
Run your program. When it gets to a line number where you've added a breakpoint, it will stop and display a message "Stopped at breakpoint ..." At this point you should select "Debugger" from the run menu. That will show the current source file, with an arrow pointing to the line of code that is about to be executed. There will be a section with headers "variable", "Value" and "summary" The variable column will have entires for "Arguments", "Locals", "Globals," "Registers," etc. You'll mostly be interested in "Arguments" and "Locals". Locals shows the local variables defined at the current scope (the current method, and the current set of braces inside that method). "Arguments" lists arguments passed in to a method, as well as having a disclosure triangle for "self", which lets you see the current object's instance variables. When a variable contains a structure, or an object with properties, that variable will also have a disclosure triangle that lets you view the contents.
At the top of the debugger window are buttons to step into the next call, step out of the current method/function and return to the caller, continue running, etc. There is also a "show" item in the run menu that will let you view a list of breakpoints, and disable/add/delete breakpoints, as well as add debugger commands or log statements to your breakpoints.
There's much more to it than that, but that much is very powerful, and will let you examine variables and trace through your code.
I still can't trace back why the _session is returning nil.
Tried building from scratch and still crashes because of session nil
Ok, where do you set the value of _session to something other than nil? Set a breakpoint there, and set a breakpoint in the code that uses session. Make sure _session gets set correctly, and then step through the code and see where it is getting set to nil.
On the simulator you can right click on a variable in the debugger and set a "watchpoint," which causes the debugger to watch a variable and detect when it changes. You might try that.
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.
In the ViewController.h I import the FBConnect.h that I downloaded from the FBConnect Source Code. Everything with _session comes from the FBSession files. I don't believe an error would be there, because the other ViewController that does work is set up the exact same way. As I've said before, it works in one, and not the other. The only other place I found where _session is set to anything is the dealloc where I have
Code:
[_session release];
_session=nil;
Also the _session resume code is working because if I restart the app and click the IBAction button it still has me logged in and pulls the Publish Story to Wall window up just fine. My head hurts from all this as I've been on it for 48 hours now.
In the ViewController.h I import the FBConnect.h that I downloaded from the FBConnect Source Code. Everything with _session comes from the FBSession files. I don't believe an error would be there, because the other ViewController that does work is set up the exact same way. As I've said before, it works in one, and not the other.
The only other place I found where _session is set to anything is the dealloc where I have
Code:
[_session release];
_session=nil;
That sounds like your culprit. Is that code called in the dealloc for your view controller? It sounds like you set up the session, and then when you dismiss the first view controller, you're setting the _session variable to nil;
Put a break point on EVERY line that sets session to nil, and see when it's being called. Really, the debugger is your friend. Learn to use it.
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.
Ok, this is turning into a lot of work and getting away from the original problem I had. The original problem was when I clicked the button it would pop up the login dialog, but then close without ever showing the Publish Story to Wall dialog. I was told that the initWithSession:_session would work, but obviously it is not. How else could I change this code below to log in, then automatically show the post to wall window?
Code:
- (IBAction)postGradesTapped:(id)sender {
_posting = YES;
// If we're not logged in, log in first...
if (![_session isConnected]) {
self.loginDialog = nil;
_loginDialog = [[FBLoginDialog alloc] init];
[_loginDialog show];
}
// If we have a session and a name, post to the wall!
else if (_facebookName != nil) {
[self postToWall];
}
// Otherwise, we don't have a name yet, just wait for that to come through.
}