I have a tab bar app with 3 tabs. On each one I implement FB through an UIActionSheet. When I debug on my phone, the first time I run the app it asks me to login, then says redirecting, and then the window disappears. I wait, but the publish to wall page won't show. If I click the button it will pull up the fb window and say redirecting and then close again. Then, if I close the app all the way, restart and click it; it does perfect. Any suggestions? Here's .m for one of the view controllers. All view controllers are same except for the void post to wall statement and of course the website they load in the viewdidload.
.m
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;
- (IBAction)done {
[self.parentViewController dismissModalViewControllerAnimated:YES];
}
-(IBAction)action:(id)sender {
UIActionSheet *popup1 = [[UIActionSheet alloc] initWithTitle:@"Share article with a friend..." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Email Entire Article", @"Email Link To Article", @"Facebook", nil];
[popup1 showInView:self.tabBarController.view];
[popup1 release];
}
- (void)actionSheet:(UIActionSheet *)popup1 clickedButtonAtIndex:(NSInteger)buttonIndex {
switch (buttonIndex) {
case 0:
{self.currentURL = steve.request.URL.absoluteString;
self.text = [steve stringByEvaluatingJavaScriptFromString:@"document.body.innerText"];
Class mailClass = (NSClassFromString(@"MFMailComposeViewController"));
if (mailClass != nil)
{
if ([mailClass canSendMail])
{
[self displayComposerSheet2];
}
else
{
[self launchMailAppOnDevice];
}
}
else
{
[self launchMailAppOnDevice];
}
}
break;
case 1:
{self.currentURL = steve.request.URL.absoluteString;
self.title = [steve stringByEvaluatingJavaScriptFromString:@"document.title"];
Class mailClass = (NSClassFromString(@"MFMailComposeViewController"));
if (mailClass != nil)
{
if ([mailClass canSendMail])
{
[self displayComposerSheet];
}
else
{
[self launchMailAppOnDevice];
}
}
else
{
[self launchMailAppOnDevice];
}
}
break;
case 2:
{self.currentURL = steve.request.URL.absoluteString;
self.title = [steve stringByEvaluatingJavaScriptFromString:@"document.title"];
_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.
}
break;
}
}
-(void)displayComposerSheet
{
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
[picker setSubject:@"Your friend is sharing this article"];
// Set up recipients
NSArray *toRecipients = [NSArray arrayWithObject:@"friend@example.com"];
[picker setToRecipients:toRecipients];
// Fill out the email body text
NSString *emailBody = [[[[[[self.title stringByAppendingString:@"\n"] stringByAppendingString:@"\n"] stringByAppendingString:@"Click the link below to read this article by Jeff Jenkins"] stringByAppendingString:@"\n"] stringByAppendingString:@"\n"] stringByAppendingString:self.currentURL];
[picker setMessageBody:emailBody isHTML:NO];
[self presentModalViewController:picker animated:YES];
[picker release];
}
-(void)displayComposerSheet2
{
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
[picker setSubject:@"Your friend is sharing this article"];
// Set up recipients
NSArray *toRecipients = [NSArray arrayWithObject:@"friend@example.com"];
[picker setToRecipients:toRecipients];
// Fill out the email body text
NSString *emailBody = [[[@"Check out this article by Jeff Jenkins!" stringByAppendingString:@"\n"] stringByAppendingString:@"\n"] stringByAppendingString:self.text];
[picker setMessageBody:emailBody isHTML:NO];
[self presentModalViewController:picker animated:YES];
[picker release];
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
switch (result)
{
case MFMailComposeResultCancelled:{
UIAlertView *cancelled = [[UIAlertView alloc] initWithTitle:@"Results" message:@"Email Was Cancelled" delegate:self cancelButtonTitle:@"Okay" otherButtonTitles:nil];
[cancelled show];
[cancelled release];}
break;
case MFMailComposeResultSaved:{
UIAlertView *saved = [[UIAlertView alloc] initWithTitle:@"Results" message:@"Email Draft Saved" delegate:self cancelButtonTitle:@"Okay" otherButtonTitles:nil];
[saved show];
[saved release];}
break;
case MFMailComposeResultSent:{
UIAlertView *sent = [[UIAlertView alloc] initWithTitle:@"Results" message:@"Email Sent" delegate:self cancelButtonTitle:@"Okay" otherButtonTitles:nil];
[sent show];
[sent release];
}
break;
case MFMailComposeResultFailed:{
UIAlertView *failed = [[UIAlertView alloc] initWithTitle:@"Results" message:@"Email Failed To Send, Please Check Internet Connection And Try Again." delegate:self cancelButtonTitle:@"Okay" otherButtonTitles:nil];
[failed show];
[failed release];
}
break;
}
[self dismissModalViewControllerAnimated:YES];
}
-(void)launchMailAppOnDevice
{
NSString *recipients = @"mailto:friend@example.com&subject=Your friend wants you to read this article";
NSString *body = @"&body=My Guest Information";
NSString *email = [NSString stringWithFormat:@"%@%@", recipients, body];
email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:email]];
}
- (void)touchesEnded: (NSSet *)touches withEvent: (UIEvent *)event {
for (UIView* view in self.view.subviews) {
if ([view isKindOfClass:[UITextField class]])
[view resignFirstResponder];
}
}
- (void)viewDidLoad {
[steve loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.316apps.com/jeffsblog.html"]]];
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:@selector(tick) userInfo:nil repeats:YES];
static NSString* kApiKey = @"REMOVEDFORPRIVACY";
static NSString* kApiSecret = @"REMOVEDFORPRIVACY";
_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];
}
- (void)webView:(UIWebView *)steve didFailLoadWithError:(NSError *)error {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Connection Error" message:@"Sorry You Need an Internet Connection For This Application to Work" delegate:self cancelButtonTitle:@"Okay" otherButtonTitles:nil];
[alert show];
[alert release];
}
- (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 Jeff Jenkins\",\"description\":\"\",\"media\":[{\"type\":\"image\",\"src\":\"http://www.316apps.com/jeffjenkins.jpg\",\"href\":\"http://jeffajenkins.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
In my app I use the FBLoginButton rather than the FBLoginDialog directly, but I had issues until I manually set the session of the login button like this:
Code:
[_loginButton setSession:_session];
;
I noticed that FBLoginDialog has an init method where you can pass in the session, so maybe you could try creating your with that, so change:
It worked great...almost. On all of the view controllers that implement a facebook connect using the UIActionSheet, there were no problems. It logged in, grabbed the correct title and URL from the NSString, and pulled publish to wall on the first go.
The almost part requires a bit more info from me. I forgot that I have another View setup that uses FBConnect, but from an IBAction, not a UIActionSheet. If I leave the code
Code:
_loginDialog = [[FBLoginDialog alloc] init];
it will log in, then nothing. I have to stay logged in, restart the app, then click the button and since I'm still logged in it will pull the posttoWall correctly. If I change the code again to:
In the viewcontroller that uses the ibaction the code is exactly the same as the case code in the uiactionsheet code. It checks for the session first and has you login if no login is detected. I didn't run the debugger but it would log me in then say redirecting then take me right back to the home screen.
Here's the code for the view controller that uses an IBAction instead of UIActionSheet to login to FBConnect
Code:
#import "FirstViewController.h"
#import <QuartzCore/QuartzCore.h>
@implementation FirstViewController
@synthesize session = _session;
@synthesize postGradesButton = _postGradesButton;
@synthesize logoutButton = _logoutButton;
@synthesize loginDialog = _loginDialog;
@synthesize facebookName = _facebookName;
@synthesize posting = _posting;
- (IBAction)switchfifteen {
[self presentModalViewController:fifteenthView animated:YES];
}
- (IBAction)maps; {
NSString *name = @"Church of Christ";
NSString *latlong = @"29.909509,-96.856665";
NSString *url = [NSString stringWithFormat: @"http://maps.google.com/maps?q=%@&mrt=yp&ll=%@",
[name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
[latlong stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
}
- (IBAction)email {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto://office@lagrangecoc.com"]];
}
/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
// 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";
static NSString* kApiSecret = @"REMOVED";
_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];
}
- (IBAction)fbConnect:(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\":\"test\",\"href\":\"http://www.google.com\",\"caption\":\"Check out this article by Scott Elliott\",\"description\":\"\",\"media\":[{\"type\":\"image\",\"src\":\"http://www.316apps.com/lagrange.jpg\",\"href\":\"http://resurrectedliving.wordpress.com\"}]}"];
[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
Here is what Debugger said:
Code:
2010-09-10 10:21:33.844 LaGrangeCoC[15486:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFDictionary setObject:forKey:]: attempt to insert nil value (key: session_key)'
I googled that error and it seems to have something to do with your _session, I'm just throwing things out there, but I see you have an IBAction with a _session logout, you didn't accidently attach that to your post button did you?
Can you try this, delete the app from your device and then reinstall it so you remove any of the stuff FBConnect has saved in NSUserDefaults. Then go straight to that view and try to login, let me know if that works.
I'm reading some stuff that it may because by using the session in multiple views, just want to find out if that is the problem.
Still messing up. If I go to one of the view controllers that is using UIActionSheet to login first, it will perform perfectly. Then, if I go to the view controller with IBAction it will let me post because it is still logged in. But, if I go to one with IBAction first it crashes when trying to login if I have the initWithSession code called, if I just init the login.dialog it will login, but not pull up the post to wall. I've tried cleaning targets and everything.
Would not having a Post-authorize URL set up in Facebook on the app cause this problem? I checked and the app I have made for this doesn't even have a place for me to put a Post-Authorize URL, but others I have made do.
Here's the latest situation. I have 2 similar apps. On both there is one view controller with IBACtion for fb, and one view controller with UIActionSheet. On the BellAvenue2 file, it works fine: on both. BellAvenue2 has the initWithSession:_session code in the logindialog and has no problems.
LaGrange on the other hand also has initWithSession:_session code and it crashes every time. Could someone take a look at these and see what the difference is? I've been going through stuff for hours and have no clue. Again BellAvenue2 runs the code fine. LaGrange crashes and I have no clue why. I would greatly appreciate a 2nd pair of eyes.