I'm having troubles developing an iOS 5 App with Facebook SDK integration.
My App has many ViewsController and for each of them I want to get different data from Facebook using different requests (of course).
At the moment I can get data only on the AppDelegate (following the official tutorial) but I need to get data in the view controller.
I tried 2 option:
1) Instantiate different Facebook instances on each ViewController
-It dosent' work, I don't know why but it seems that
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation;
is never called
2 Instantiate the Facebook instance in the AppDelegate and then use that instance in ViewControllers
- It don't works because with this code (found here: How do I persist a Facebook instance in multiple IOS view classes?)
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
Facebook *facebook = [appDelegate facebook];
}
facebook is nil.
Which is the proper way to work?
(I will edit my answer if you give me after that more feedback.)
I haven't used a lot the Facebook SDK but I remember having that problem when I tried.
The - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation implementation should be in your app delegate
If you need to access your Facebook instance in just one viewController (let's say MyFacebooViewController)
You can have something like this
#import <UIKit/UIKit.h>
#import "FBConnect.h"
#interface MyFacebookViewController : UIViewController <FBSessonDelegate, FBRequestDelegate>
#property (nonatomic, strong) Facebook *facebook;
...
#end
And your .m file should contain this
#import "MyFacebookViewController.h"
#import "MyAppDelegate.h"
#implementation MyFacebookViewController
#synthesize facebook = _facebook;
-(Facebook *)facebook
{
if(!_facebook){
_facebook = [[Facebook alloc] initWithAppId:MY_APP_ID andDelegate:self];
}
return _facebook;
}
-(void)viewDidLoad
{
[super viewDidLoad];
((MyAppDelegate *)([[UIApplication sharedApplication] delegate])).facebookViewController = self;
...
}
And of course you need to implement your FBSessionDelegate and FBRequestDelegate methods
In the AppDelegate :
#import <UIKit/UIKit.h>
#import "MyFacebookViewController.h"
#interface MyAppDelegate : UIResponder <UIApplicationDelegate>
...
#property (strong, nonatomic) MyFacebookConnectViewController *facebookViewController;
#end
And it's in the implementation file that you need to put the - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
#implementation MyAppDelegate
#synthesize facebookViewController = _facebookViewController;
...
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [self.facebookViewController.facebook handleOpenURL:url];
}
If you still have problems let me know!
I'm not editing my answer as this is code I've never tested.
If you want to use the same Facebook instance in different viewController this is one way to do it:
As already mentioned in your question, you should initialize it in the appDelegate (I would use a property.)
MyAppDelegate.h :
#import <UIKit/UIKit.h>
#import "FBConnect.h"
#interface MyAppDelegate : UIResponder <UIApplicationDelegate>
...
#property (strong, nonatomic) Facebook *facebook;
#end
MyAppDelegate.m :
#import "MyAppDelegate.h"
#implementation MyAppDelegate
#synthesize facebook = _facebook;
-(Facebook *)facebook
{
if(!_facebook){
_facebook = [Facebook alloc] initWithAppId:MY_APP_ID andDelegate:self];
}
return _facebook;
}
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [self.facebook handleOpenURL:url];
}
Then in any ViewController that you would want you could have a Facebook property that you would not alloc/init but you would ask it from the appDelegate this way
AnyViewController.h :
#import "MyAppDelegate.h"
#interface AnyViewController : UIViewController
...
#property (strong, nonatomic) Facebook *facebook;
#end
AnyViewController.m :
#import "AnyViewController.h"
#implementation AnyViewController
#synthesize facebook = _facebook;
-(Facebook *)facebook
{
if(!_facebook){
MyAppDelegate *theAppDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
_facebook = theAppDelegate.facebook;
}
return _facebook;
}
And of course the different ViewControllers you'll be using should implement the different SDK protocols depending on your needs
You should have a look at the sample project by Facebook that's called Hackbook
Related
I need to initialize an object of an object in my AppDelegate at startup. Whenever I try to access the ivar somewhere else all that is returned is null.
What I'm doing can be simplified as
//AppDelegate.h:
#interface AppDelegate : UIResponder <UIApplicationDelegate> {
NSString* someString;
}
#property (nonatomic, copy) NSString* someString;
and then in the AppDelegate.m
//AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
someString = #"Hello World!";
NSLog(#"%#",someString);
}
This is what I do when I access the NSString-object
//ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSLog(#"%#",delegate.someString);
}
The output text is
2012-09-30 22:26:45.125 Labor3[22524:f803] Hello World!
2012-09-30 22:26:45.127 Labor3[22524:f803] (null)
without any warnings or errors.
The problem I'm having is a bit more complicated but the essentials stay the same and I can't even get the above example to work. Any hint is appreciated.
Add
#synthesize someString;
After
#implementation AppDelegate
in your app-delegate .m file.
You should use _someString instead of someString or use Andrey's answer solution, in your code property someString and ivar someString haven't any connection
when intergrating FB to my app it comes up and varifies that its connected but when it comes to going back to the app it crashes,
This is the return process from the fb developer page and added it that it comes to my view controller page
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [[Ball_GameViewController facebook] handleOpenURL:url];
}
has an advisory on the line that starts
return
saying
warning: 'Ball_GameViewController' may not respond to '+facebook'
any idea am i directing it to the wrong areas.
Ball_GameViewController.h
#import <UIKit/UIKit.h>
#import "FBConnect.h"
#interface Ball_GameViewController : UIViewController <FBSessionDelegate, FBDialogDelegate>
{
Facebook *facebook;
}
#property (nonatomic, retain) Facebook *facebook;
#property (nonatomic, retain) IBOutlet UIWindow *window;
- (IBAction)facebookpost;
- (IBAction)start;
- (IBAction)reset;
- (void)countdown;
- (void)scorecount;
- (void)checkTime;
#end
In your Ball_GameViewController #interface you need to use:
#property (nonatomic, strong) Facebook * facebook;
In your Ball_GameViewController #implementation you need to #synthesize facebook;
Update
Try this:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
Ball_GameViewController * ballGame = [[Ball_GameViewController alloc] init];
return [[ballGame facebook] handleOpenURL:url];
}
I have a facebook variable in my ViewController that I want to refer to in my AppDelegate method. How do I do this?
In my AppDelegate I want to put this, since it uses UIApplication and all other refs are in this class:
// For iOS 4.2+ support
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [facebook handleOpenURL:url];
}
The facebook variable is in my ViewController .h file:
#import <UIKit/UIKit.h>
#import "FBConnect.h"
#interface ImportPicViewController : UIViewController <FBSessionDelegate, FBDialogDelegate>{
Facebook *facebook;
}
#property (nonatomic, retain) Facebook *facebook;
#end
and synthesized in the .m file:
#implementation ImportPicViewController
#synthesize facebook;
But it is alloc init in the ViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
facebook = [[Facebook alloc] initWithAppId:#"224207854355440" andDelegate:self];
Any idea?
I have a facebook variable in my ViewController that I want to refer
to in my AppDelegate method.
That doesn't sound like a good plan -- it'll mean that your app delegate is dependent on this view controller.
A better design might have the app delegate create the instance of Facebook and keep a reference to it in its own ivar. Then, when it creates the view controller, it can pass in a reference to the instance of Facebook. This way, both the app delegate and the view controller are using the same Facebook object, but they're not trying to access each other's instance variables.
Declare a public method in AppDelegate which accepts a pointer to your ViewController. Then store the received pointer in AppDelegate. Once you need the ViewController, just use the stored pointer.
An uglier way to use 'facebook' would be to place it in a global variable.
I have been reading numerous books on iPhone development and doing the examples but I notice the idea of MVC is not really being taught correctly (although the authors do say that Xcode "lends itself" to the MVC way of coding).
A quick example. I want to make a simple calculator app (as many who are starting out do).
I have a working version with all of my code inside the xxxxxViewController.m file. Actions and Outlets all working well. The trouble with this approach is if I want to have multiple views (normal calculator and scientific calculator) I would have copy and paste my code so I now have two versions. I am clearly trying to avoid this.
So, I have created my own class (based on NSObject) as my CalculatorEngine.
Trouble is when trying to allocate and initialise my CalculatorEngine I receive errors such as "Redefinition of CalculatorEngine with a different type" and "Type specifier missing, defaults to int".
I guess I am missing something obvious.
Can you point me in the direction of a sample of any kind where a separate class is being used as an "engine" rather than having the code inside the xxxxViewController?
At this point the code below does not actually do anything. I am just trying to get the CalculatorEngine object useable in CalculatorViewController.m. This is where I receive the error.
// CalculatorAppDelegate.h
// Calculator
#import <UIKit/UIKit.h>
#class CalculatorViewController, CalculatorEngine;
#interface CalculatorAppDelegate : NSObject <UIApplicationDelegate>
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet CalculatorViewController *viewController;
#end
// CalculatorAppDelegate.m
// Calculator
#import "CalculatorAppDelegate.h"
#import "CalculatorViewController.h"
#implementation CalculatorAppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
}
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
- (void)applicationWillTerminate:(UIApplication *)application
{
}
- (void)dealloc
{
}
#end
// CalculatorViewController.h
// Calculator
#import <UIKit/UIKit.h>
#interface CalculatorViewController : UIViewController
#end
// CalculatorViewController.m
// Calculator
#import "CalculatorViewController.h"
#import "CalculatorEngine.h"
#implementation CalculatorViewController
// This was wrong here. Now moved to viewDidLoad().
//CalculatorEngine *CalcEng;
//CalcEng = [[CalculatorEngine alloc] init];
// trouble here. CalcEng is unknow.
-(IBAction)buttonPressed:(id)sender {
[CalcEng setRegisterX:1];
}
- (void)viewDidLoad
{
CalculatorEngine *CalcEng;
CalcEng = [[CalculatorEngine alloc] init];
[super viewDidLoad]
}
- (void)didReceiveMemoryWarning
{
}
- (void)viewDidUnload
{
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
}
#end
// CalculatorEngine.h
// Calculator
#import <Foundation/Foundation.h>
#interface CalculatorEngine : NSObject
#end
// CalculatorEngine.m
// Calculator
#import "CalculatorEngine.h"
#implementation CalculatorEngine
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
#end
This code is in the wrong location:
CalculatorEngine *CalcEng;
CalcEng = [[CalculatorEngine alloc] init];
Put that into -(void)viewDidLoad.
UPDATE
You cannot call your method because your view controller is not keeping a reference to the CalcEngine (by the way, variables like this should be camel cased to keep in line with naming conventions, so it would be calcEngine). To keep a reference you need to add an iVar, or more appropriately, a property called CalcEngine. To do this, your CalculatorViewController header would look like this:
// CalculatorViewController.h
// Calculator
#import <UIKit/UIKit.h>
#interface CalculatorViewController : UIViewController {
CalculatorEngine *CalcEngine;
}
#property (retain) CalculatorEngine *CalcEngine;
#end
Your implementation would look like this:
// CalculatorViewController.m
// Calculator
#import "CalculatorViewController.h"
#import "CalculatorEngine.h"
#implementation CalculatorViewController
// This was wrong here. Now moved to viewDidLoad().
//CalculatorEngine *CalcEng;
//CalcEng = [[CalculatorEngine alloc] init];
// trouble here. CalcEng is unknow.
-(IBAction)buttonPressed:(id)sender {
[CalcEng setRegisterX:1];
}
- (void)viewDidLoad
{
CalcEng = [[CalculatorEngine alloc] init];
[super viewDidLoad]
}
- (void)didReceiveMemoryWarning
{
}
- (void)viewDidUnload
{
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
}
#end
Don't take this the wrong way, but you should spend some time reading Apple's Objective C guide. The problems you are having have nothing to do with MVC, but with objective-c.
Did you?
#class CalculatorEngine;
But not?
#import "CalculatorEngine.h"
I have started working more with delegate as suggested in another question I made. Now, I have made a UIViewController called ProfileViewController in which I would like to load the News Feed from Facebook. I also have subclass of NSObject called FBFeed. This subclass loads the Facebook News Feed and should pass it back to the view controller. All my requests to Facebook are sent through a singleton named FBRequestWrapper which (in this case) should pass the result to FBFeed.
I call getFBRequestWithGraphPath:andDelegate: in FBRequestWrapper which will call a method in the Facebook iOS SDK which takes a delegate as a parameter. If I put in self (which will be the FBRequestWrapper) it works just fine. If I put in _delegate (which is an instance of FBFeed) the application crashes with EXC_BAD_ACCESS.
I have a theory why it might crash. I have read that
#property (nonatomic, retain) id <FBFeedDelegate> delegate;
should be
#property (nonatomic, assign) id <FBFeedDelegate> delegate;
But when doing so I get the following error:
Existing ivar 'delegate' for unsafe_unretained property 'delegate'
must be __unsafe_unretained
Also, I don't know if that enough to make the application crash.
Here is my code.
ProfileViewController.h
#import <UIKit/UIKit.h>
#import "FeedTableView.h"
#import "FBFeed.h"
#interface ProfileViewController : UIViewController <FBFeedDelegate>
{
FeedTableView *tableView;
}
- (void)loadFeed;
#end
ProfileViewController.m
#implementation ProfileViewController
- (void)loadFeed
{
FBFeed *feed = [[FBFeed alloc] init];
[feed loadNewsFeedWithDelegate:self];
}
#pragma mark -
#pragma FBFeedDelegate
- (void)finishedLoadingFeed:(FBFeed *)_feed
{
NSLog(#"ProfileViewController: FBFeed Finished.");
}
- (void)failedToLoadFeed:(FBFeed *)_feed
{
NSLog(#"ProfileViewController: FBFeed Failed.");
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Load feed
[self loadFeed];
}
#end
FBFeed.h
#import <Foundation/Foundation.h>
#import "FBRequestWrapper.h"
#protocol FBFeedDelegate;
#interface FBFeed : NSObject <FBRequestDelegate>
{
id <FBFeedDelegate> delegate;
}
#property (nonatomic, retain) id <FBFeedDelegate> delegate;
- (void)loadNewsFeedWithDelegate:(id)_delegate;
#end
#protocol FBFeedDelegate <NSObject>
#required
- (void)finishedLoadingFeed:(FBFeed *)_feed;
- (void)failedToLoadFeed:(FBFeed *)_feed;
#end
FBFeed.m
#import "FBFeed.h"
#implementation FBFeed
#synthesize delegate;
- (void)loadNewsFeedWithDelegate:(id)_delegate
{
self.delegate = _delegate;
[[FBRequestWrapper defaultManager] getFBRequestWithGraphPath:#"me" andDelegate:self];
}
#pragma mark -
#pragma mark FBRequest Delegate
- (void)request:(FBRequest *)request didLoad:(id)result
{
NSLog(#"FBFeed: FBRequest Did Load.");
NSLog(#"%#", result);
}
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error
{
NSLog(#"FBFeed: FBRequest Failed.");
NSLog(#"%#", error);
}
#end
FBRequestWrapper.h
#import <Foundation/Foundation.h>
#import "FBConnect.h"
#interface FBRequestWrapper : NSObject <FBRequestDelegate, FBSessionDelegate>
{
Facebook *facebook;
BOOL isLoggedIn;
}
#property (nonatomic, assign) BOOL isLoggedIn;
+ (id)defaultManager;
- (void)setIsLoggedIn:(BOOL)_loggedIn;
- (void)FBSessionBegin:(id<FBSessionDelegate>)_delegate;
- (void)FBLogout;
- (void)getFBRequestWithGraphPath:(NSString *)_path andDelegate:(id)_delegate;
- (void)getFBRequestWithMethodName:(NSString *)_methodName andParams:(NSMutableDictionary *)_params andDelegate:(id)_delegate;
#end
FBRequestWrapper.m
#import "FBRequestWrapper.h"
static FBRequestWrapper *defaultWrapper = nil;
#implementation FBRequestWrapper
#synthesize isLoggedIn;
+ (id)defaultManager
{
if(!defaultWrapper)
{
defaultWrapper = [[FBRequestWrapper alloc] init];
}
return defaultWrapper;
}
- (void)getFBRequestWithGraphPath:(NSString *)_path andDelegate:(id)_delegate
{
if (_path != nil)
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
if (_delegate == nil)
{
_delegate = self;
}
[facebook requestWithGraphPath:_path andDelegate:_delegate];
}
}
#pragma mark -
#pragma mark FBRequestDelegate
- (void)request:(FBRequest *)request didLoad:(id)result
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
NSLog(#"%#", result);
}
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
NSLog(#"FBRequest Failed: %#", error);
}
#end
I guess you are using ARC and the new iOS5 SDK which is still under NDA. Instead of using retain, use the Keyword 'strong'. Using strong will have the same behaviour intended as using retain. Set up your property like this:
#property (nonatomic, strong) id <FBFeedDelegate> delegate;
I think there are two ways for you:
Don't use ARC and leave your code as it was
If you want to use ARC, be aware that delegates normally shouldn't be retained. But when using ARC, an ivar declaration like this:
id <FBFeedDelegate> delegate
defaults to
id <FBFeedDelegate> __strong delegate
So when you declare a property like this:
#property (readwrite, unsafe_unretained) id <FBFeedDelegate> delegate
The ivar should look like this:
id <FBFeedDelegate> __unsafe_unretained delegate
I had the same problem but then I was able to make it work correctly under iOS 5.1 on XCode 4.3.2
Use this code in your .h file
#protocol AnimationManagerCountdownTimerDelegate <NSObject>
#required
-(void)countdownCompleted;
#end
#interface AnimationManager : NSObject{
....
}
#property (nonatomic, strong) id <AnimationManagerCountdownTimerDelegate> countdownTimerDelegate;
#end
Then in the .m file you simply synthesize:
#synthesize countdownTimerDelegate;