8

I've honestly spent hours on this trying to get it to work. Unfortunately Facebook & App Link's documentation is not clear enough. Even the App Links video from F8.

App Requirements:

  1. Share a link to FB as an Open Graph story which users can click on to take them directly into my app and do specific tasks (my app needs to receive specific parameters from the link)
  2. Share the link to FB without FB login (i.e. via the Share Dialog and switch to the native iOS FB app as opposed to using API calls).

Progress so far:

I'm using the following code to create the hosted App Link (as I only have mobile content) as per the FB developer's website under Publishing iOS SDK.

NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
                            @"{My app name}", @"name",
                            {custom URL}, @"al:iphone:url",
                            @"{app store ID}", @"al:iphone:app_store_id",
                            @"{My app name}", @"al:iphone:app_name",
                            @"{\"should_fallback\": false}", @"web",
                            fbAccessToken, @"access_token",
                            nil
                            ];

/* make the API call */
[FBRequestConnection startWithGraphPath:@"/{FB app id}/app_link_hosts"
                             parameters:params
                             HTTPMethod:@"POST"
                      completionHandler:^(
                                              FBRequestConnection *connection,
                                              id result,
                                              NSError *error
                                              ) {
                              /* handle the result */
                              NSLog(@"Result = %@",result);
                              if(error) NSLog(@"error = %@",error);
                          }];

Next I post the OG story to FB (this is posts fine but without a correct url)

// Create OG object
id<FBGraphObject> object =
[FBGraphObject openGraphObjectForPostWithType:@"{app name}:{FB object_name}"
                                        title:@"Test Link"
                                        image:@"https://cdn3.iconfinder.com/data/icons/picons-social/57/56-apple-512.png" // hosted wallpaper with unique id for background
                                          url:nil // Assuming I need to put the url to the app link host object here??

                                  description:@"Click to on this test link!"];

// Create an action
id<FBOpenGraphAction> action = (id<FBOpenGraphAction>)[FBGraphObject graphObject];

// Link the object to the action
[action setObject:object forKey:@"{FB object name}"];

// Check if the Facebook app is installed and we can present the share dialog
FBOpenGraphActionParams *params = [[FBOpenGraphActionParams alloc] init];
params.action = action;
params.actionType = @"{app name}:{FB action name}";

// If the Facebook app is installed and we can present the share dialog
if([FBDialogs canPresentShareDialogWithOpenGraphActionParams:params]) {
    // Show the share dialog
    [FBDialogs presentShareDialogWithOpenGraphAction:action
                                          actionType:@"{app name}:{FB action name}"
                                 previewPropertyName:@"{FB object name}"
                                             handler:^(FBAppCall *call, NSDictionary *results, NSError *error) {
                                                 if(error) {
                                                     // An error occurred, we need to handle the error
                                                     // See: https://developers.facebook.com/docs/ios/errors
                                                     NSLog(@"Error publishing story: %@", error.description);
                                                 } else {
                                                     // Success
                                                     NSLog(@"result %@", results);
                                                 }
                                             }]; 
}

To handle the incoming URL when someone clicks on the link in the FB OG story I've added the following code to AppDelegate.m as per FB documentation - see Handling incoming links

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {

    BOOL urlWasHandled =
    [FBAppCall handleOpenURL:url
           sourceApplication:sourceApplication
             fallbackHandler:
     ^(FBAppCall *call) {
         // Parse the incoming URL to look for a target_url parameter
         NSString *query = [url query];
         NSDictionary *params = [self parseURLParams:query];
         // Check if target URL exists
         NSString *appLinkDataString = [params valueForKey:@"al_applink_data"];
         if (appLinkDataString) {
             NSError *error = nil;
             NSDictionary *applinkData =
             [NSJSONSerialization JSONObjectWithData:[appLinkDataString dataUsingEncoding:NSUTF8StringEncoding]
                                             options:0
                                               error:&error];
             if (!error &&
                 [applinkData isKindOfClass:[NSDictionary class]] &&
                 applinkData[@"target_url"]) {
                 NSString *targetURLString = applinkData[@"target_url"];
                 // Show the incoming link in an alert
                 // Your code to direct the user to the
                 // appropriate flow within your app goes here
                 [[[UIAlertView alloc] initWithTitle:@"Received link:"
                                             message:targetURLString
                                            delegate:nil
                                   cancelButtonTitle:@"OK"
                                   otherButtonTitles:nil] show];
             }
         }
     }];
    return urlWasHandled;
}

