I've been trying this Twitter api stuff and it's really confusing...
I keep getting EXC_BAD_ACCESS bad access with the following code... What is the problem here?
NSURL *followingURL = [NSURL URLWithString:#"https://api.twitter.com/1/users/lookup.json"];
// Pass in the parameters (basically '.ids.json?screen_name=[screen_name]')
id fromIntToNum = [NSNumber numberWithInteger: friID];
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:#"159462573", #"user_id", nil];
// Setup the request
twitterRequest = [[TWRequest alloc] initWithURL:followingURL
parameters:parameters
requestMethod:TWRequestMethodGET];
// This is important! Set the account for the request so we can do an authenticated request. Without this you cannot get the followers for private accounts and Twitter may also return an error if you're doing too many requests
[twitterRequest setAccount:theAccount];
// Perform the request for Twitter friends
[twitterRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (error) {
/*
// deal with any errors - keep in mind, though you may receive a valid response that contains an error, so you may want to look at the response and ensure no 'error:' key is present in the dictionary
NSLog(#"%#",error);*/
} else {
/*NSError *jsonError = nil;
// Convert the response into a dictionary
NSDictionary *twitterGrabbedUserInfo = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONWritingPrettyPrinted error:&jsonError];
// Grab the Ids that Twitter returned and add them to the dictionary we created earlier
NSLog(#"%#", [twitterGrabbedUserInfo objectForKey:#"screen_name"]);*/
}
}];
I separated the line the my code fails on...
What could be causing this Twitter API problem?
The following line causes the crash:::
[twitterRequest performRequestWithHandler:^(NSData *responseData,
NSHTTPURLResponse *urlResponse, NSError *error) {
I will also sometimes get this error:
[__NSCFNumber credentialForAccount:]: unrecognized
UPDATE: I commented out the handler and I made TwitterRequest and ivar, but it still crashes...
In your block you look for an error, but if you get one you log it and continue on. You should put an "else" statement in and only proceed if no error.
Why not try to comment out all your code in the handler - don't do anything - and see if you get the crash. Then try with just the error code. Then try with the JSON serialization, and finally the last line. If you can find the part of the block that is causing the problem that would help.
Also, I suspect that performRequestWithHandler: does not block, but expects you to notify your class within the block that the request is done. If so it means "TWRequest *twitterRequest" should be an ivar or property, and you need to allows for some method to get called when the handler is done. Your crash may be due to ARC reallocating your object while the object is running.
EDIT:
Note that that the TWRequest class description says: "Use the initWithURL:parameters:requestMethod: method to initialize a newly created TWRequest object passing the required property values. " It says PLURAL properties, meaning more than 1. Could it be that it also expects a "credentialForAccount" property? You have to read the twitter docs to find all the required properties.
EDIT2:
Well, we don't even know if you get as far as your handler. Put a NSLog there but I suspect its never getting that far. If true this leaves three possibilities:
a) it does't like the URL (although this seems good)
b) you are missing some parameters it expects
c) id doesn't like "theAccount" object - is it a valid ACAccount object? Try NSLogging it.
It has to be one of these three things.
I had similar issues with iOS5 and TWRequest, and the problem turned out to be the ACAccountStore being released before the request handler block was called. The following code fixed this for me:
__weak ACAccountStore *wAccountStore = accountStore;
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
__strong ACAccountStore *sAccountStore = wAccountStore;
// handle the finished request here
// to silence Xcode warning 'unused variable'
// not necessary for releasing sAccountStore,
// it will go out of scope anyway when the block ends
sAccountStore = nil;
}];
This way, the accountStore gets retained until the block finishes.
Related
I am trying to create an application that implements Facebook Chat. I have set up all of the XMPP stuff correctly to the best of my knowledge, but I cannot get it to work.
After the user has logged in and been authenticated to Facebook (via FBSession) I try to connect to the chat service. Here is where the XMPP comes in:
-(void)connect
{
[self setupStream];
NSError *err;
[self.xmppStream connectWithTimeout:10.00 error:&err];
}
-(void)setupStream
{
_xmppStream = [[XMPPStream alloc] initWithFacebookAppId:FACEBOOK_APP_ID];
[self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}
- (void)xmppStreamDidConnect:(XMPPStream *)sender {
NSError *error;
NSError *err;
[self.xmppStream secureConnection:&err];
bool authed = [self.xmppStream authenticateWithFacebookAccessToken: FBSession.activeSession.accessTokenData.accessToken error:&error];
NSLog(#"%#", err);
NSLog(#"%#", [self.xmppStream authenticationDate]);
NSLog(#"%d, %#", authed, error);
}
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
NSLog(#"did authenticate");
[self goOnline];
}
When running the above, everything seems to go fine: xmppStreamDidConnect is called after a short wait and authed always returns YES and its error is always null.
However, secureConnection returns Error Domain=XMPPStreamErrorDomain Code=1 "Please wait until the stream is connected." UserInfo=0xb23dc30 {NSLocalizedDescription=Please wait until the stream is connected.} The authenticationDate is always null as well. Also, none of the other delegate methods are ever called, including xmppStreamDidAuthenticate. What am I doing wrong?
I have finally found my answer!! Here it is, in case anyone else runs in to the same problem as me:
When calling openActiveSessionWithReadPermissions:allowLoginUI:completionHandler: the FBSession object does not actually communicate with the Facebook servers or attempt to authenticate with them, it simply loads the previous authenticationToken. In my case, this token had become invalid, but I did not realize it and nothing was there to tell me. I finally figured it out by logging the token and putting it in Facebook's Access Token Debugger. To check if your token is valid, you must call [FBSession renewSystemCredentials:] and await the result. Then you can determine if you need to manually closeAndClearTokenInformation before attempting to create a new token.
I'm trying to call a RESTful web service with RESTKit 0.2 that returns only a string, but the RKResponseDescriptor class forces me to use a mapping with its method responseDescriptorWithMapping in order to be able to get the string value, I created the mapping and got the string value without any problems, but how can i get this string value without having to create the object mapping (i.e. creating an NSObject subclass, creating a property for the string to be received, and creating a mapping dictionary between the returned JSON key and this property) ?
You can still use RestKit to make the request. Sometimes it makes sense to do so in case server is returning an error for example, in which case it is necessary to access the raw data returned from server.
[[RKObjectManager sharedManager] getObjectsAtPath:kLoginURL
parameters:params
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSError *error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:operation.HTTPRequestOperation.responseData options:NSJSONReadingMutableLeaves error:&error];
NSLog(#"msg: %#", [json objectForKey:#"msg"]);
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
In this case, don't use RestKit to make the request. RestKit uses AFNetworking for the underlying network communications so if you import the classes you can use it too. Try directly using AFHTTPRequestOperation to make the request.
I am using restkit for sending request, response mapping and all. but sometimes, i am sending same request multiple time which comes from different screens. So it get added to request queue. But i don't want to add request into request queue if the request is already there. How can i check that.
I am calling function like this
//Here I need to check if the following url is in RKRequestQueue. If it not there then call the below method,
[self getDataFromServer];
- (void)getDataFromServer{
RKObjectManager *manager = [[RestKit sharedDataManager] objectManager];
[manager loadObjectsAtResourcePath:#"/getData" usingBlock:^(RKObjectLoader *loader) {
[RKClient sharedClient].requestQueue.showsNetworkActivityIndicatorWhenBusy = YES;
loader.method = RKRequestMethodPOST;
loader.params = inputData;
loader.onDidFailWithError = ^(NSError *error) {
};
loader.onDidLoadObjects = ^(NSArray *objects) {
};
}
Any help is appreciated.
Interesting question, I was pretty sure that you can actually check the URLs of your requests in the RKRequestQueue, but I wasn't able to find anything useful for that in the RKRequestQueue reference. Instead, I've found the containsRequest: method, but this will only compare RKRequest objects, not the actual URLs.
I guess the simplest thing to do is to create some kind of proxy for managing your network activity, implement the requestQueue:didSendRequest: delegate method and monitor which URLs are currently processed.
As i see in all RestKit documentations, didWSRequestLoadObjects delegate function is used to handle service response.
The problem is, if I have a different requests (postObject) in my view controller i have to check response type in didWSRequestLoadObjects for each request.
Is there a way to register a function before each postObject and get each response in different function?
Which version of RestKit are you using?
On the last release it is highly encouraged to use blocks instead of a loadObjects delegate function. For example, the RKObjectManager postObject method has a success and error parameters which receives a block.
Here is an example of use:
RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://some.url"];
//Configure here your manager with response descriptors and stuff..
[manager postObject:someObject path:#"/some/path" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
//Success Response code here
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
//Error Response code here
}];
Let me make this clear. I am NOT using the Facebook SDK. I'm using iOS SDK's Social.framework, and ACAccountStore to access Facebook accounts, and post with it/them.
I use the same code to post on Twitter. It works 100%. But for some reason regardless of what I do for Facebook integration, I get a "400" error when I try to post.
My method is:
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *facebookAccountType = [account accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
// Specify App ID and permissions
NSDictionary *options = #{ ACFacebookAppIdKey: #"MY_APP_ID",ACFacebookPermissionsKey: #[#"publish_stream", #"publish_actions"],ACFacebookAudienceKey: ACFacebookAudienceFriends };
[account requestAccessToAccountsWithType:facebookAccountType options:options
completion:^(BOOL granted, NSError *error)
{
if (granted == YES)
{
NSDictionary *parameters = #{#"message": string999};
NSURL *feedURL = [NSURL URLWithString:#"https://graph.facebook.com/me/feed"];
SLRequest *feedRequest = [SLRequest
requestForServiceType:SLServiceTypeFacebook
requestMethod:SLRequestMethodPOST
URL:feedURL
parameters:parameters];
acct.accountType = facebookAccountType;
// Post the request
[feedRequest setAccount:acct];
// Block handler to manage the response
[feedRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if (granted && error == nil) {
} else {
NSLog(#"Facebook response, HTTP response: %i %#", [urlResponse statusCode], [error description]);
[self closeShareMenu];
}
}];
}
}
I don't know where I'm going wrong! It's so annoying! I've set up my app correctly in Facebook Developers and all! Please help -_-'
Following up to the chat session held between #fguchelaar and yours truly yesterday; I was able to ascertain the following solution for this issue.
Add the following in your iOS completion handler:
NSString *temp = [[NSString alloc] initWithData:data encoding: NSUTF8StringEncoding];
NSLog(temp);
//'data' is your 'responseData' (or another object name) that you declare in your completion handler.
This will allow you to see the exact cause of the issue printed to the Debug Console. Now depending on the issue presented, you'll need to grab a Facebook account from the Array of Accounts generated when you call this handler in the iPhone SDK. Not at any prior stage whatsoever, as the Access Token will likely expire and give you this '400' error.
In my case; the error printed was: error:{'400' A valid access token is required… which vastly annoyed me as my prior method to access and check the current Twitter account was working perfectly. And my theory was that it should work just as well for Facebook. Why should the access token be instantaneously revoked if I'm grabbing the account a split second before?
The way I solved my issue (depending on the reason for your error the answer can vary) was to use a for loop to check the newly created array of accounts, with the sole purpose of finding the account there with the same identifier string as the one I saved into NSData/NSKeyedArchiver.
for(ACAccount *a in arrayOfAccounts) {
if([a.identifier isEqualToString:storedAccount.identifier]) {
//set the account to be used
accountToBeUsed = a;
//don't forget to break the For loop once you have your result.
break;
} else {
//This else{} block is not strictly necessary, but here you could set an account if no account was found with a matching identifier.
}
}
For it to work, it's recommended to declare an ACAccount object in your View Controller's .h file, add a #property and #synthesize it, so it can be assigned within the for loop and used after the break; statement.
This effectively solved my whole issue with the '400' error. It was inexplicably frustrating for about six hours of my day, so I hope that my explanation helps anybody who happens to stumble across this issue, and my question here on Stack Overflow :)
Regards,
cocotutch