I've been a reader of Stack Overflow for quite a while now and am looking forward to participating! On to the question...
I've been programming for about 10 years, but am brand new to iOS programming & Objective-C.
I am trying to build an application that requires Facebook login. When the application opens it needs to load a view with a logo and a Facebook login button. I can do that and make it work with no problem. After they log in with Facebook, my application will send an API call to my server and authenticate them with my system and send back a callback with their system permissions. If it comes back that they have the correct permissions to advance, they will get sent to a view with a tabbed interface. I also have the tabbed interface working.
I made a FirstViewController to handle the Facebook login button, and have view controllers for each tab.
In the MyAppDelegate.m file will exist something like this:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//init facebook & send initial view with login button
facebook = [[Facebook alloc] initWithAppId:#"MYAPPID"];
[self.window addSubview:FirstViewController.view];
[self.window makeKeyAndVisible];
//Code to check permissions from webserver
if (permissions && _isLoggedIn) {
// Add the tab bar controller's view to the window and display.
[self.window addSubview:tabBarController.view];
[self.window makeKeyAndVisible];
}
else //access denied view
return YES;
}
Now, I need to make the call to the Facebook login method in FirstViewController...
[facebook authorize:nil delegate:self];
1) How do I access it the Facebook instance in MyAppDelegate? (I need to be able to access from all other views also.) Do I create a delegate/protocol to access it from MyAppDelegate?
2) Am I doing this an incredibly idiotic way? If so, please help an amateur iOS programmer get on the right path.
3) How can I utilize the "delegate:" argument in the Facebook method well? I'm not sure I understand it very well.
Thanks,
Matt
I've never used the Facebook Objective-C API, but I can give you some insight into setting up global access to your Facebook instance. You can get to the app delegate like so:
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
As long as you have a public method for accessing the facebook ivar/property of MyAppDelegate, you can get to it like so:
Facebook *theFacebook = [appDelegate facebook];
This won't give you any warnings as long as you #import "MyAppDelegate.h" somewhere.
As for -authorize:delegate:, take a look at Facebook.h. The delegate object you pass to that method should implement the FBSessionDelegate protocol.
For more on delegation in general, check out Apple's documentation on the subject.
Edit
To be really explicit here, this is what you should put in your MyAppDelegate.h file:
// MyAppDelegate.h
#import <UIKit/UIKit.h>
#class Facebook;
#interface MyAppDelegate : NSObject {
UIWindow *window;
UITabBarController *tabBarController;
Facebook *facebook;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#property (nonatomic, retain) Facebook *facebook;
#end
And MyAppDelegate.m
// MyAppDelegate.m
#import "MyAppDelegate.h"
#import "Facebook.h"
#implementation MyAppDelegate
#synthesize window;
#synthesize tabBarController;
#synthesize facebook;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/*...your implementation here...*/
}
/*...more methods...*/
#end
Related
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'm trying to implement Facebook API in my app code.
Pratically i have a simple TabbarController with 2 ViewController.
My AppDelegate code:
#interface AppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
UIWindow *window;
UITabBarController *tabBarController;
Facebook *facebook;
}
#property (retain, nonatomic) IBOutlet UIWindow *window;
#property (retain, nonatomic) IBOutlet UITabBarController *tabBarController;
#property (retain, nonatomic) Facebook *facebook;
method code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self.window addSubview:self.tabBarController.view];
[self.window makeKeyAndVisible];
facebook = [[Facebook alloc] initWithAppId:#"12345678910" andDelegate:_tabBarController];
............
return YES;
}
FirstViewController.h
#import "Facebook.h"
#interface FirstViewController : UIViewController <FBRequestDelegate, FBDialogDelegate, FBSessionDelegate> {
}
FirstViewController.m
#import "Appdelegate.h"
-(IBAction)shareFacebook {
AppDelegate *appdelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (![appdelegate.facebook isSessionValid]) {
NSArray *permissions = [[NSArray alloc] initWithObjects:
#"publish_stream",
#"read_stream",
nil];
[appdelegate.facebook authorize:permissions];
[permissions release];
}
}
I received a warning for this code: "andDelegate:_tabBarController"
Sending "UITabBarController *" to parameter of incompatible type 'id'
i would say UITabBarController * is not an FBSessionDelegate ... add the protocol as <UIApplicationDelegate, UITabBarControllerDelegate,FBSessionDelegate > and then
facebook = [[Facebook alloc] initWithAppId:#"12345678910" andDelegate:self];
In my app I declare and initialize the facebook object in the AppDelegate just like you. However, I made the AppDelegate use these (just like the other answer said it) : FBRequestDelegate, FBDialogDelegate, FBSessionDelegate. After that I placed all the facebook login/logout/post..etc methods in app delegate class.
After that you can just pass the facebook object to your FirstViewController from the didFinishLaunchingWithOptions.
If you dont understand let me know I'll give you more details. I might not explain it very well.
PS: i might be wrong with this but from what i've seen around the web it is easier to declare methods at the beginning in the app delagete didfinishlaunchingwithoptions and then pass them to specific view controllers.
I´m completely new to core data programming. i just try to find out where the best place for implementing the core data code would be. i´ve done the apple tutorial Locations and it worked well. now i try to transfer that to my current project what is a bit more complex.
the Locations tutorial shows one RootViewController including a programmatically generated tableView. my project is based on a tabView template. it owns a MainWindow.xib including the TabBarController including three ViewController (MapView, ListView, SettingsView) where each view has it´s own navigationController and xib-file.
The first stumbling block was changing the code that it will run with a xib for the tableView instead of creating it programmatically. I´ve managed that but there is still one error. I can´t connect the managedObjectContext from the appDelegate to the listViewController. I´ve tried the examples and suggestions for that issue from this forum here. but it still doesn´t work.
after looking at the CoreDataBooks sample project i´ve seen that the core data code was implemented in the RootViewController as well. Seems that it would be the wrong way to implement it in the ListViewController. But i don´t have a RootViewController in my project. In the AppDelegate i directly pass the tabBarController as the rootViewController. therefore i don´t know how to reach the listViewController to set the context like it was done in the Locations sample.
As the MapView is the first view i can´t set the context in the appDelegate. And after struggling a long time with the managedObjectContext i wonder if it would be better to invent a RootViewController to be able to place additional code there. the model should be accessible by all three views and it seems that the RootViewController is the right place.
But how do i combine that with a tabBarController which includes three more viewControllers based on xib-files? Could somebody recommend me examples or tutrials including core data based on a tab bar app?
Please read the following article by Marcus Zarra: Passing around a NSManagedObjectContext on iOS. That should give you an idea how to solve your problem.
In general you should add a NSManagedObjectContext property to all of your ViewControllers and pass the context before adding them to the view stack via pushViewController:animated:. You should not take the context from your app delegate.
If you pass a single NSManagedObject to a ViewController, e.g. to present a kind of detail view, you can access the context from that object, as every NSManagedObject knows about the NSManagedObjectContext it is "living" in.
If you are a registered iOS developer, I'd also recommend the WWDC 2010 and 2011 videos. There are some sessions about mastering Core Data.
ok, now i have the correct solution. it took a while to understand but now it works with dependency injection from application delegate into the view controllers (listViewController).
my problem was that i didn´t know how to reference my view controllers as they are nested into dedicated navControllers and one tabBarController.
after reading a lot of postings here i understood i have to declare my view controllers in the appDelegate.h and synthesize them in appDelegate.m and after that connect them to the appropirate item in IB. that was done fast & easy after understanding :-)
there is no rootViewController needed.
MyAppDelegate.h:
#import <UIKit/UIKit.h>
#import "ListViewController.h"
#interface MyAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
UIWindow *window;
UITabBarController *tabBarController;
IBOutlet ListViewController *listViewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
#property (nonatomic, retain) IBOutlet ListViewController *listViewController;
#property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
#property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
#end
MyAppDelegate.m:
#import "MyAppDelegate.h"
#import "ListViewController.h"
#implementation MyAppDelegate
#synthesize window=_window;
#synthesize tabBarController=_tabBarController;
#synthesize managedObjectContext=__managedObjectContext;
#synthesize managedObjectModel=__managedObjectModel;
#synthesize persistentStoreCoordinator=__persistentStoreCoordinator;
#synthesize listViewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSManagedObjectContext *context = [self managedObjectContext];
if (!context) {
// Handle the error.
}
// Pass the managed object context to the view controller.
listViewController.managedObjectContext = context;
// Override point for customization after application launch.
// Add the tab bar controller's current view as a subview of the window
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque animated:NO];
return YES;
}
...
ListViewController.h
#import <CoreLocation/CoreLocation.h>
#interface ListViewController : UITableViewController <CLLocationManagerDelegate> {
UINavigationController *navController;
NSManagedObjectContext *managedObjectContext;
}
#property (nonatomic, retain) IBOutlet UINavigationController *navController;
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
-(NSManagedObjectContext *)managedObjectContext;
#end
ListViewController.m
#import "MyAppDelegate.h"
#import "ListViewController.h"
#implementation ListViewController
#synthesize navController;
#synthesize managedObjectContext;
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"managedObjectContext: %#",[self managedObjectContext]);
NSError *error = nil;
if (![managedObjectContext save:&error]) {
NSLog(#"error: %#",[self managedObjectContext]);
return;
}
...
I've coded an app like that some time ago. The way I've solved it is I made a singleton which had a persistentStoreCoordinator property like the one in Apple documentation to hold the access to the database (so I don't have to write it every time). Then in every tab bar view controller I've initiated its own NSManagedObjectContext.
NSPersistentStoreCoordinator *coordinator = [[Singleton sharedSingleton] persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator: coordinator];
}
That way every controller approaches the database with it's own context, if you understand what I mean.
Please note that if any of your view controllers has a detail view controller, take the standard approach of passing the managed object context to it like in sample code (Books, Locations, Recipes).
i just fixed the bug. i missed out some methods necessary in the appDelegate. it works now if i put following code into viewDidLoad of my ListViewController
if (managedObjectContext == nil) {
NSLog(#"managedObjectContext is nil");
managedObjectContext = [(IntraAppAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
is that OK concerning proper MVC pattern rules? in my case the ViewController takes the context from the appDelegate now.
trying to set the context in the appDelegate with something like that throws an error:
NSManagedObjectContext *context = [self managedObjectContext];
if (!context) {
// Handle the error.
}
// Pass the managed object context to the view controller.
self.tabBarController.listViewController.navController.managedObjectContext = context;
self.window.rootViewController = self.tabBarController;
how can i gather the reference of other viewControllers which are controlled by the tabBarController and are not the topView/superView after app start? the first view is the MapView. do i have to instantiate or declare the listViewController in appDelegate? how must it be coded that it referes to the listViewController controlled by the tabBarController?
This is probably a noob question but can't get my head around it.
How do i make a connection between 2 viewcontrollers or a view controller and my appdelegate?
what i usually do is add the following to my app delegate "h" file
#class RootViewController;
#interface TabBarWithSplitViewAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
RootViewController *rootViewController;
}
#property (nonatomic, retain) IBOutlet RootViewController *rootViewController;
#end
and then create a connection in the Interface Builder. from my root view controller to the app delegate and automatically tells me thats the rootViewController that i added above.
and if you do this on the app delegate "m" file:
#import "RootViewController.h"
NSLOG(#"Controller %#",rootViewController);
it gives you a bunch of numbers indicating that there is a connection
But as you know with xcode 4 this changed since you usually no longer have the main.xib where you can create the connection, you do almost all those connections programatically.
i`ve tried everything from using the same code without the "IBOutlet" to adding:
rootViewController = [[RootViewController]alloc] init;
but nothing seems to work.
can anybody help out?
Thanks in advance
You will basically want to create an ivar of your view controller in your app delegate.
ViewController *myVC;
#property (nonatomic, retain) IBOutlet ViewController *myVC;
then synthesize it in the implementation file.
Then when the view controller loads, call something along the lines of this:
- (void)viewDidLoad {
AppDelegateClass *appDelegate = (AppDelegateClass *)[[UIApplication sharedApplication] delegate];
appDelegate.myVC = self;
}
At this point, you now have a direct connection to your view controller from the app delegate. Similarly, you could do the opposite to call app delegate methods from the view controller. In that case, you'd set up a delegate in the view controller's header.
id delegate;
#property (nonatomic, assign) id delegate;
again synthesizing it in the implementation file.
Now when you are in viewDidLoad, you'd call something like this:
- (void)viewDidLoad {
self.delegate = (AppDelegateClass *)[[UIApplication sharedApplication] delegate];
}
That should give you what you need to get going, so I hope that helps
You can do this with interface builder in XCode 4. I have made a short video on how to do it:
http://www.youtube.com/watch?v=6VOQMBoyqbA
When my app first starts, it shows a main page to log in to Facebook. Then, it goes to the UITabBarController. The code that I have in my app delegate is the following:
//this is the .h
#interface NMeAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MainViewController *controller;
UITabBarController *tabBar;
}
#property (nonatomic, retain) IBOutlet UITabBarController *tabBar;
#property (nonatomic, retain) MainViewController *controller;
#property (nonatomic, retain) IBOutlet UIWindow *window;
#end
//this is the .m of the app delegate
#import "NMeAppDelegate.h"
#implementation NMeAppDelegate
#synthesize window;
#synthesize tabBar;
#synthesize controller;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
controller = [[MainViewController alloc] init];
[window addSubview:tabBar.view];
[window addSubview:controller.view];
[window makeKeyAndVisible];
return YES;
}
Inside of MainViewController, I actually have a UserInfo object, which has all of the information that I need for the UITabBarController. The problem is that after getting this info in the UITabViewController, how do I pass this UserInfo to the UITabBarController` or possible the ViewController that is inside the UITabBarController so they were able to access this UserInfo? How to do this?
I hope my question makes sense.
you need to have an instance of your UserInfo object available to the tab bar controller. probably pass it into a instance variable of type UserInfo in your UItabBarController/each view controller whenever you transition into the specified controller.
edit: you should really have this passed into the view Controller it needs to be in (since it doesn't appear you have a custom UITabBarController subclass), or you could use a shared UserInfo variable in your app delegate to keep up with the information.
But as the commenter said, the question is not very clear at all and i could be completely misunderstanding what you want to do.
Hope that helps.