I need my APP to be able to get data on the user Facebook friends.
I managed to get the id of the Facebook friends but I'm stuck on getting their data.
I found an Objective C code that supposed to get the data, the problem is that I am using swift but I don't know how to convert the Objective C code to swift.
This is the code:
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc]
initWithGraphPath:#"/{user-id}"
parameters:params
HTTPMethod:#"GET"];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection,
id result,
NSError *error) {
// Handle the result
}];
Any suggestions.
The Swift version would be
var request : FBSDKGraphRequest = FBSDKGraphRequest(
graphPath: "/{user-id}/taggable_friends",
parameters: params,
HTTPMethod: "GET"
)
request.startWithCompletionHandler { (
connection: FBSDKGraphRequestConnection!,
result: AnyObject!,
error:NSError!) -> Void in
// Handle the result
}
Related
I would like to use dialogflow service in the app written using obj-c. Have been using api.ai library for a while but could not seem to find a library for obj-c for dialogflow v2(beta1) apis. My agent is upgraded to v2 already, but the api.ai internally is using /v1/ endpoints and I need to use v2beta1 specific features like access to knowledge bases. (https://cloud.google.com/dialogflow/docs/reference/rpc/google.cloud.dialogflow.v2beta1#queryparameters - knowledge_base_names).
The dialogflow api is a standard REST API, so all I need to have is OAuth2.0 & REST client, but coding this sounds like re-inventing the wheel.
Please advice. Thank you
I don't think there's a library written specifically for Dialogflow v2; however, the library google-api-objectivec-client-for-rest is a generic library provided by Google, that simplifies the code to consume their Rest APIs.
This library is updated to be used with Dialogflow V2. In order to use it, you'll need to match the Rest API, with the "Queries" (API methods) and "Objects" (API types) in the library, which is not that difficult because the names are basically the same.
For example, the detectIntent method full name is:
projects.agent.sessions.detectIntent
In the library, it is the equivalent to the Query:
GTLRDialogflowQuery_ProjectsAgentSessionsDetectIntent
Here's an example of a detectIntent request:
// Create the service
GTLRDialogflowService *service = [[GTLRDialogflowService alloc] init];
// Create the request object (The JSON payload)
GTLRDialogflow_GoogleCloudDialogflowV2DetectIntentRequest *request =
[GTLRDialogflow_GoogleCloudDialogflowV2DetectIntentRequest object];
// Set the information in the request object
request.inputAudio = myInputAudio;
request.outputAudioConfig = myOutputAudioConfig;
request.queryInput = myQueryInput;
request.queryParams = myQueryParams;
// Create a query with session (Path parameter) and the request object
GTLRDialogflowQuery_ProjectsAgentSessionsDetectIntent *query =
[GTLRDialogflowQuery_ProjectsAgentSessionsDetectIntent queryWithObject:request
session:#"session"];
// Create a ticket with a callback to fetch the result
GTLRServiceTicket *ticket =
[service executeQuery:query
completionHandler:^(GTLRServiceTicket *callbackTicket,
GTLRDialogflow_GoogleCloudDialogflowV2DetectIntentResponse *detectIntentResponse,
NSError *callbackError) {
// This callback block is run when the fetch completes.
if (callbackError != nil) {
NSLog(#"Fetch failed: %#", callbackError);
} else {
// The response from the agent
NSLog(#"%#", detectIntentResponse.queryResult.fulfillmentText);
}
}];
You can find more information and samples, in the library wiki. Finally, the library also has a sample code using Google Cloud Storage which ilustrates its use with GCP services.
I think that without a specific library for Dialogflow V2, this might be the next thing to try before implementing it from scratch.
EDIT
Oops, I was missing the fact that the generated service for Dialogflow does not contain v2beta1.
In this case, it is needed an additional first step, which is to use the Dialogflow v2beta1 DiscoveryDocument and the ServiceGenerator, to create the service interface for v2beta1. Then you can continue working the same as I mentioned before.
Following #Tlaquetzal example, I ended up doing something like below
In pod file
pod 'GoogleAPIClientForREST'
pod 'JWT'
Using ServiceGenerator and Discovery Document as mentioned above, generated set of DialogFlow v2beta1 classes. Command line
./ServiceGenerator --outputDir . --verbose --gtlrFrameworkName GTLR --addServiceNameDir yes --guessFormattedNames https://dialogflow.googleapis.com/\$discovery/rest?version=v2beta1
And added them to the project.
#import "DialogflowV2Beta1/GTLRDialogflow.h"
Next step is to generate JWT Token. I have used this library JSON Web Token implementation in Objective-C. I want to connect using a service account.
NSInteger unixtime = [[NSNumber numberWithDouble: [[NSDate date] timeIntervalSince1970]] integerValue];
NSInteger expires = unixtime + 3600; //expire in one hour
NSString *iat = [NSString stringWithFormat:#"%ld", unixtime];
NSString *exp = [NSString stringWithFormat:#"%ld", expires];
NSDictionary *payload = #{
#"iss": #"<YOUR-SERVICE-ACCOUNT-EMAIL>",
#"sub": #"<YOUR-SERVICE-ACCOUNT-EMAIL>",
#"aud": #"https://dialogflow.googleapis.com/",
#"iat": iat,
#"exp": exp
};
NSDictionary *headers = #{
#"alg" : #"RS256",
#"typ" : #"JWT",
#"kid" : #"<KID FROM YOUR SERVICE ACCOUNT FILE>"
};
NSString *algorithmName = #"RS256";
NSData *privateKeySecretData = [[[NSDataAsset alloc] initWithName:#"<IOS-ASSET-NAME-JSON-SERVICE-ACCOUNT-FILE>"] data];
NSString *passphraseForPrivateKey = #"<PASSWORD-FOR-PRIVATE-KEY-IN-CERT-JSON>";
JWTBuilder *builder = [JWTBuilder encodePayload:payload].headers(headers).secretData(privateKeySecretData).privateKeyCertificatePassphrase(passphraseForPrivateKey).algorithmName(algorithmName);
NSString *token = builder.encode;
// check error
if (builder.jwtError == nil) {
JwtToken *jwtToken = [[JwtToken alloc] initWithToken:token expires:expires];
success(jwtToken);
}
else {
// error occurred.
MSLog(#"ERROR. jwtError = %#", builder.jwtError);
failure(builder.jwtError);
}
When token is generated, it can be used for an hour (or time you specify above).
To make a call to dialogflow you need to define your project path. To create a project path for the call, append to the code below your unique session identifier. Session is like a conversation for dialogflow, so different users should use different session ids
#define PROJECTPATH #"projects/<YOUR-PROJECT-NAME>/agent/sessions/"
Making dialogflow call
// Create the service
GTLRDialogflowService *service = [[GTLRDialogflowService alloc] init];
//authorise with token
service.additionalHTTPHeaders = #{
#"Authorization" : [NSString stringWithFormat:#"Bearer %#", self.getToken.token]
};
// Create the request object (The JSON payload)
GTLRDialogflow_GoogleCloudDialogflowV2beta1DetectIntentRequest *request = [GTLRDialogflow_GoogleCloudDialogflowV2beta1DetectIntentRequest object];
//create query
GTLRDialogflow_GoogleCloudDialogflowV2beta1QueryInput *queryInput = [GTLRDialogflow_GoogleCloudDialogflowV2beta1QueryInput object];
//text query
GTLRDialogflow_GoogleCloudDialogflowV2beta1TextInput *userText = [GTLRDialogflow_GoogleCloudDialogflowV2beta1TextInput object];
userText.text = question;
userText.languageCode = LANGUAGE;
queryInput.text = #"YOUR QUESTION TO dialogflow agent"; //userText;
// Set the information in the request object
//request.inputAudio = myInputAudio;
//request.outputAudioConfig = myOutputAudioConfig;
request.queryInput = queryInput;
//request.queryParams = myQueryParams;
//Create API project path with session
NSString *pathAndSession = [NSString stringWithFormat:#"%#%#", PROJECTPATH, [self getSession]];
// Create a query with session (Path parameter) and the request object
GTLRDialogflowQuery_ProjectsAgentSessionsDetectIntent *query = [GTLRDialogflowQuery_ProjectsAgentSessionsDetectIntent queryWithObject:request session:pathAndSession];
// Create a ticket with a callback to fetch the result
// GTLRServiceTicket *ticket =
[service executeQuery:query
completionHandler:^(GTLRServiceTicket *callbackTicket, GTLRDialogflow_GoogleCloudDialogflowV2beta1DetectIntentResponse *detectIntentResponse, NSError *callbackError) {
// This callback block is run when the fetch completes.
if (callbackError != nil) {
NSLog(#"error");
NSLog(#"Fetch failed: %#", callbackError);
//TODO: Register failure with analytics
failure( callbackError );
}
else {
// NSLog(#"Success");
// The response from the agent
// NSLog(#"%#", detectIntentResponse.queryResult.fulfillmentText);
NSString *response = detectIntentResponse.queryResult.fulfillmentText;
success( response );
}
}];
This is a basic implementation, but works and good for demo.
Good luck
I'm trying to configure an Open Graph post. I've followed the code examples on the FB developer site, and, using a test_user, a post is supposedly successfully generated. Here is my code:
- (void)createOGObjectForImage:(NSURL *)imageURL
{
NSMutableDictionary<FBGraphObject> *object =
[FBGraphObject openGraphObjectForPostWithType:#"ogminigame:mini_game"
title:#"Mini Game"
image:#"https://fbstatic-a.akamaihd.net/images/devsite/attachment_blank.png"
url:imageURL
description:#""];
[FBRequestConnection startForPostWithGraphPath:#"me"
graphObject:object
completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if(!error) {
NSLog(#"Result: %#", [result description]);
[self createOGActionWithResult:result];
} else {
NSLog(#"Error posting the Open Graph object to the Object API: %#", error);
[self sharePhotoWithShareDialog:self openGraphAction:nil];
}
}];
}
However, nothing is appearing on the test user wall. When I step through the code, I can see that when I create the OG object, the result has the following contents:
"FACEBOOK_NON_JSON_RESULT" = true;
More specifically, it looks like this:
So when I create my Action, when I try to retrieve the objectID using:
NSString *objectId = [result objectForKey:#"id"];
it obviously returns nil. Am I missing a stage with the result object? I've tried searching for similar problems but there doesn't seem to be much in the way of an explanation.
Well, it seems the code supplied on the Facebook developer site where you supply your custom stories is incorrect. Namely, it should look like:
- (void)createOGObjectForImage:(NSURL *)imageURL
{
NSMutableDictionary<FBOpenGraphObject> *object = [FBGraphObject openGraphObjectForPostWithType:#"ogminigame:mini_game"
title:#"Mini Game"
image:#"https://fbstatic-a.akamaihd.net/images/devsite/attachment_blank.png"
url:imageURL
description:#""];
[FBRequestConnection startForPostOpenGraphObject:object completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if(!error) {
NSLog(#"Result: %#", [result description]);
[self createOGActionWithResult:result];
} else {
NSLog(#"Error posting the Open Graph object to the Object API: %#", error);
[self sharePhotoWithShareDialog:self openGraphAction:nil];
}
}];
}
So you cast the dictionary to an FBOpenGraphObject, rather than an FBGraphObject and call startForPostOpenGraphObject instead of startForPostWithGraphPath.
It seems to me Facebook need to be a bit more consistent with their documentation.
I still have nothing showing on the test_account page, but at least the above doesn't seem to be the cause of the issue...
Edit wording..
I am using a 3rd party library called Drupal-IOS-SDk to connect my Drupal website with my under development IOS app. I use a method to index nodes on my website and I am just wondering if anyone has any knowledge about how to deal with the response from my website. To give a little context if I run a similar bit of code to my index method (a getNode method) which works fine and I am able to access my response perfectly as shown below:
//code for correctly working nodeGet method
NSMutableDictionary *nodeData = [NSMutableDictionary new];
[nodeData setValue:#"650" forKey:#"nid"];
[DIOSNode nodeGet:nodeData success:^(AFHTTPRequestOperation *operation, id responseObject) {
//print out responseObject
//NSLog(#"%#", responseObject);
//pull data from the responseObject
NSInteger testNumber = [responseObject objectForKey:#"changed"];
NSLog(#"%ld", (long)testNumber);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//we failed, uh-oh lets error log this.
NSLog(#"%#, %#", [error localizedDescription], [operation responseString]);
}];
This is the what gets printed by response object(I didnt include the whole thing but youll get the point):
//printed statement for correctly working nodeGet method
{
changed = 1390534644;
comment = 0;
created = 1390534644;
data = "b:0;";
"ds_switch" = "";
"field_additional_pictures" = (
);
"field_author" = {
und =
The above code gets node data and calling the "objectforkey" method on responseObject lets me access numbers or whatever else is stored in my responseObject. Where I have commented pull data from response object I get back the integer "1390534644" which correctly corresponds to the "changed" variable as you can see from the printed response above. The above section works fine. It is the next step where I get confused:
//code for index method that is in question
NSMutableDictionary *paramsDictionary = [NSMutableDictionary new];
[paramsDictionary setValue:#"books_e_books_other_books" forKey:#"type"];
[DIOSNode nodeIndexWithPage:#"0" fields:#"nid" parameters:paramsDictionary pageSize:#"20"
success:^(AFHTTPRequestOperation *operation, id responseObject) {
//print response object
NSLog(#"%#", responseObject);
NSLog(#"GREAT SUCCESS");
//HERE IS MY PROBLEM!!!
//what do I do with response object?
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//code
NSLog(#"Oh no failed when trying to get the index");
NSLog(#"%#, %#", [error localizedDescription], [operation responseString]);
}];
In this section I index all of the nodes and get fields from each instead of getting all of the information from only one node. I get a response which shows things are working correctly thus far because the response has the correct info. I am confused though because I am not sure what my response object is exactly. It is a collection of nodes each with a "nid" and "uri" as shown from the index method responseObject below. If I wanted to get the value "650" for example from my first "nid" in the below printed area how would I go about doing this? I dont think I can call "objectForKey" as I did in the first working example because each node has a "nid". If I told my app to look for a key named nid it doesnt know which one to look for. With no unique keys how can I access the number 650? I have printed my index method responseObject below so you can see what I am talking about.
//printed statement from index method, this is what i am confused about, there are no unique keys how do I access the value 650 or the first nid
{
nid = 650;
uri = "http://www.domain.com/domain_endpoint/node/650";
},
{
nid = 649;
uri = "http://www.domain.com/domain_endpoint/node/649";
},
{
nid = 647;
uri = "http://www.domain.com/domain_endpoint/node/647";
},
It's been a few months since you asked this question, so I'm not sure if you're still looking for an answer, but in case anyone in the future needs it.
The responseObject is an array of NSDictionary objects. You can access the responseObject items just like an array and do whatever you need to with them depending on what you need the data for.
For example:
NSArray *responseArray = responseObject;
for (NSDictionary *item in responseArray)
{
// Do something with your item here
// For example:
NSString *uriStr = [item valueForKey:#"uri"];
}
I have created an application in Xcode 4.5. It incorporates a Facebook login process, a query of my friends and their basic info and some newsfeed publishing processes successfully. However, I am having an issue in trying to query which of my friends have "liked" a certain Facebook object, in this case, a photo. Here is what I have done thus far:
Requested the following permissions in the - (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI method:user_about_me, read_friendlists, friends_likes, read_stream,
friends_likes, user_likes, friends_photos,user_photos.
I went on Facebook and found a photo that has been liked by some of my Facebook friends. I then investigated the id of the photo by going to https://graph.facebook.com/?ids=https://www.urlToFBPhoto
I used the following query code (as per the facebook developer page regarding such queries: url) SELECT user_id FROM like WHERE object_id="10152365284110475".
I was expecting that when I did an NSLog of the resulting data from this query, I would get uids of the few friends who I know like the photo. But instead, the query returned no results whatsoever.
For clarity, here is the full query code I used:
- (IBAction)getFriendLikes:(id)sender
NSString *objectID = #"10152365284110475"; //object id of a friend's FB photo
NSString *query = [NSString stringWithFormat:
#"{"
#"'friendlikes':'SELECT user_id FROM like WHERE object_id=%#'," //no data
//#"'friends':'SELECT uid2 FROM friend WHERE uid1 = me()'," //returns data
//#"'friendinfo':'SELECT uid, name, sex, pic_big, pic_square FROM user WHERE uid IN
//(SELECT uid2 FROM #friends)'," //returns data
#"}",objectID];
NSDictionary *queryParam = [NSDictionary dictionaryWithObjectsAndKeys:
query, #"q", nil];
[FBRequestConnection startWithGraphPath:#"/fql"
parameters:queryParam
HTTPMethod:#"GET"
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error) {
if (error) {
NSLog(#"Error: %#", [error localizedDescription]);
} else {
NSLog(#"Result: %#", result);
}
NSArray *friendInfo = (NSArray *) [[[result
objectForKey:#"data"]
objectAtIndex:1]
objectForKey:#"fql_result_set"];
[DataController dc].fbArray = nil;
[DataController dc].fbArray = friendInfo;
//post callback tasks
} ];
}
If anyone can offer any assistance I would greatly appreciate it!
I am trying to send a POST request to a server using AFNetworking, and everything seems to be working, i.e. the application is successfully pinging the server. However, the parameter values that it is sending are blank when it reaches the server even though after stepping through my code below using the debugger, the values appear to be being passed successfully. Any help on this would be greatly appreciated.
APIClient.m
#import "APIClient.h"
#import "AFJSONRequestOperation.h"
// Removed URL for privacy purposes.
static NSString * const kAPIBaseURLString = #"string goes here";
#implementation APIClient
+ (APIClient *)sharedClient {
static APIClient *_sharedClient;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [[APIClient alloc] initWithBaseURL:[NSURL URLWithString:kAPIBaseURLString]];
});
return _sharedClient;
}
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (self) {
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
// Accept HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
[self setDefaultHeader:#"Accept" value:#"application/json"];
}
return self;
}
#end
Login Method in LoginBrain.m
- (void)loginUsingEmail:(NSString *)email andPassword:(NSString *)password withBlock:(void (^)(NSDictionary *loginResults))block {
self.email = email;
self.password = password;
// Removed path for privacy purposes
[[APIClient sharedClient] postPath:#"insert path here" parameters:[NSDictionary dictionaryWithObjectsAndKeys:email, #"uname", password, #"pw", nil] success:^(AFHTTPRequestOperation *operation, id responseJSON) {
if (block) {
block(responseJSON);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
if (block) {
block(nil);
}
}];
// Store user data in app?
}
Login Called Method in LoginViewController.m
- (IBAction)loginPressed {
[self.loginProgressIndicator startAnimating];
NSString *email = self.emailTextField.text;
NSString *password = self.passwordTextField.text;
[self.brain loginUsingEmail:email andPassword:password withBlock:^(NSDictionary *loginResults) {
[self.loginProgressIndicator stopAnimating];
[self.delegate uloopLoginViewController:self didLoginUserWithEmail:email andPassword:password];
}];
}
UPDATE
I tried changing the parameterEncoding as recommended here, but it did not fix the problem.
SECOND UPDATE
Here is the PHP code from the server side that is accessing the POST data. This was written by a co-worker of mine, as I don't do anything on the server side and am very unfamiliar with how it works.
header('Content-type: application/json');
$username = $_POST['uname'];
$pw = $_POST['pw'];
The server code is pretty straight forward. He has some sort of log script that checks to see what the variable values are, and he says that the client is hitting the server, but the variable values are blank.
THIRD UPDATE
This is a dump of the HTTP request by generating a print_r of the $_REQUEST variable:
Array ( [sid] => FwAqvZrfckw )
And here is a dump of the $_POST variable. As you can see, it's completely blank:
Array ( )
FOURTH UPDATE
I used Wireshark to capture the packet before it's being sent to the server, and everything appears to be in order:
Accept: application/json
Content-Type: application/x-www-form-urlencoded; charset=utf-8
And the POST parameters were all there as well. We also created a test file on the server side and just did a test POST to make sure that the code there is working, and it is.
Thank you.
With the same problem, using AFFormURLParameterEncoding was what I needed.
So just to simplify all the thread, you have to use :
[[APIClient sharedClient] setParameterEncoding:AFFormURLParameterEncoding];
I don't see anything in particular that would cause a problem here but I'll start off by giving you the steps I used to solve a similar problem.
To start, checkout the tool, Charles, which is a Debugging Web Proxy that will intercept the response from the server and should give you a more clear idea of what's going wrong. There's a 30 day free trial and it really helped me pick out the little bugs. To use it, press the sequence button and filter the results via your server url. From there you can see the request and response sent and received from the server. If the following doesn't fix your problem, post the request and response that Charles spits out.
Fix wise, try adding [[APIClient sharedClient] setParameterEncoding:AFJSONParameterEncoding] right before you send the POST request. It looks like yall are using JSON as the server-side format.
So in loginUsingEmail:
self.email = email;
self.password = password;
[[APIClient sharedClient] setParameterEncoding:AFJSONParameterEncoding];
[[APIClient sharedClient] postPath:#"insert path here" parameters:[NSDictionary dictionaryWithObjectsAndKeys:email, #"uname", password, #"pw", nil] success:^(AFHTTPRequestOperation *operation, id responseJSON) {
if (block) {
block(responseJSON);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
if (block) {
block(nil);
}
}];
// Store user data in app?
}