Does TWRequest work for the twitter streaming api? - ios

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)
}];

Related

Force app to wait for method completion (data download)

I'm working with an app that requests data from an OAuth2.0 protected server. When I use the GTM OAuth Library to retrieve data, the program continues to run while the data is being downloaded in the background. I need some sort of mechanism to either force my application to wait until the didFinishWithData selector is called,or I need a way to notify my ViewController of the download's completion, so I can then utilize the data immediately.
I've tried conditional blocks, but those aren't doing it for me. I've also tried polling the object whose data I'm interested in, but if I do that, the data never seems to download. I've heard I can somehow utilize the Notification Center to accomplish this task, so I'll look more into that while I'm waiting for replies here.
Here is basically what is going on:
-(void) getAlert{
// Define the URL of the API module we'd like to utilize.
NSURL *url = [[NSURL alloc] initWithString:#"https://access.active911.com/interface/open_api/api/alerts"];
// Constructs a an HTTP request object to send to the server in order to obtain data.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:#"1" forHTTPHeaderField:#"alert_days"];
// This fetcher sends the request along with the authentication header in a recognizable manner.
GTMHTTPFetcher *fetcher = [[GTMHTTPFetcher alloc] initWithRequest:request];
// Attach the OAuth credentials for the fetcher's use.
[fetcher setAuthorizer:auth];
// Execute the operation.
[fetcher waitForCompletionWithTimeout:10];
NSLog(#"About to get alert");
[fetcher beginFetchWithDelegate:self didFinishSelector:#selector(responseHandler:finishedWithData:finishedWithError:)];
NSLog(#"got alert");
}
-(void)responseHandler:(id)valueNotUsed finishedWithData:(NSData *)data finishedWithError:(NSError *)error{
// Retrieve the server data in a usable object
// All that's being done here is conversion to an NSDictionary
// followed by the creation of subdictionaries from that dictionary
// until our final value can be picked directly out of the resulting dict
NSData *jsonData = [[NSData alloc] initWithData:data];
NSError *dictError;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:jsonData //1
options:kNilOptions
error:&dictError];
NSDictionary *token = [json objectForKeyedSubscript:#"message"];
NSArray *alerts = [token objectForKeyedSubscript:#"alerts"];
NSDictionary *alertData = alerts[0];
mapCode = [alertData objectForKeyedSubscript:#"map_code"];
NSString *city = [alertData objectForKeyedSubscript:#"city"];
NSLog(#"Map code: '%#' with city '%#' and access token %#", mapCode, city, accessToken);
}
And I need to pass the mapCode to my view controller.
Thanks for the help!
First off, please rethink about having the UI halt while you fetch results from the server. This can create an extremely bad UX for the app and only should be done if absolutely necessary.
Second, does your responseHandler method work? And do you only need mapCode in the VC that responseHandler is in?
If so, you don't even need to use Notifications. Simply do:
-(void)responseHandler:(id)valueNotUsed finishedWithData:(NSData *)data finishedWithError:(NSError *)error{
...
...
mapCode = [alertData objectForKeyedSubscript:#"map_code"];
[self updateVCWithMapCode:mapCode];
}
That will call the method after the response has been received. Passing it explicitly too so you don't need to have mapCode be a property as well.

iOS Twitter Reverse OAuth

I have been pouring over the internet for days now trying to figure out how to implement this.
I need to request the access token and secret from twitter in order to pass this to a server that will process the users tweets for my application.
I have been following this link https://dev.twitter.com/docs/ios/using-reverse-auth
The problem is step 1. They dont give you an example of step 1.
Here is my code:
NSURL *url = [NSURL URLWithString:TW_OAUTH_URL_REQUEST_TOKEN];
NSDictionary *parameters = #{TW_X_AUTH_MODE_KEY:TW_X_AUTH_MODE_REVERSE_AUTH};
SLRequest *getTwitterAuth = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:url parameters:parameters];
// Assume that we stored the result of Step 1 into a var 'resultOfStep1'
NSString *S = resultOfStep1;
NSDictionary *step2Params = [[NSMutableDictionary alloc] init];
[step2Params setValue:#"kfLxMJsk7fqIuy8URhleFg" forKey:#"x_reverse_auth_target"];
[step2Params setValue:S forKey:#"x_reverse_auth_parameters"];
NSURL *url2 = [NSURL URLWithString:#"https://api.twitter.com/oauth/access_token"];
SLRequest *stepTwoRequest =
[SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:url2 parameters:step2Params];
// You *MUST* keep the ACAccountStore alive for as long as you need an ACAccount instance
// See WWDC 2011 Session 124 for more info.
self.accountStore = [[ACAccountStore alloc] init];
// We only want to receive Twitter accounts
ACAccountType *twitterType =
[self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Obtain the user's permission to access the store
[self.accountStore requestAccessToAccountsWithType:twitterType
withCompletionHandler:^(BOOL granted, NSError *error) {
if (!granted) {
// handle this scenario gracefully
} else {
// obtain all the local account instances
NSArray *accounts =
[self.accountStore accountsWithAccountType:twitterType];
// for simplicity, we will choose the first account returned - in your app,
// you should ensure that the user chooses the correct Twitter account
// to use with your application. DO NOT FORGET THIS STEP.
[stepTwoRequest setAccount:[accounts objectAtIndex:0]];
// execute the request
[stepTwoRequest performRequestWithHandler:
^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSString *responseStr =
[[NSString alloc] initWithData:responseData
encoding:NSUTF8StringEncoding];
// see below for an example response
NSLog(#"The user's info for your server:\n%#", responseStr);
}];
}
}];
I have been trying to figure out how I process the SLRequest in oder to pass it to step 2 from the twitter docs.
I have also used this here: https://github.com/seancook/TWReverseAuthExample
This code is great but very complex. Any help would be greatly appreciated! Thanks!
The reason step one doesn't have any code is that they assume you will do this on your server or before hand or something like that. Basically you need to generate a key that your app will use to convert iOS tokens to normal tokens.
There is a script that will make the request for you here: http://www.ananseproductions.com/twitter-reverse-auth-headaches/ Its written in ruby so you could use something similar if you have a ruby server.
Personally I would have my app request this token from my server, then make the request to twitter, then post the new token back to my server.
Here is a class to help accomplish just this with a single method call that returns a dictionary with the token and token secret.
https://github.com/kbegeman/Twitter-Reverse-Auth
Hope this helps others out!
As of this code https://github.com/seancook/TWReverseAuthExample , it's fairly simple to implement in your own application. I prefer to create reusable classes, so I don't have to implement the same code multiple times. Normally you would create some singleton and work with it on the following tutorial. However the point of this instruction is not to teach you how to create singletons, so for the simplicity sake, we will use AppDelegate.h/m which is easily accessible from all over the application.
All you have to do is the following:
Open yours and Sean Cook's project (the one which URL is above)
Drag and copy Source->Vendor->ABOauthCore group into your project
Select TWAPIManager.h/m, TWSignedRequest.h/m and copy them into your project
Add the below code into your AppDelegate.h file
#property (nonatomic, strong) ACAccountStore* store;
#property (nonatomic, strong) TWAPIManager *apiManager;
#property (nonatomic, strong) NSArray *accounts;
-(void)storeAccountWithAccessToken:(NSString *)token secret:(NSString *)secret;
-(void)performReverseAuth:(id)sender inView:(UIView*)viewToDisplaySheet;
-(void)_refreshTwitterAccounts;
Now paste the following methods into your AppDelegate.m file
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex;
-(void)_refreshTwitterAccounts;
-(void)_obtainAccessToAccountsWithBlock:(void (^)(BOOL))block;
-(void)performReverseAuth:(id)sender inView:(UIView*)viewToDisplaySheet;
In some initialization method of your file, or as of this example in: `application: didFinishLaunchingWithOptions' paste the following code:
_store = [[ACAccountStore alloc] init];
_apiManager = [[TWAPIManager alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(_refreshTwitterAccounts) name:ACAccountStoreDidChangeNotification object:nil];
Remember to remove observer using the following code. Paste it in AppDelegate.m:
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Open your app-Info.plist file and add 2 string keys. Take their values from: https://apps.twitter.com/
TWITTER_CONSUMER_KEY
TWITTER_CONSUMER_SECRET
In the View Controller that you want to use to implement twitter features, in the viewDidLoad method, add the following code:
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate _refreshTwitterAccounts];
OK, finally you are ready to start the whole machine. In the View Controller that you want to use to implement twitter features, create UIButton called _reverseAuthBtn and create an IBAction to it. Then in your IBAction paste the following code:
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate performReverseAuth:sender inView:self.view];
Whew, I guess that's it! If I haven't forgotten about anything, you have got Twitter Reverse Oauth implementation, and if you want to use it in multiple view controllers, all you have to do is do steps 1-8, and then paste the code from the steps 9 and 10 into your view controller.
Best regards!
Use this lib, it works perfectly!
https://github.com/nst/STTwitter
Info how to implement: https://github.com/nst/STTwitter#reverse-authentication
:)

Sending Tweet without TWTweetComposeViewController

I want to send a tweet directly from my iPhone app without showing TWTweetComposeViewController pop up
Also i want to get all followers
is there a way to do it from twitter framework on the ios5 or should i use another api!
If I have to use another api can u specify a great api for me ? because all the apis i found
on the internet was so old.
Thanks in advance
You can use TWRequest for programatically posting tweets from my app, if you are using iOS 5.0 and up. Posting tweets is quite straightforward and there is no need for an external framework or anything.
You need the #import <Twitter/Twitter.h>. You retrieve the twitter account from the iPhone account store (you could check if there are multiple accounts) and then you use the account to post the request.
Here is an example of a method for posting a tweet with an image.
- (void)shareTwitterImage:(UIImage *)image
{
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error)
{
if(granted)
{
NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];
if ([accountsArray count] > 0)
{
ACAccount *twitterAccount = [accountsArray objectAtIndex:0];
TWRequest *postRequest = [[TWRequest alloc] initWithURL:[NSURL URLWithString:#"https://upload.twitter.com/1/statuses/update_with_media.json"] parameters:[NSDictionary dictionaryWithObject:self.textViewOutlet.text forKey:#"status"] requestMethod:TWRequestMethodPOST];
[postRequest addMultiPartData:UIImagePNGRepresentation(image) withName:#"media" type:#"multipart/png"];
[postRequest setAccount:twitterAccount];
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
//show status after done
NSString *output = [NSString stringWithFormat:#"HTTP response status: %i", [urlResponse statusCode]];
NSLog(#"Twiter post status : %#", output);
}];
}
}
}];
}
There are quite a few options:
Well, on iOS 6, there is the Apple Social framework, and you can use SLComposeViewController to post to Twitter as well as Facebook and Sina Weibo, here are the docs. TWTweetComposeViewController has been deprecated, devices running iOS 6 will have to run into backward compatibility -- so avoid that.
There is also ShareKit, however I do not generally recommend it, its messy and bloated. Lastly there is the Oauth Library in iOS... create a consumer key and secret key. You can also call the Twitter API with TWRequest.
That's all of them I can think of.
Rohan.
In case you plan on integrating TwitterKit by Twitter to perform the tweets via your custom twitter app then this might help you.
https://stackoverflow.com/a/28602749/1740354
With the same methods you can get the followers list by using the following API
https://dev.twitter.com/rest/reference/get/followers/list
Hope it helps.

Twitter iOS Streaming API: no data being received

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)
}];

Using a Hidden Twitter Account

Not sure if any one has tried this. I want to access twitter with an account that the user of the phone doesn't have access to.
The way I have looked at it is by adding a twitter account using the OAth (token/secret)
I can add an account using the following code.
NSString *token = #"blahblahblah";
NSString *secret = #"blahblahblah";
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountCredential *credential = [[ACAccountCredential alloc] initWithOAuthToken:token tokenSecret:secret];
ACAccountType *twitterAcctType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
ACAccount *newAccount = [[ACAccount alloc] initWithAccountType:twitterAcctType];
newAccount.credential = credential;
[store saveAccount:<#(ACAccount *)#> withCompletionHandler:<#^(BOOL success, NSError *error)completionHandler#>
[store saveAccount:newAccount withCompletionHandler:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"Account was saved!");
} else {
//something went wrong, check value of error
}
}];
But this code stores the user id associataed with the secret in the iphones account store. The problem being that if the phone user then uses twitter the account privilages are still there for him to use...
There doesn't seem to be a store deleteAccount method implemented
For my second attempt i tried using just the user that was created without adding it to the account store but I think the SaveAccount method is what verifies the token and secret and gets the user name...
Any tips???
Just a friendly reminder that this will most likely have your app banned from the app store, as this can easily be classified as spying on the user/device, if you can pull it off which I doubt.
Also, beware of having thousands/millions of users twitting from the same account.
Finally, there does not seem to be a way to remove the account, as you said, plus in order to use it you'd need to use requestAccessToAccountsWithType:withCompletionHandler: which would trigger a notice to the user and would make them think you were requesting access to his tweeter account, rather than yours...

Resources