Allow iOS app user to authenticate with Facebook only when NECESSARY - ios

I have an iPhone app that allows users to record videos and I'd like them to be able to share those videos on Facebook.
However, unless the user decides to share, I don't want him/her to be redirected to Facebook. I've tried the method provided in the Facebook iOS tutorial, and it requires the user to be redirected to Facebook and authenticate as soon as the app starts up.
That's unnecessary.
What I'd like to do is have a "Share" button that allows the user to authenticate and then automatically upload a video right afterward using the POST request.
Is this possible? Has anyone achieved a similar effect?
Thanks.

I'm not sure which tutorial you're looking at, but it probably has you authenticate at launch just as a demo.
In your app, you can authorize you app with something like this after the video is saved
// Share button action
- (IBAction)sharePressed {
// If user is already authenticated
if ([facebook isSessionValid]) {
[self shareLinkToFacebook];
} else {
// Authenticate with just email permissions
NSArray* permissions = [NSArray arrayWithObjects:
#"email", nil];
[facebook setSessionDelegate:self];
[facebook authorize:permissions];
}
}
- (void) shareLinkToFacebook {
// Create a simple post
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setValue:[video url] forKey:#"link"];
[params setValue:[video title] forKey:#"name"];
[params setValue:#"description" forKey:#"description"];
[facebook dialog:#"feed" andParams:params andDelegate:self];
}
// FBSessionDelegate
- (void) fbDidLogin {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:kFBAccessToken];
[defaults setObject:[facebook expirationDate] forKey:kFBExpirationDateKey];
[defaults synchronize];
[self shareLinkToFacebook];
}
You're going to have to authenticate with facebook to get an access key- there's no way around that.

You cannot do what you want. To publish something, you must have a valid access_token. the only way to achieve this is to follow the login procedure that you saw in the official example.

The answer to this question lies in creating a singleton that operates as both the FBSessionDelegate and the FBRequestDelegate, and allows you to pass one instance of Facebook around your program.
I called my singleton FacebookVideoUploader, and in my app delegate I called:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [[[FacebookVideoUploader sharedInstance] facebook] handleOpenURL:url];
That's it. This keeps the app delegate uncluttered and let's the singleton handle the delegate methods.
In my implementation file for my view controller that handles the video upload I put:
(IBAction)shareWithFacebook:(UIButton *)sender {
[[FacebookVideoUploader sharedInstance] postVideoToFacebook];
}
That's it. All of the delegate and session methods as well as postVideoToFacebook are handled in the FacebookVideoUploader singleton.
This allows my users to log into my app without being redirected to Facebook and lets them authenticate as soon as they decide they want to share a video.

Related

Facebook Integration and data fetching not working

I have used the following code to fetch data from the Facebook to my app.
I have did all the other stuff in app delegate and plist.
This sometimes works and fetched data and sometimes don't. Can anyone help me with this. I can't figure what the problem is!
- (IBAction)facebook:(id)sender
{
FBLoginView *loginView=[[FBLoginView alloc]init];
loginView.delegate=self;
loginView.readPermissions = #[#"first_name",
#"last_name",
#"location",
#"id",
#"access_token"];
NSArray* permissions = [NSArray arrayWithObjects: #"email", nil];
loginView.readPermissions=permissions;
[FBSession openActiveSessionWithAllowLoginUI:YES];
[FBRequestConnection
startForMeWithCompletionHandler:^(FBRequestConnection *connection,
id<FBGraphUser> user1,
NSError *error) {
if (!error) {
firstname=user1.first_name;
lastname=user1.last_name;
city=[user1.location objectForKey:#"name"];
email=user1[#"email"];
fbid=user1.id;
Loggedin=#"Y";
[[NSUserDefaults standardUserDefaults]setObject:Loggedin forKey:#"token"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
if (![fbid isEqualToString:#""])
{
[self performSegueWithIdentifier: #"Facebooksegue" sender: self];
}
NSLog(#"%#",firstname);
NSLog(#"%#",lastname);
NSLog(#"%#",city);
NSLog(#"%#",email);
NSLog(#"%#",fbid);
}];
}
My AppDelegate
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// attempt to extract a token from the url
return [FBAppCall handleOpenURL:url sourceApplication:sourceApplication];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[FBSession class];
return YES;
}
I had a similar situation and I had used almost the same code as you did.
My problem was with the test Facebook id which I had used. I hadn't had it verified so it didn't respond with the email. Try using another account and it might work. If not check whether your app is authorized with the Facebook account synchronized with your account in the settings.
Hope this helps.
Go to the blue project icon at the top left, select your target, go to build settings, find other linker flags, add -ObjC. This will make it so that the entire framework gets statically linked and thus you can reference FBLoginView in the storyboard successfully.

how to manage sqlite database lite version to paid version in iphone application?

I created iPhone application using sqlite db. In this sqlite db I have stories document, directory images url, and other parameters. When the user is using the "lite" version of the app, everything works fine. But when I upgrade app from "lite" version to a "paid" version, I'd like to be able to copy database and latest files in my Documents directory to the "paid" app. Assistance would be appreciated.
If the data base in Documents directory then while updating your app iOS keep the old database.
Because of app sandboxing, you can't do precisely what you want, but there are a number of approaches:
Instead of separate pro version, only have a single version of the app, which offers an In App Purchase that enables certain features. See Convert the "lite app" to "pro app" within the application. Thus, this eliminates the need to import files/settings from one app to another.
You could implement separate custom URL scheme for both free and pro versions of your app to allow them to exchange limited amount of data via a URL. Thus, the pro app would probably, on the first time it's run, check to see if the lite app is installed, and if so, request data from it:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
BOOL runAlready = [userDefaults boolForKey:kRunAlready];
if (!runAlready)
{
[userDefaults setBool:YES forKey:kRunAlready];
[userDefaults synchronize];
[self importDataFromLite];
}
}
- (void)importDataFromLite
{
NSURL *liteURL = [NSURL URLWithString:#"testapp-lite://getdata"];
// if we can open test app, then test app installed, so launch it, providing "getdata" request
if ([[UIApplication sharedApplication] canOpenURL:liteURL])
{
// Getting data from lite app
[[UIApplication sharedApplication] openURL:liteURL];
}
}
The lite version would obviously have to set up its Info.plist to register this custom URL scheme, and its app delegate would need to respond to this request for data, in turn calling a custom URL scheme of the pro app to send the data back:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
[[[UIAlertView alloc] initWithTitle:nil message:[url absoluteString] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
if ([[url absoluteString] isEqualToString:#"testapp-lite://getdata"])
{
// I'm going to create URL from local NSDictionary, but you would presumably retrieve data from your model/database
NSDictionary *dictionary = #{#"name" : #"Rob", #"age" : #"29"};
NSMutableArray *array = [NSMutableArray array];
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, NSString *obj, BOOL *stop) {
[array addObject:[NSString stringWithFormat:#"%#=%#", key, [obj stringByAddingPercentEscapesForURLParameterUsingEncoding:NSUTF8StringEncoding]]];
}];
// now create the URL
NSURL *proURL = [NSURL URLWithString:[NSString stringWithFormat:#"testapp-pro://data?%#", [array componentsJoinedByString:#"&"]]];
// if we can open pro app, then then do so, providing "getdata" request
if ([[UIApplication sharedApplication] canOpenURL:proURL])
{
NSLog(#"Opening pro app");
[[UIApplication sharedApplication] openURL:proURL];
}
}
return YES;
}
In turn, the pro version could have custom URL scheme to receive the data from the lite version of the app. Thus, the pro app's handler for the data from the lite app might look like:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
NSArray *absoluteStringComponents = [[url absoluteString] componentsSeparatedByString:#"?"];
NSArray *parameters = [absoluteStringComponents[1] componentsSeparatedByString:#"&"];
for (NSString *parameter in parameters)
{
NSArray *parameterComponents = [parameter componentsSeparatedByString:#"="];
// I'm just logging the results, but you'd presumably integrate the results into your model
NSLog(#"%# is equal to %#", parameterComponents[0], [parameterComponents[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]);
}
return YES;
}
This works with modest amounts of basic data, but I would not have thought it would be well suited for exchanging image files. If interested, I can show you example of this.
In a variation of the above, you could use the UIDocumentInteractionController to import larger amounts of data, though I think this would request you to present a popover in the lite app for the user to specify to open the data file in the pro app (which seems inelegant).
You could, theoretically, store data on the cloud, possibly iCloud, DropBox, or your own server.

Facebook API for IOS

I am including Facebook API for IOS in my app to share the link from my app in facebook. When I click "Share" from my app, It shares the link when I already logged in facebook in my device. If not it asks me to login. Once I logged in, the window closed and its not shared that link. i need to click "Share" again to share link. What should I need to share automatically after I logged in.
Pls Help me.
Did you use the Facebook SDK for iOS (https://developers.facebook.com/docs/mobile/ios/build/)? I scrupulously followed those instructions and it worked well for me on the first try. The only trick for me was the much discussed solution to make sure the app didn't launch Safari for authentication when people didn't have the Facebook app by replacing:
[self authorizeWithFBAppAuth:YES safariAuth:YES];
with
[self authorizeWithFBAppAuth:YES safariAuth:NO];
in the (void)authorize:(NSArray *)permissions method in Facebook.h. But other than that, I just followed the instructions on that above web site and it worked seamlessly for me.
If this doesn't answer your question, perhaps you can post some code so we can help diagnose what's going on. The question (in the absence of code) is a little ambiguous.
See my question if you have any doubt with facebook API integration.
Facebook API dialog page showing error in second time and after that
Try this code:
-(void)buttonPressed:(id)sender{
facebook = [[Facebook alloc] initWithAppId:#"YOUR_APP_ID" andDelegate:self];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"] && [defaults objectForKey:#"FBExpirationDateKey"]) {
facebook.accessToken = [defaults objectForKey:#"FBAccessTokenKey"];
facebook.expirationDate = [defaults objectForKey:#"FBExpirationDateKey"];
}
if (![facebook isSessionValid]) {
[facebook authorize:nil];
}else{
[self postWall];
}
// Pre 4.2 support
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [facebook handleOpenURL:url];
}
- (void)fbDidLogin {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
[self postWall];
}
-(void)postWall{
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"https://developers.facebook.com/docs/reference/dialogs/",#"link",
#"http://fbrell.com/f8.jpg",#"picture",
#"Facebook Dialogs",#"name",
#"Reference Documentation",#"caption",
#"Using Dialogs to interact with users.",#"description",
#"Facebook Dialogs are so easy!",#"message",
nil];
[[self facebook] dialog:#"feed" andParams:params andDelegate:self];
}
If your app is not re-launching your app after sign-in (from browser or if the user has installed the Facebook app) then you have not added your FB App Id to your Info.plist (which is how your app gets launched)
See the documentation here go to section Modify the app property list file:
... Create a new row named URL types with a single item, URL Schemes,
containing a single value, fbYOUR_APP_ID (the literal characters fb
followed by your app ID). The following shows exactly how the row
should appear in the .plist file...
Eg:
URL Types.[0].URL Schemes.[0].fb1234 (where your Facebook app id is 1234)

Facebook SDK iOS login with Facebook app installed

UPDATE: I've realized that the below problem only occurs if the user has already given Facebook access to my app, and then reinstals my app. The first time the user selects the Facebook button in my app, it goes to the Facebook app to get authentication, but since the authentication status is still saved in the Facebook app, it returns immediately to my app without explanation to the user, and the postToFacebookWall method fails.
UPDATE 2: I ended up implementing the solution found here: How to authorize user through a DIALOG with the NEW Facebook Connect iOS API? and avoided the Facebook app. Safari is better because at least the user gets a message saying that they will be logging in. However the Feed Dialogue still fails the first time as before.
I'm having what must be a common problem, but I haven't been able to find the solution yet. Note that my app is restricted to iOS 5 and above.
I have an app with a Facebook button for posting to a users wall. When the button is pressed, the app will check to see if the user has already given permission to the app to post to the wall. If they have, a view pops up with a Feed Dialog with the wall post information. If they haven't, Safari will be opened and the user will be allowed to login. After logging in they will be taken back to the app and the Feed Dialog view will pop up.
This works on the Simulator, and on my device, if the Facebook app is not installed. If the Facebook app is installed, the user is taken to the Facebook app, but then immediately returned to the original app, without allowing the user to do anything. The Feed Dialog pops up, but it is completely blank and dismisses automatically. Pressing the Facebook button again will bring up the Feed Dialog view as it is supposed to once the user has given permission to the app to post to their wall.
I pretty much followed the instructions found here:
http://developers.facebook.com/docs/mobile/ios/build/#implementsso
and here:
http://developers.facebook.com/docs/reference/dialogs/feed/
In the AppDelegate I have the following code:
- (void)loginToFacebook
{
// Set up facebook
self.facebook = [[Facebook alloc] initWithAppId:#"YOUR_APP_ID" andDelegate:self];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"]
&& [defaults objectForKey:#"FBExpirationDateKey"]) {
self.facebook.accessToken = [defaults objectForKey:#"FBAccessTokenKey"];
self.facebook.expirationDate = [defaults objectForKey:#"FBExpirationDateKey"];
}
if (![self.facebook isSessionValid]) {
[self.facebook authorize:nil];
}
}
// This is an FBSessionDelegate protocol method
// that gets called after a successful login
- (void)fbDidLogin
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[self.facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[self.facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName:#"POST_TO_FACEBOOK_WALL" object:nil];
}
// This is a UIApplicationDelegate protocol method
// It is called when safari is dismissed
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [self.facebook handleOpenURL:url];
}
The only difference between this and the sample code is that I added an NSNotification that is picked up by my view controller, where I have the following code:
- (void)facebookButtonPressed:(UIControl*)sender
{
Facebook *facebook = [(AppDelegate*)[[UIApplication sharedApplication] delegate] facebook];
if (![facebook isSessionValid])
{
// Login to facebook if not already logged in
[(AppDelegate*)[[UIApplication sharedApplication] delegate] loginToFacebook];
}
else
{
[self postToFacebookWall];
}
}
- (void)postToFacebookWall
{
Facebook *facebook = [(AppDelegate*)[[UIApplication sharedApplication] delegate] facebook];
/*Facebook Application ID*/
// TODO: Get this either from the AppDelegate facebook property or the info plist
NSString *client_id = #"YOUR_APP_ID";
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
kAppId, #"app_id",
#"http://developers.facebook.com/docs/reference/dialogs/", #"link",
#"http://fbrell.com/f8.jpg", #"picture",
#"Facebook Dialogs", #"name",
#"Reference Documentation", #"caption",
#"Using Dialogs to interact with users.", #"description",
nil];
[facebook dialog:#"feed" andParams:params andDelegate:self];
}
Does anyone know how to fix my issue? (Also, in case it isn't clear, YOUR_APP_ID is replaced with my app id in my code).
This sounds like an out-case in that it is this scenario of 'only when the app was installed, authed, then uninstalled, then reinstalled'.
You should be able to fall-back to "classic web view authentication" when this particular thing happens.
if (facebookAppDidNotReturnProperly) {
// therefore I must have been re-installed
// fallback to UIWebView
[facebook authorizeWithFBAppAuth:NO safariAuth:NO];
}
Then again, you could also use safari auth too I suppose.
I ended up solving my questions by implementing the two solutions found here:
https://stackoverflow.com/a/5683454/472344
https://stackoverflow.com/a/9137887/472344

ios5 - posting to facebook when facebook app is already on device

I'm following the information provided in facebook url. Its working well and good if facebook app is not there on the device.
consider facebook app is not on device: i'm authorizing facebook and its opening safari. i authorize the app, comes back to ios app and it calls the delegate "fbDidLogin" in FBSessionDelegate. I'm saving the access token and expiration date as mentioned in above URL. Then later, I'm able to post it facebook. This works fine.
now, facebook app is available on the device and logged in also: when i call "authorize", it opens facebook app and authorizes the app, then comes back to the ios app. but its not calling "fbDidLogin" delegate method. how should I recognize or catch the access token and expiration date?
I'm writing the following code:
facebook = [[Facebook alloc] initWithAppId:FB_APPID andDelegate:self];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"]
&& [defaults objectForKey:#"FBExpirationDateKey"])
{
facebook.accessToken = [defaults objectForKey:#"FBAccessTokenKey"];
facebook.expirationDate = [defaults objectForKey:#"FBExpirationDateKey"];
}
NSArray* permissions = [NSArray arrayWithObjects:#"publish_stream",nil];
if (![facebook isSessionValid])
{
[facebook authorize:permissions];
}
- (void)fbDidLogin {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults setBool:YES forKey:#"IIRY_FACEBOOK"];
[defaults synchronize];
}
Can some one point me the mistake that I'm doing?
Actually when you tap on authorize then it will open safari and if there is facebook app on device then it will open facebook app otherwise it will open facebook in safari. If you don't want to open the safari and facebook app then in facebook.m change this statement everywhere where it is calling to NO.
[self authorizeWithFBAppAuth:NO safariAuth:NO];
Found the solution. The problem is that the bundle ID mentioned in facebook app and my app's bundle Id are not same and so its not working.
Look the URL to know more

Resources