Issue loading Twitter Tweets into UITableView w/ AFNetworking 1.2 - ios

I am using AFNetworking 1.2 to create an App that displays the tweets from the user. The app already successfully authenticates the user but doesn't display the tweets. Here is what I am doing to receive the Tweets.
- (void)fetchTweets
{
self.twitterClient = [[AFOAuth1Client alloc] initWithBaseURL:[NSURL URLWithString:#"https://api.twitter.com/1.1/"] key:#"4oFCF0AjP4PQDUaCh5RQ" secret:#"NxAihESVsdUXSUxtHrml2VBHA0xKofYKmmGS01KaSs"];
[self.twitterClient authorizeUsingOAuthWithRequestTokenPath:#"/oauth/request_token" userAuthorizationPath:#"/oauth/authorize" callbackURL:[NSURL URLWithString:#"floadt://success"] accessTokenPath:#"/oauth/access_token" accessMethod:#"POST" scope:nil success:^(AFOAuth1Token *accessToken, id responseObject) {
[self.twitterClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self.twitterClient getPath:#"statuses/user_timeline.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *responseArray = (NSArray *)responseObject;
tweets = responseArray;
[responseArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"Success: %#", obj);
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
} failure:^(NSError *error) {
NSLog(#"Error: %#", error);
}];
}
Here is how I am attempting to load the received Tweets into to the UITableView:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TweetCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *text = [tweet objectForKey:#"text"];
NSString *name = [[tweet objectForKey:#"user"] objectForKey:#"name"];
cell.textLabel.text = text;
cell.detailTextLabel.text = [NSString stringWithFormat:#"by %#", name];
return cell;
}
I have added the Cell Identifier in Storyboard already, so that is not the issue.

[tableView reloadData];
Reload table after fetching all tweets and assigning it to array
means
- (void)fetchTweets
{
self.twitterClient = [[AFOAuth1Client alloc] initWithBaseURL:[NSURL URLWithString:#"https://api.twitter.com/1.1/"] key:#"4oFCF0AjP4PQDUaCh5RQ" secret:#"NxAihESVsdUXSUxtHrml2VBHA0xKofYKmmGS01KaSs"];
[self.twitterClient authorizeUsingOAuthWithRequestTokenPath:#"/oauth/request_token" userAuthorizationPath:#"/oauth/authorize" callbackURL:[NSURL URLWithString:#"floadt://success"] accessTokenPath:#"/oauth/access_token" accessMethod:#"POST" scope:nil success:^(AFOAuth1Token *accessToken, id responseObject) {
[self.twitterClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self.twitterClient getPath:#"statuses/user_timeline.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *responseArray = (NSArray *)responseObject;
tweets = responseArray;
[tableView reloadData];
//^ Add this line
[responseArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"Success: %#", obj);
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
} failure:^(NSError *error) {
NSLog(#"Error: %#", error);
}];
}
Note : tableView is your tableview name

Related

Update UICollectionViewCell Before Scroll?

I have a working UICollectionViewCell in UITableViewCell. I'm using HWViewPager, so the collectionview cell moves from left to right. Unfortunately, the data is not updated until the user scrolls to the left or to the right. The content that fills up the cell is fetched from json, so I figure that has something to do but I'm not sure what. I load everything in awakeFromNib.
- (void)awakeFromNib {
] videoArray = [[NSMutableArray alloc] init];
[self getDisco];
}
with getDisco being a void function.
- (void)getDisco
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
videoArray = [NSMutableArray arrayWithArray:[responseObject valueForKey:#"releases"]];
// NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
Now, where the problem is.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
if (indexPath.item < [videoArray count]){
DiscoCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CollectionCell2" forIndexPath:indexPath];
NSDictionary *shot = [videoArray objectAtIndex:[indexPath row]];
cell.label2.text = [shot objectForKey:#"title"];
return cell;
return nil;
}else{
DiscoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CollectionCell2" forIndexPath:indexPath];
cell.label2.text = [NSString stringWithFormat:#"Cell %d", indexPath.row];
return cell;
}
}
Without the if/else statements, the viewcontroller crashed completely. With it, nothing updates till the user scrolls. My question is how do I get it all to pre-load, without crashing, for the user? Please keep UICollectionViewCell is in a tableviewcell.
You should reload collectionView after finished the request:
- (void)getDisco
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:#"http://musicbrainz.org/ws/2/release/?query=arid:e0140a67-e4d1-4f13-8a01-364355bee46e%20AND%20primarytype:single&fmt=json&limit=100" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
videoArray = [NSMutableArray arrayWithArray:[responseObject valueForKey:#"releases"]];
// NSLog(#"JSON: %#", responseObject);
[collectionView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}

Crash error: [__NSCFArray objectAtIndex:]: index (10) beyond bounds (10)'

I know this seems to be a fairly common error, but I'm going crazy trying to sort out why I'm getting this message. As I scroll down my tableView, the console throws me this:
Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray objectAtIndex:]: index (10) beyond bounds (10)'
Any idea how I can fix this? I'm trying to get the number of items in the array storageData (e.g. return [storageData count];) See code below.
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.storageData = [[NSMutableDictionary alloc] init];
userName.text = [[[DIOSSession sharedSession] user] objectForKey:#"name"];
emailAddress.text = [[[DIOSSession sharedSession] user] objectForKey:#"mail"];
NSLog(#"%#", [[DIOSSession sharedSession] user]);
NSMutableDictionary *viewParams = [NSMutableDictionary new];
[viewParams setValue:#"storeditems" forKey:#"view_name"];
[DIOSView viewGet:viewParams success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.descripData = responseObject;
NSLog(#"%#",self.descripData);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", [error localizedDescription]);
}];
[DIOSNode nodeIndexWithPage:#"0" fields:#"title" parameters:[NSArray arrayWithObjects:#"storage_item", nil] pageSize:#"20" success:^(AFHTTPRequestOperation *operation, id responseObject) {
__block int iCount = 0;
for (id object in responseObject) {
// NSLog(#"adding object!");
[self.storageData setObject:(NSDictionary *)object forKey:[NSString stringWithFormat:#"%d",iCount]];
iCount++;
[self.tableView reloadData];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//failure
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [storageData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *DoctorsTableIdentifier = #"StorageItemTableViewCell";
StorageItemTableViewCell *cell = (StorageItemTableViewCell *)[tableView dequeueReusableCellWithIdentifier:DoctorsTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"StorageItemTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
NSDictionary *tmp = [self.storageData objectForKey:[NSString stringWithFormat:#"%d",(long)indexPath.row]];
[[cell itemName] setText:(NSString *)[tmp objectForKey:#"title"]];
NSString *title = [tmp objectForKey:#"title"];
[[cell itemName] setText:title];
NSDictionary *node = [self.descripData objectAtIndex:indexPath.row];
[[cell itemDescrip] setText:[node objectForKey:#"body"]];
NSString *secondLink = [[self.descripData objectAtIndex:indexPath.row] objectForKey:#"photo"];
[cell.itemPhoto sd_setImageWithURL:[NSURL URLWithString:secondLink]];
}
return cell;
}

Data not populating on TableView

I am fetching data from the website and loading on the tableViewController. Tableviewcontroller is inside the tabbarcontroller. Whenever I clickked on tabbar, tableview data does not populated. However once I click other viewcontrollers and then click again on tableviewcontroller, then data populated.
#import "GetBookViewController.h"
#import "AFNetworking.h"
#interface GetBookViewController ()
#end
#implementation GetBookViewController
#synthesize booksArray;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
-(void)viewWillAppear:(BOOL)animated
{
[self loadData];
}
-(void)viewDidAppear:(BOOL)animated
{
[self.tableView reloadData];
}
-(void) loadData
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:#"http://XXXXXX.com/coursera/books.php" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
if ([[responseObject valueForKey:#"status"] isEqualToString:#"success"]) {
int count = [[responseObject valueForKey:#"total"] integerValue];
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:count];
for (int i = 1; i <= count; i++) {
NSString *obj = [NSString stringWithFormat:#"%i", i];
[array addObject:[responseObject objectForKey:obj]];
}
booksArray = array;
for (id obj in booksArray) {
NSLog(#"%#", [obj valueForKey:#"title"]);
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [booksArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UILabel* label = (UILabel*)[cell viewWithTag:100];
NSString *title = [[booksArray objectAtIndex:indexPath.item] valueForKey:#"title"];
label.text = title;
return cell;
}
You aren't doing anything once you receive a response from the network and populate your array?
What you need to do is notify the table view that it needs to query its data source again to refresh its values. Simply calling reloadData on your table view once you have your array would to the trick:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:#"http://ilyasuyanik.com/coursera/books.php" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
if ([[responseObject valueForKey:#"status"] isEqualToString:#"success"]) {
int count = [[responseObject valueForKey:#"total"] integerValue];
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:count];
for (int i = 1; i <= count; i++) {
NSString *obj = [NSString stringWithFormat:#"%i", i];
[array addObject:[responseObject objectForKey:obj]];
}
dispatch_async(dispatch_get_main_queue,^{
booksArray = array;
for (id obj in booksArray) {
NSLog(#"%#", [obj valueForKey:#"title"]);
}
//now you can update your table view
[self.tableView reloadData];
});
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];

Issue with Pagintation

I am trying to use the Instagram API for Pagination in my iOS App. However whenever I add my new pictures to my existing array I get this Error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'
Here is my code on how to get the initial set of Instagram Images:
- (void)fetchInstagramPics {
NSUserDefaults *user = [NSUserDefaults standardUserDefaults];
BOOL *status = [user boolForKey:#"isInstagramLoggedIn"];
if (status) {
[[InstagramClient sharedClient] getPath:#"users/self/feed"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// NSLog(#"Response: %#", responseObject);
self.timelineResponse = [responseObject mutableCopy];
[instaPics addObjectsFromArray:responseObject[#"data"]];
[self updateArrays];
[self.tableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
}];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData]; });
}
Here is how I am attempting to add more pictures into the instaPics Array:
- (void)nextInstagramPage:(NSIndexPath *)indexPath{
NSDictionary *page = self.timelineResponse[#"pagination"];
NSString *nextPage = page[#"next_url"];
[[InstagramClient sharedClient] getPath:[NSString stringWithFormat:#"%#",nextPage] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.timelineResponse = [responseObject mutableCopy];
[self.timelineResponse addEntriesFromDictionary:responseObject];
[instaPics addObjectsFromArray:responseObject[#"data"]];
[self.tableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
}];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.y == roundf(scrollView.contentSize.height-scrollView.frame.size.height)) {
NSLog(#"we are at the endddd");
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self nextInstagramPage:indexPath];
}
}
Here are all the properties defined in the Header file of this class:
#property (strong, nonatomic) NSMutableArray *instaPics;
#property (strong, nonatomic) AFOAuth1Client *twitterClient;
#property (strong, nonatomic) IBOutlet UINavigationItem *navBar;
#property (strong, nonatomic) NSMutableArray *tweets;
//Edit: Now have the construction of the Cells:
if (indexPath.row % 2 == 0) {
static NSString *CellIdentifier = #"TweetCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"Background.png"]];
NSDictionary *tweet = tweets[indexPath.row];
return cell;
}else {
static NSString *CellIdentifier = #"InstagramCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"Background.png"]];
NSDictionary *entry = instaPics[indexPath.row];
return cell;
}
}
You have to initialize the NSMutableArray, add instaPics = [[NSMutableArray alloc] init]; in fetchInstagramPics

Assign NSArray from a ViewController to go through a client

I have a NSArray in my ViewController called tweets. To retrieve the tweets I have to use a method in my TwitterClient.m class. The method is shown below:
-(NSArray*)getTimeline {
NSArray *timelineArray;
[self.twitterClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self.twitterClient getPath:#"1.1/statuses/user_timeline.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *responseArray = (NSArray *)responseObject;
[responseArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"Success: %#", obj);
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
return responseArray;
}
However as you know you cant return the responseArray because it is inside a block. What would be a more efficient way to do this.
I have another way but in this method I cannot assign the response array as the NSArray that is the parameter:
-(void)getTimeline:(NSArray*)tweetArray {
[self.twitterClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self.twitterClient getPath:#"1.1/statuses/user_timeline.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *responseArray = (NSArray *)responseObject;
[responseArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"Success: %#", obj);
responseArray = tweetArray;
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
Basically the final question is how do I assign the tweets array in my View Controller to equal the response array that comes in through the TwitterClient class.
You should pass a block that is getting called after you are done getting your required information:
- (void)getTimelineWithCompletionBlock:(void (^)(NSError *err, NSArray *arr)) block {
[self.twitterClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self.twitterClient getPath:#"1.1/statuses/user_timeline.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
(block ? block(nil, responseObject) : nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
(block ? block(error, nil) : nil);
}];
}
You can call the method the following way:
[self getTimelineWithCompletionBlock:^(NSError *err, NSArray *arr) {
}];
Inside the completion block you have the data available and you can update your UI or whatever you intend to do. Depending on the implementation of getPath, you maybe have to dispatch the completion block on the main queue, because you are just allowed to update UI on the main thread.

Resources