// A function for parsing URL parameters
- (NSDictionary*)parseURLParams:(NSString *)query {
    NSArray *pairs = [query componentsSeparatedByString:@"&"];
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    for (NSString *pair in pairs) {
        NSArray *kv = [pair componentsSeparatedByString:@"="];
        NSString *val = [[kv objectAtIndex:1]
                         stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        [params setObject:val forKey:[kv objectAtIndex:0]];
    }
    return params;
}

Has anyone been able to get this working? I'm still not clear on how the hosted App Link works and where to put it (I'm assuming it should go in the 'url' parameter when calling the FBGraphObject openGraphObjectForPostWithType method.

I really don't want to create a website to store all the urls and add App Link meta tags (I'd have to do all this via the app as each App Link is going to be dynamic and unique for each user that generates it from with in the app).

Please help!

Tim
  • 1,428
  • 19
  • 26
  • 1
    can you include results from the requests made by any of the above code snippets? Couple of observations I have: 1.) we require the app link host calls to have an app access token (rather than a user one), so generally those requests shouldn't be made on a mobile client. 2.) For the OG objects, you should use the fb.me url that we give you once you create the app link host (see this doc on how to get the fb.me url - https://developers.facebook.com/docs/applinks/hosting-api ) – Ming Li Jun 16 '14 at 00:49
  • @MingLi Thanks for the direction! I've got the app access token working now. Will do some testing to see if I can get the custom URL passing info back to my app properly. Is there a limit on how many App Link Hosts the app can have and how long they're active for? – Tim Jun 16 '14 at 09:44
  • @MingLi My app posts to OG with the hosted App Link fine now. When I click on the link in iOS FB it switches to my app but NSString *query = [url query] (in AppDelegate) is returning null despite a massive url being returned?? This is based on code from the FB developers page. Any ideas what might be wrong? – Tim Jun 16 '14 at 11:04
  • hmmm, weird, can you PM me the url so I can take a look? – Ming Li Jun 16 '14 at 16:41
  • @MingLi Didn't know there was a way to PM you on here so I've sent it in a msg to you via FB (will be in your 'Other' folder). Hope that's ok. – Tim Jun 16 '14 at 21:39
  • did you guys ever solved it ?? I'm also at the point where i send out the custom App Link ("https://fb.me/2...) but i don't receive it at the other end. when pressing on the OG story, my app opens and i get a url in the form of : fb{my app id}://authorize/#access_token={token}&expires_in={number}. don't know if relevant buy I'm using the presentMessageDialogWithOpenGraphActionParams to send the OG story. – Boaz Saragossi Aug 30 '14 at 12:24
  • @MingLi I have used the approach suggested, but when I try to open the link on my Facebook app, it simply opens a web page within the fb app which shows "You have reached an implementation detail of ". Please suggested if I am doing something wrong. – Rajkumar Jan 15 '15 at 07:40
  • Please create another question with the app link hosting url that you created. – Ming Li Jan 15 '15 at 18:21

3 Answers3

1

With the help of MingLi from FB I managed to get it working with the following code:

