Getting tweets via TWRequest for a tableview - uitableview

Ok, I'm pretty new to this and been struggling with what you guys may feel is quite an easy exercise. I have trawled high and low and cannot find a good tutorial or walkthrough on how to do this. Basically, I am using the code below to obtain tweets and I only want the 'text' part of the tweet. How do I extract it out of the NSDictionary in order to use the 'text' key in a tableview? I have tried [dict objectForKey:#"text"] but it does not work - 'dict' does not seem to contain a 'text' attribute. Thanks in advance for any help.
// Do a simple search, using the Twitter API
TWRequest *request = [[TWRequest alloc] initWithURL:[NSURL URLWithString:
#"http://search.twitter.com/search.json?q=iOS%205&rpp=5&with_twitter_user_id=true&result_type=recent"]
parameters:nil requestMethod:TWRequestMethodGET];
// Notice this is a block, it is the handler to process the response
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if ([urlResponse statusCode] == 200)
{
// The response from Twitter is in JSON format
// Move the response into a dictionary and print
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
NSLog(#"Twitter response: %#", dict);
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];

Yes there is an objectForKey#"text" but it is an array which means that every entry (tweet) has text (and several other attributes). So we've to loop through the tweets to get FOR every tweet the text.
In your .h file
NSMutableArray *twitterText;
In your .m file
Do this somewhere in viewdidload
twitterText = [[NSMutableArray alloc] init];
And now we can loop through your results. Paste this code where you've NSLog(#"Twitter response: %#", dict);
NSArray *results = [dict objectForKey#"results"];
//Loop through the results
for (NSDictionary *tweet in results)
{
// Get the tweet
NSString *twittext = [tweet objectForKey:#"text"];
// Save the tweet to the twitterText array
[twitterText addObject:(twittext)];
And for your cells in your tableView
cell.textLabel.text = [twitterText objectAtIndex:indexPath.row];
I think that should be working.

Related

How would I get the latest tweet from a user and update it it in a UILabel?

I'm trying to get the most recent tweet from one twitter account and update that in a UILabel on my view. The tweet should be stored in a string and updated every time a new tweet is available. I tried using the twitter API with parameters of "screen_name" and "count", and storing all the tweets in an array. When I print the array, it returns a null. Any help would be greatly appreciated.
Edit:
I am getting a "bad authentication data" error now.
NSURL *requestAPI = [NSURL URLWithString:#"https://api.twitter.com/1.1/search/tweets.json"];
NSDictionary *parameters = #{#"q": #"#user",
#"result_type":#"recent",
#"count": #"1"};
SLRequest *posts = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:requestAPI parameters:parameters];
[posts performRequestWithHandler:^(NSData *response, NSHTTPURLResponse *urlResponse, NSError *error){
self.tweet = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableLeaves error:&error];
NSLog(#"%#",self.tweet);
}];
If you want to obtain the tweets from one specific user, you should probably use App Only Authentication (See link: https://dev.twitter.com/docs/auth/application-only-auth). Also, I would suggest using the STTwitter library to simplify things. See code below:
STTwitterAPI *twitter = [STTwitterAPI twitterAPIAppOnlyWithConsumerKey:#"CONSUMER KEY" consumerSecret:#"CONSUMER SECRET KEY"];
[twitter verifyCredentialsWithSuccessBlock:^(NSString *username) {
[twitter getUserTimelineWithScreenName:#"thetwitteraccount" count:100 successBlock:^(NSArray *statuses) {
self.tweets = [NSMutableArray arrayWithArray:statuses];
//Say we want to access the text of the first tweet in the array
NSDictionary *text = self.tweets[0];
self.yourlabel.text = text[#"text"];
} errorBlock:^(NSError *error) {
code
}];
} errorBlock:^(NSError *error) {
code
}];

What goes wrong to access twitter timeline account?

I tried the fallowing code to get access twitter timeline. It doesn't received any data from the server.What went wrong here?
ACAccount *twitterAccount=[arrayOfAccounts lastObject];
NSURL *requestURL=[NSURL URLWithString:#"http://api.twitter.com/1/statuses/user_timeline.json"];
NSMutableDictionary *parameters=[NSMutableDictionary new];
//[parameters setObject:#"100" forKey:#"count"];
//[parameters setObject:#"1" forKey:#"include_entities"];
SLRequest *post=[SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:requestURL parameters:parameters];
post.account=twitterAccount;
[post performRequestWithHandler:^(NSData *response, NSHTTPURLResponse *urlResponse, NSError *error) {
self.array=[NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableLeaves error:&error];
if(self.array.count !=0)
NSLog(#"%#",self.array);
else
NSLog(#"No Data Recived");
Thanks in advance.
Twitter has advice to use Version 1.1 not advice v1. In version 1.1 api https,So try to use this url https://api.twitter.com/1.1/statuses/user_timeline.json instated of this url http://api.twitter.com/1/statuses/user_timeline.json. This work's fine.
Those NSError objects the API gives you? Their purpose is to tell you what went wrong. Use them.
Your problem is that you don't know what happened because you just try to convert to JSON. That is what could have went wrong:
the request failed (e.g. network problem)
you are not authorized to do whatever you did
the data returned is not actually JSON
the JSON object is not an array (would have lead to a crash).
To write defensive code (and that's what you want if you want to release this thing to the public) you have to check each of these steps to figure out what went wrong, so you can act accordingly. Yes, that will take more code, but less code is not always the best choice.
Code with better error handling would more look like this. Note how it checks the result of each step that could go wrong:
[post performRequestWithHandler:^(NSData *response, NSHTTPURLResponse *urlResponse, NSError *error) {
if (response) {
// TODO: might want to check urlResponse.statusCode to stop early
NSError *jsonError; // use new instance here, you don't want to overwrite the error you got from the SLRequest
NSArray *array =[NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableLeaves error:&jsonError];
if (array) {
if ([array isKindOfClass:[NSArray class]]) {
self.array = array;
NSLog(#"resulted array: %#",self.array);
}
else {
// This should never happen
NSLog(#"Not an array! %# - %#", NSStringFromClass([array class]), array);
}
}
else {
// TODO: Handle error in release version, don't just dump out this information
NSLog(#"JSON Error %#", jsonError);
NSString *dataString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSLog(#"Received data: %#", dataString ? dataString : response); // print string representation if response is a string, or print the raw data object
}
}
else {
// TODO: show error information to user if request failed
NSLog(#"request failed %#", error);
}
}];

What am I doing wrong with the Pocket API for Objective-C that's causing archive commands to fail?

I'm really scratching my head at this one. I'm using the Pocket API to allow users to archive Pocket articles from my app, but whenever I try to do so with the below code I get this error:
Error Domain=PocketSDK Code=400 "Invalid request, please refer to API documentation" UserInfo=0xc17d3b0 {NSLocalizedDescription=Invalid request, please refer to API documentation}
Code:
NSDictionary *arguments = #{#"action": #"archive",
#"item_id": articleID};
[[PocketAPI sharedAPI] callAPIMethod:#"send" withHTTPMethod:PocketAPIHTTPMethodPOST arguments:arguments handler:^(PocketAPI *api, NSString *apiMethod, NSDictionary *response, NSError *error) {
if (!error) {
NSLog(#"Archived article.");
}
}];
Exactly what part of that is incorrect? Am I not POSTing a send method to the API?
EDIT: I even changed it to have #"action" be #"actions" and to supply it the above NSDictionary, and it returns without an error but doesn't affect it on the Pocket website...
EDIT 2: Per the response of Joseph Chen I changed my code to the following:
// Create data to pass to the Pocket API (a JSON array of actions)
NSError *error;
NSArray *actions = #[#{#"action": #"archive",
#"item_id": articleID}];
NSData *actionsAsJSONData = [NSJSONSerialization dataWithJSONObject:actions options:kNilOptions error:&error];
NSString *actionsAsJSONString = [[NSString alloc] initWithData:actionsAsJSONData encoding:NSUTF8StringEncoding];
NSDictionary *arguments = #{#"actions": actionsAsJSONString};
[[PocketAPI sharedAPI] callAPIMethod:#"send" withHTTPMethod:PocketAPIHTTPMethodPOST arguments:arguments handler:^(PocketAPI *api, NSString *apiMethod, NSDictionary *response, NSError *error) {
if (!error) {
NSLog(#"%#", response);
}
else {
NSLog(#"%#", error);
}
}];
Which returns:
action_results" = (
1
);
status = 1;
Yet when I go to the website and log in, the article I "archived" is still staring me in the face, unarchived.
According to the documentation the actions parameter should be a JSON dictionary. So you could either...
Create the JSON dictionary manually:
NSString *jsonString = [NSString stringWithFormat:#"[{\"action\":\"archive\",\"item_id\":\"%#\"}]", articleID]; // articleID is a NSString?
NSDictionary *arguments = #{#"actions": jsonString};
Use NSJSONSerialization:
NSDictionary *actions = #{#"action": #"archive", #"item_id": articleID};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:actions
options:kNilOptions
error:&error];
NSString *jsonString = [[NSString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding];
NSDictionary *arguments = #{#"actions": jsonString};
This answer is also a reference.
Here's the code taken (almost) straight from my app:
NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
NSDictionary *arguments = #{#"actions" : #[#{#"action" : #"archive",
#"item_id" : itemId,
#"time" : [NSString stringWithFormat:#"%ld", (long)timestamp]}]};
[self.pocketAPI callAPIMethod:#"send"
withHTTPMethod:PocketAPIHTTPMethodPOST
arguments:arguments
handler:^(PocketAPI *api, NSString *apiMethod, NSDictionary *response, NSError *error)
{
if (!error) {
// OK
} else {
// handle error
}
}];

NSMutableArray appears empty but is being populated

Am having a major problem with an NSMutableArray, I'm probably missing something really obvious - have probably been looking at it so much that I just can't see it.
Am reading some Tweets and then using the results to populate an NSMutableArray :
#synthesize testArray;
- (void)viewDidLoad{
[super viewDidLoad];
testArray = [[NSMutableArray alloc] init];
TWRequest *request = [[TWRequest alloc] initWithURL:[NSURL URLWithString:#"http://search.twitter.com/search.jsonq=kennedy&with_twitter_user_id=true&result_type=recent"]
parameters:nil requestMethod:TWRequestMethodGET];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if ([urlResponse statusCode] == 200) {
// The response from Twitter is in JSON format
// Move the response into a dictionary
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
NSArray *results = [dict objectForKey:#"results"];
//Loop through the results
for (NSDictionary *tweet in results) {
tweetStore *tweetDetails = [[tweetStore alloc] init];
NSString *twitText = [tweet objectForKey:#"text"];
//Save the tweet to the twitterText array
tweetDetails.name = #"test";
tweetDetails.date = #"test";
tweetDetails.tweet = twitText;
[testArray addObject:tweetDetails];
}
tweetStore *retrieveTweet = (tweetStore*)[testArray objectAtIndex:0];
NSLog(#"tweet is: %#", retrieveTweet.tweet);
//NSLog(#"Array is: %#", testArray); - *can* view Array etc here
}
else {
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}
}
];
NSLog(#"test array: %#", testArray); //Array is now empty......
}
I can view the array within the code where I've marked it, retrieve the objects I want etc, it works perfectly. But as soon as I try to access or display anything from the array outside of this then it seems to be empty. Any help or pointers would be appreciated.
The problem is that the code in the block is performed in an asynchronous way, so it is executed after the NSLog code.
The code executed follows this flow:
First you perform the initialization:
[super viewDidLoad];
testArray = [[NSMutableArray alloc] init];
TWRequest *request = [[TWRequest alloc] initWithURL:[NSURL URLWithString:#"http://search.twitter.com/search.jsonq=kennedy&with_twitter_user_id=true&result_type=recent"]
parameters:nil requestMethod:TWRequestMethodGET];
You start the request:
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) { <block_code> } ];
You print array information and it is empty (correct):
NSLog(#"test array: %#", testArray);
Now the asynchronous request ends and the code in the block is executed: so the array is populated correctly only in the block code.

Weird error with NSJSONSerialization and UIPIckerView

The way I have my program working is it pulls data down into the program via AFHTTPClient. Then I get my response data. I then take this data and parse it into a NSMutableArray via NSJSONSerialization. This array is used to populate a UIPickerView.
This method fires when the user opens the app or presses a refresh button. The problem is when the app opened, the call is made and I get the data back. If I go to the picker it seems empty but when you scroll down the bottom 2 of 5 are in there and when you scroll back up the others get in too. If at any point I press the refresh button the errors go away and the picker is properly populated. Any reason why it is not working properly? I reloadAllComponents after each call is made too.
-(void) getData{
// Paramaters
NSString *lat = #"40.435615";
NSString *lng = #"-79.987872";
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys: lat, #"lat", lng, #"lng", nil];
// posting the data and getting the response
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://mysite.com/"]];
[client postPath:#"/mypostpath.php" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *text = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"Response: %#", text);
NSError *error;
json = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
// setting the first location to the text feild
NSDictionary* name = [json objectAtIndex:0];
locationField.text = [name objectForKey:#"name"];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#", [error localizedDescription]);
}];
// reload the picker
[pickerCust reloadAllComponents];
// setting the first location to the text feild
NSDictionary* name = [json objectAtIndex:0];
locationField.text = [name objectForKey:#"name"];
}
The problem is because the [pickerCust reloadAllComponents]; is being called before the block that loads the data is finished. Move the call into the success block.
Since it interacts with a UI component, wrap it in an dispatch_async so it runs on the main queue.
dispatch_async(dispatch_get_main_queue(), ^{
[pickerCust reloadAllComponents];
});

Resources