I'm trying to modify Apple's sample code for using the Twitter API so that I can use the streaming API to filter tweets. I am attempting to implement the method suggested in response to this question:
Does TWRequest work for the twitter streaming api?
I have created the signed NSURLRequest and NSURLConnection objects as suggested, and set the delegate for the connection object. A valid twitter account is selected and used to sign the url. The problem is that the delegates connection:didReceiveData: method is never being called.
Here's my code:
#implementation Twitter
-(id)init{
if (self=[super init]) {
NSLog(#"Twitter init");
// Tell the notification centre to inform the app if the twitter account changes
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(twitterAccountChanged)
name:ACAccountStoreDidChangeNotification object:nil];
// Create an account store object.
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
// Create an account type that ensures Twitter accounts are retrieved.
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request access from the user to use their Twitter accounts.
[accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
if(granted) {
NSLog(#"Twitter: Access to twitter accounts granted");
// Get the list of Twitter accounts.
NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];
// Pick the twitter account to use
if ([accountsArray count] > 0) {
// Grab the initial Twitter account to tweet from.
ACAccount *twitterAccount = [accountsArray objectAtIndex:0];
// This is for a status filter
NSURL *url=[NSURL URLWithString:#"https://stream.twitter.com/1/statuses/filter.json"];
// Create the parameters dictionary
NSDictionary *dictionary=[NSDictionary dictionaryWithObjectsAndKeys:#"twitter", #"track", nil];
// Create TWRequest object
TWRequest *req=[[TWRequest alloc] initWithURL:url parameters:dictionary requestMethod:TWRequestMethodPOST];
// Set the account
[req setAccount:twitterAccount];
// Get a signed URL request
NSURLRequest *signedRequest=[req signedURLRequest];
// Initate the connection
[NSURLConnection connectionWithRequest:signedRequest delegate:self];
} else {
NSLog(#"Twitter: No twitter accounts to access");
}
} else {
NSLog(#"Twitter: Access to twitter accounts denied");
}
}];
return self;
}
return nil;
}
-(void)twitterAccountChanged{
NSLog(#"Twitter twitterAccountChanged");
}
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
NSLog(#"data received");
}
The output from the program is:
2012-07-24 09:50:03.668 TwitterAPITest[36722:10403] Twitter init
2012-07-24 09:50:03.836 TwitterAPITest[36722:11d03] Twitter: Access to twitter accounts granted
As you can see, everything appears to work OK, the only problem is that the delegate method is never called and I currently have no idea why.
Any help would be much appreciated...
Mike
It took me a bit of time to get this up and running, So I thought I aught to post my code for others. In my case I was trying to get tweets close to a certain location, so you will see that I used a locations parameter and a location struct I had in scope. You can add whatever params you want to the params dictionary.
Also note that this is bare bones, and you will want to do things such as notify the user that an account was not found and allow the user to select the twitter account they would like to use if multiple accounts exist.
Happy Streaming!
//First, we need to obtain the account instance for the user's Twitter account
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *twitterAccountType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request permission from the user to access the available Twitter accounts
[store requestAccessToAccountsWithType:twitterAccountType
withCompletionHandler:^(BOOL granted, NSError *error) {
if (!granted) {
// The user rejected your request
NSLog(#"User rejected access to the account.");
}
else {
// Grab the available accounts
NSArray *twitterAccounts = [store accountsWithAccountType:twitterAccountType];
if ([twitterAccounts count] > 0) {
// Use the first account for simplicity
ACAccount *account = [twitterAccounts objectAtIndex:0];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:#"1" forKey:#"include_entities"];
[params setObject:location forKey:#"locations"];
[params setObject:#"true" forKey:#"stall_warnings"];
//set any other criteria to track
//params setObject:#"words, to, track" forKey#"track"];
// The endpoint that we wish to call
NSURL *url = [NSURL URLWithString:#"https://stream.twitter.com/1.1/statuses/filter.json"];
// Build the request with our parameter
TWRequest *request = [[TWRequest alloc] initWithURL:url
parameters:params
requestMethod:TWRequestMethodPOST];
// Attach the account object to this request
[request setAccount:account];
NSURLRequest *signedReq = request.signedURLRequest;
// make the connection, ensuring that it is made on the main runloop
self.twitterConnection = [[NSURLConnection alloc] initWithRequest:signedReq delegate:self startImmediately: NO];
[self.twitterConnection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[self.twitterConnection start];
} // if ([twitterAccounts count] > 0)
} // if (granted)
}];
Related
I want to save the facebook account to ACAccountStore in iOS,
I will get accessTocken from facebookSDK but how to obtain tocken and secret
How to save it to ACAccountStore
Thanks in Advance.
------ Edit--------
I have an application that uses facebook to login and after with login information need to share photos that he selected.
My use cases are,
If account already in settings app then I can use it for login and share.
User using safari or webview for login then how I can proceed for sharing.
a. facebook SDK says If the user need to use OS integrated share controller then, login method used to authenticate the user must be native iOS 6.0 authentication..
b. If I need to share option that provided by facebook SDK uses facebook App I don't want this option because I want to avoid App switching as much as possible.
How to solve this..
to do it you should have an accesstoken and secret,
if (self.accountStore == nil) {
self.accountStore = [[ACAccountStore alloc] init];
}
//make credentials to assign to ACAccount
ACAccountCredential *credential = [[ACAccountCredential alloc] initWithOAuthToken:accesstoken tokenSecret:secret];
ACAccountType *acctType =[self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
ACAccount *newAccount = [[ACAccount alloc] initWithAccountType:acctType];
newAccount.credential = credential;
NSDictionary *options = [[NSDictionary alloc] initWithObjectsAndKeys:
"xXXXXXX", (NSString *)ACFacebookAppIdKey,
[NSArray arrayWithObject:#"basic_info"], (NSString *)ACFacebookPermissionsKey,
ACFacebookAudienceEveryone, (NSString *)ACFacebookAudienceKey,
nil];
[_accountStore requestAccessToAccountsWithType:acctType options:options completion:^(BOOL granted, NSError *error) {
if (granted == YES) {
[self.accountStore saveAccount:newAccount withCompletionHandler:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"the account was saved!");
}
else {
if ([error code] == ACErrorPermissionDenied) {
NSLog(#"Got a ACErrorPermissionDenied, the account was not saved!");
}
NSLog(#"%#",error);
}
}];
}
else {
NSLog(#"%# ",error);
}
}];
I am trying to use the built in iOS 5 Twitter framework and the TwitVid SDK to upload a video to twitter.
Steps I am taking:
1) Use TWRequest to get a signed request.
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
[accountStore requestAccessToAccountsWithType:[accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter] withCompletionHandler:^(BOOL granted, NSError *error) {
if(error) {
[[[UIAlertView alloc] initWithTitle:#"Sorry!" message:[error description] delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles: nil] show];
}
else if (granted) {
NSArray *arrayOfAccounts = [accountStore accountsWithAccountType:[accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter]];
if ([arrayOfAccounts count] > 0) {
ACAccount *account = [arrayOfAccounts objectAtIndex:0];
NSURL *spURL = [NSURL URLWithString:#"https://api.twitter.com/1/account/verify_credentials.json"];
TWRequest *twRequest = [[TWRequest alloc] initWithURL:spURL parameters:nil requestMethod:TWRequestMethodGET];
[twRequest setAccount:account];
NSURLRequest *signedURLRequest = [twRequest signedURLRequest];
2) Create an instance of TwitVid and fire off an upload request
TwitVid *twitVid = [[TwitVid alloc] initWithSource:#"Test" delegate:self];
[twitVid uploadWithMediaFileAtPath:moviePath offset:0 message:#"Test Message" mediaID:nil playlistID:nil vidResponseParent:nil userTags:nil geoLatitude:nil geoLongitude:nil tags:nil categoryID:nil description:#"upload test" title:#"The title" xVerifyCredentialsAuthorization:[signedURLRequest valueForHTTPHeaderField:#"Authorization"] xAuthServiceProvider:[spURL absoluteString]];
The first time I did this, I got some error back from the appropriate delegate method, and assumed it was a signing issue (I had some bugs in my code there). Now, no matter what I do, I get NOTHING in any of my delegate methods. Its not sending data, no response is received - nothing. I am at a loss of what to do. Any ideas?
i have used tweet sheet to tweet in my app,which works fine but can’t log out after setting ..i have to re launch the app to tweet
but what i want is to make a separate login button but in twitter is it possible to login and log out from twitter inside app?
Thanks loads in advance :)
I don't think that this is possible. You have to logout in your phones Settings.
You have to provide your own UI for Twitter login. E.g. once user entered twitter credentials you can first add Twitter account and then call Tweet sheet.
I doubt if log out or delete account is possible using SDK.
Sample code to add twitter account:
ACAccountStore *store = [[ACAccountStore alloc] init] ;
ACAccountType *twitterType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[store requestAccessToAccountsWithType:twitterType withCompletionHandler:^(BOOL granted, NSError *error) {
if(granted) {
ACAccount *twitterAccount = [[ACAccount alloc] initWithAccountType:[store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter]];
ACAccountCredential *outhCredential = [[ACAccountCredential alloc] initWithOAuthToken:token.key tokenSecret:token.secret];
twitterAccount.credential = outhCredential;
[store saveAccount:twitterAccount withCompletionHandler:^(BOOL success, NSError *error) {
if(success)
{
[self performSelectorOnMainThread:#selector(showTweetSheet) withObject:nil waitUntilDone:NO];
}
}];
[outhCredential release];
[twitterAccount release];
[store release];
}
// Handle any error state here as you wish
}];
Once a user has tweeted something, I need to differentiate which twitter account he used. Lets consider that the user would have 1 or several accounts configured on the phone.
I need tho know this after the tweet is successful, so the proper place would be the callback. I tried to fetch the accounts using ACAccountStore but it provides an array with all the accounts set up on the phone, not a clue about the last account used (not even the order of the array).
Does anyone knows if TWTweetComposeViewController remembers this account and, how to fetch it?
Thanks
My code:
if ([TWTweetComposeViewController canSendTweet])
{
TWTweetComposeViewController *tweetSheet =
[[TWTweetComposeViewController alloc] init];
[tweetSheet setInitialText:#"initial text"];
[tweetSheet addImage:[UIImage imageNamed:image]];
// Callback
tweetSheet.completionHandler = ^(TWTweetComposeViewControllerResult result) {
// if tweet was successful
if(result == TWTweetComposeViewControllerResultDone) {
// Get the accounts
account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[account requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error)
{
// if access granted I populate the array
if (granted == YES) {
NSArray *arrayOfAccounts = [account accountsWithAccountType:accountType];
ACAccount *account1 = [arrayOfAccounts objectAtIndex:0];
ACAccount *account2 = [arrayOfAccounts objectAtIndex:1];
NSString *username1 = account1.username;
NSString *username2 = account2.username;
// Always same order
NSLog(userName1);
NSLog(userName2);
}
}];
[self furtherMethodsInCaseOfSuccessfulTweet];
} else if(result == TWTweetComposeViewControllerResultCancelled) {
NSLog(#"twit canceled");
}
[self dismissViewControllerAnimated:YES completion:nil];
};
[self presentModalViewController:tweetSheet animated:YES];
}
else
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"No tweet is possible on this device" delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
}
}
I found a way.
Instead looking for the username on the insctance of TWTweetComposeViewController , lets use GET users/lookup to qwery the names gathered by ACAccountStore.
http://api.twitter.com/1/users/lookup.xml?screen_name=username1,username2
(use json instead xml)
Parsing the results we can get the date/time of the last tweet of the users ("status" tag), and voila, we have the last account used.
In additiom, you can test the qwery results on the console.
Thanks to #theSeanCook
I am trying to make a basic iphone app that shows nearby tweets. I was using the TWRequest object to accomplish this with the twitter search api. Unfortunately, I would actually like to mark the tweets on a map using their GPS coordinates and the search api doesn't seem to return the actual location that a tweet was made with any better accuracy than the city name.
As such, I think I need to switch to the streaming api. I am wondering if it is possible to continue using the TWRequest object in this case or if I need to actually switch over to using NSURLConnection? Thanks in advance!
Avtar
Yes, you can use a TWRequest object. Create your TWRequest object using the appropriate URL and parameters from the Twitter API doco, and set the TWRequest.account property to the ACAccount object for the Twitter account.
You can then use the signedURLRequest method of TWRequest to get an NSURLRequest which can be used to create an asynchronous NSURLConnection using connectionWithRequest:delegate:.
Once this is done, the delegate's connection:didReceiveData: method will be called whenever data is received from Twitter. Note that each NSData object received may contain more than one JSON object. You will need to split these up (separated by "\r\n") before converting each one from JSON using NSJSONSerialization.
It took me a bit of time to get this up and running, So I thought I aught to post my code for others. In my case I was trying to get tweets close to a certain location, so you will see that I used a locations parameter and a location struct I had in scope. You can add whatever params you want to the params dictionary.
Also note that this is bare bones, and you will want to do things such as notify the user that an account was not found and allow the user to select the twitter account they would like to use if multiple accounts exist.
Happy Streaming!
//First, we need to obtain the account instance for the user's Twitter account
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *twitterAccountType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request permission from the user to access the available Twitter accounts
[store requestAccessToAccountsWithType:twitterAccountType
withCompletionHandler:^(BOOL granted, NSError *error) {
if (!granted) {
// The user rejected your request
NSLog(#"User rejected access to the account.");
}
else {
// Grab the available accounts
NSArray *twitterAccounts = [store accountsWithAccountType:twitterAccountType];
if ([twitterAccounts count] > 0) {
// Use the first account for simplicity
ACAccount *account = [twitterAccounts objectAtIndex:0];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:#"1" forKey:#"include_entities"];
[params setObject:location forKey:#"locations"];
[params setObject:#"true" forKey:#"stall_warnings"];
//set any other criteria to track
//params setObject:#"words, to, track" forKey#"track"];
// The endpoint that we wish to call
NSURL *url = [NSURL URLWithString:#"https://stream.twitter.com/1.1/statuses/filter.json"];
// Build the request with our parameter
TWRequest *request = [[TWRequest alloc] initWithURL:url
parameters:params
requestMethod:TWRequestMethodPOST];
// Attach the account object to this request
[request setAccount:account];
NSURLRequest *signedReq = request.signedURLRequest;
// make the connection, ensuring that it is made on the main runloop
self.twitterConnection = [[NSURLConnection alloc] initWithRequest:signedReq delegate:self startImmediately: NO];
[self.twitterConnection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[self.twitterConnection start];
} // if ([twitterAccounts count] > 0)
} // if (granted)
}];