- (void)shareToOpenGraphCountdownInvite
{
    NSURL *url = [NSURL URLWithString:@"https://graph.facebook.com/oauth/access_token?grant_type=client_credentials&client_id={insert your FB app ID here}&client_secret={insert client secret here}"];
    NSString *fullToken = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
    NSArray *components = [fullToken componentsSeparatedByString:@"="];
    FBAppAccessToken = [components objectAtIndex:1];

    NSDictionary *paramsForAppLinksHost = [NSDictionary dictionaryWithObjectsAndKeys:
                                           FBAppAccessToken, @"access_token",
                                           @"{your app name}", @"name",
                                           @"{your app's custom url}", @"al:ios:url",
                                           @"{app store ID}", @"al:ios:app_store_id",
                                           @"{your app name}", @"al:ios:app_name",
                                           @"{\"should_fallback\": false}", @"web",
                                           nil
                                           ];

    [FBRequestConnection startWithGraphPath:@"/{FB app ID}/app_link_hosts"
                                 parameters:paramsForAppLinksHost
                                 HTTPMethod:@"POST"
                          completionHandler:^(
                                              FBRequestConnection *connection,
                                              id result,
                                              NSError *error
                                              ) {
                              AppLinksHostURL_ID = [result objectForKey:@"id"]; // store this ID in an NSString
                              [self postOGStoryWithCustomURL];
                              if(error) NSLog(@"error = %@", error.description);

                          }];
}

- (void)postOGStoryWithCustomURL
{
    NSString *urlString = [NSString stringWithFormat:@"https://fb.me/%@/%@", AppLinksHostURL_ID, customURL];

    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[self pathForS3ObjectWithFilename:previewImageFilename]]]];

    // Create OG object
    id<FBGraphObject> object =
    [FBGraphObject openGraphObjectForPostWithType:@"timeflyz:countdown_invite"
                                            title:eventBeingShared.eventName
                                            image:image
                                              url:urlString // fb.me app links hosted url here
                                      description:@"{insert description here}"];

    // Create an action
    id<FBOpenGraphAction> action = (id<FBOpenGraphAction>)[FBGraphObject graphObject];

    // Link the object to the action
    [action setObject:object forKey:@"countdown_invite"];

    // Check if the Facebook app is installed and we can present the share dialog
    FBOpenGraphActionParams *params = [[FBOpenGraphActionParams alloc] init];
    params.action = action;
    params.actionType = @"timeflyz:create";

    // If the Facebook app is installed and we can present the share dialog
    if([FBDialogs canPresentShareDialogWithOpenGraphActionParams:params]) {
        // Show the share dialog
        [FBDialogs presentShareDialogWithOpenGraphAction:action
                                              actionType:@"timeflyz:create"
                                     previewPropertyName:@"countdown_invite"
                                                 handler:^(FBAppCall *call, NSDictionary *results, NSError *error) {
                                                     if(error) {
                                                         // An error occurred, we need to handle the error
                                                         // See: https://developers.facebook.com/docs/ios/errors
                                                         NSLog(@"Error publishing story: %@", error.description);
                                                     } else {
                                                         //                                                         NSLog(@"result %@", results);
                                                         if([[results objectForKey:@"completionGesture"] isEqualToString:@"post"]) {
                                                             NSLog(@"Posted successfully!");
                                                             [[NSNotificationCenter defaultCenter] postNotificationName:@"showShareSuccessfullMessage" object:self userInfo:nil];
                                                         } else
                                                             NSLog(@"Something else happened - user didn't post");
                                                     }
                                                 }];


    }

Note that "customURL" is an NSString that follows the pattern "?variable1=result1&variable2=result2..."

Tim
  • 1,428
  • 19
  • 26
  • You should not use/store the FB app secret in your client-side. You should create AppLinks URLs server-side and then query for the AppLinks URLs from the client-side. – Mat Dec 02 '14 at 12:05
  • This is the requirement for me too...sharing open graph story without login...however even with your approach I am getting back URL which has no custom params of mine. The URL received looks like fb://bridge/ogshare?version=20140410&cipher=. Any ideas? Do I need to sign up anywhere except getting my FB app right? – Nirav Bhatt Jan 06 '15 at 13:58
  • Not to mention share dialog works for me but the only issue is I want target_URL back for custom processing which is missing here. That's the issue for me. – Nirav Bhatt Jan 06 '15 at 13:59
  • @Tim can you please share your code with us, I m trying to implement facebook deep linking in ios 8. your help will save me. – Gamer Jun 26 '15 at 07:05
  • Hi, while sharing post on facebook I am getting error : Error publishing story: Error Domain=com.facebook.Facebook.platform Code=102 "The operation couldn’t be completed. (com.facebook.Facebook.platform error 102.)" UserInfo=0x155d4620 {error_code=102, error_description=An error occurred during publishing., app_id=568617263271525, error_reason=The operation couldn’t be completed. (FBAPIErrorDomain error 200.)} – Gamer Jul 23 '15 at 09:28
1

This is working for Graph v2.5 and SDK 4.8. Definitely not easy and not documented. But thanks Tim For inspiring. I'm using a custom graph object, I don't know how it works with default ones. I'm also using App Links Host since I don't have web app. I obtained a the {app_access_token} from Graph API Explorer, select your app then get app access token

First I created graph stories, actions and object types on Facebook developer (your facebook app). Make sure you have an app query scheme for your app in info.plist LSApplicationQueriesSchemes should contain a scheme pointing to your app I will refer to it as {app_scheme}.

Then in my iOS app I create a new link for every share.

- (void)createAppLinkHost:(void(^)(NSString* appLinkURL))success error:(void(^)(NSError* error))failure{

NSString *url = [NSString stringWithFormat:@"{app_scheme}://%li",self.object.identifier];
NSString *appAccessToken = {app_access_token};
NSString *iosLink = [NSString stringWithFormat:@"[{\"url\":\"%@\",\"app_name\":\"%@\",\"app_store_id\":%i},]",url,@"{app_name}",{app_store_id_int}];
NSDictionary *appLinkHostParams = @{@"access_token":appAccessToken,
                                    @"name":@"{link name}",
                                    @"web":@"{\"should_fallback\": false}",
                                    @"ios":iosLink
                                    };

FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc]initWithGraphPath:@"/{fb_appId}/app_link_hosts"
                                                              parameters:appLinkHostParams
                                                             tokenString:appAccessToken
                                                                 version:@"v2.5"
                                                              HTTPMethod:@"POST"];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
    if (!error) {
        NSString *appLinkHostId = [result objectForKey:@"id"];
        NSString *urlString = [NSString stringWithFormat:@"https://fb.me/%@", appLinkHostId];
        success(urlString);
    }
    else{
        NSLog(@"--ERROR-- [FACEBOOK APP LINK HOST] %@",error);
        failure(error);
    }
}];

}

If error, check it. Errors from App Link host make more sense than the rest of Facebook SDK. Graph API Explorer is useful to know what data you should send.

Once you get a success get that AppLinkHost and share it.

if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fbauth2://"]]){
[self createAppLinkHost:^(NSString *appLinkURL) {
    SURL *imageURL = [NSURL URLWithString:self.activity.imageURL];;
    FBSDKSharePhoto *photo = [FBSDKSharePhoto photoWithImageURL:imageURL userGenerated:NO];
    //Custom objects needs to be set from facebook first.
    //Set object properties
    NSDictionary *properties = @{
                                 @"og:type": @"{namespace}:{graph_object}",
                                 @"og:title": @"title",
                                 @"og:description": @"body",
                                 @"og:image": @[photo],
                                 @"og:url":appLinkURL,
                                 @"fb:app_id":@"{fb_appId}",
                                 @"{namespace}:{custom_property}":@(self.object.identifier), //optional
                                 };            
    //Create GraphObject
    FBSDKShareOpenGraphObject *object = [FBSDKShareOpenGraphObject objectWithProperties:properties];

    //Create Action
    FBSDKShareOpenGraphAction *action = [FBSDKShareOpenGraphAction actionWithType:@"{namespace}:{graph_action}" object:object key:@"{namespace}:{graph_object}"];

    FBSDKShareOpenGraphContent *openGraphcontent = [[FBSDKShareOpenGraphContent alloc] init];
    openGraphcontent.action = action;
    openGraphcontent.previewPropertyName = @"{namespace}:{graph_object}";

    [FBSDKShareDialog showFromViewController:self withContent:openGraphcontent delegate:self];
} error:^(NSError *error) {}];

}

Ayoub Khayati
  • 121
  • 1
  • 6
0

I don't have enough rep to comment, but check out this answer if you're still stuck:

Cannot get the new AppLinks to work on iOS or Android

Community
  • 1
  • 1
jday
  • 578
  • 6
  • 14