Ok, I'm a bit out of my league but here goes... I'm working on making some updates to an iPhone app for a client (it hasn't been updated since 2013...just to put into context how old the programming is). While making simple updates to the app, I noticed the twitter feed section kept crashing. I checked the debug and came up with this error.
[_NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array
I know the array is empty... I'm just not sure why. Here's the code where I think it is filling the array
- (void)loadData {
NSURL *tutorialsUrl = [NSURL URLWithString:#"https://twitter.com/EdwinOrange/lists/kentucky-general-assembly"];
NSData *tutorialsHtmlData = [NSData dataWithContentsOfURL:tutorialsUrl];
TFHpple *tutorialsParser = [TFHpple hppleWithHTMLData:tutorialsHtmlData];
NSString *tutorialsXpathQueryString = #"//p[#class='TweetTextSize js-tweet-text tweet-text']";
NSArray *tutorialsNodes = [tutorialsParser searchWithXPathQuery:tutorialsXpathQueryString];
NSMutableArray *newTutorials = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in tutorialsNodes) {
NSString *strResponse = #"";
for (int l=0; l<[[element children] count]; l++)
{
strResponse =[strResponse stringByAppendingString:[[[element children] objectAtIndex:l] content]];
NSArray *_children = [[[element children] objectAtIndex:l] children];
NSLog(#"count = %d",[_children count]);
for (int k=0; k<[_children count]; k++)
{
NSLog(#"%#",[[_children objectAtIndex:k] children]);
NSArray *_internalChildren = [[_children objectAtIndex:k] children];
for (int j=0; j<[_internalChildren count]; j++)
{
NSLog(#"%#",[[_internalChildren objectAtIndex:j] content]);
strResponse = [strResponse stringByAppendingFormat:#"%#",[[_internalChildren objectAtIndex:j] content]];
}
}
}
Tutorial *tutorial = [[Tutorial alloc] init];
[newTutorials addObject:tutorial];
tutorial.title = strResponse;
NSLog(#"strResponse = %#",strResponse);
NSLog(#"Data---%#",tutorial.title);
}
_data = newTutorials;
delegateObj.arrDetailContent = [_data mutableCopy];
}
It might also we worth it to note the Log does not return any information for the above code. I added an exception breakpoint which breaks at this line (because the _data array is empty)
Tutorial *data = [_data objectAtIndex:indexPath.row];
This may also help to track down the problem (this is at the top of the TwitterController.m)
#pragma mark - Calling Twitter feeds
-(void)getAndParseTwitterFeeds
{
[self loadAtData];
[self loadNames];
[self loadData];
[self loadImages];
[self loadHours];
[self loadLink];
[self loadImages];
[self loadHours];
}
-(void)getFeeds
{
[self getAndParseTwitterFeeds];
}
-(void)getFeedsLocally
{
_link = delegateObj.arrTwitterLink;
_objects = delegateObj.arrObjects;
_members = delegateObj.arrMemberName;
_data = delegateObj.arrDetailContent;
arrProfileImages = delegateObj.arrImages;
_hours = delegateObj.arrHour;
[self updateTableViewWithTheData];
}
Hope that is enough explanation... I'm really hoping someone can help me figure out why the array is not being filled. Thanks!!!!
EDIT: I'm starting to wonder now if the HTML parser is having trouble with images in tweets. This code is from around 2013 which pre-dates twitter's inline images. Could this be causing the array to return empty?
Also of note the arrays _link, _members, _hours, etc. are all being populated correctly and have identical coding up until the "NSString *strResponse" section for loadData.
Related
I have developed an application in which I am using this: BTSimpleSideMenu
In my app I am passing cell label's through this:
-(void)show{
sideMenu.delegate = self;
int count;
count = [rssOutputData count];
for (int i = 0; i < count; i++){
NSString *items = [[rssOutputData objectAtIndex:i]xmltitle];
BTSimpleMenuItem *item = [[BTSimpleMenuItem alloc]initWithTitle:[[rssOutputData objectAtIndex:i]xmltitle] image:[UIImage imageNamed:#"arrow.png"]
onCompletion:^(BOOL success, BTSimpleMenuItem *item) {
catLbl.text = [[rssOutputData objectAtIndex:i]xmltitle];
}];
sideMenu = [[BTSimpleSideMenu alloc]initWithItem:#[item] addToViewController:self];
}
[sideMenu toggleMenu];
}
In this code [[rssOutputData objectAtIndex:i]xmltitle] is actually the data which I am parsing from an XML file. I have a total of 5 entries in it that I want to show in the side menu, but unfortunately through this way the menu is only showing the last entry and only a single row.
I know this might be because it is overwriting entries on the first cell.
Please view the link given above and please help me out with this.
This code will solve your problem your are creating BtSimplemenuitem and BTSimplesidemmenu in every iteration so it will make a lot of sidemenu just change your code to this will work
-(void)show
{
sideMenu.delegate = self;
int count;
NSMutableArray *itemsArry = [[NSMutableArray alloc] init];
count = [rssOutputData count];
for (int i = 0; i < count; i++){
NSString *items = [[rssOutputData objectAtIndex:i]xmltitle];
BTSimpleMenuItem *item = [[BTSimpleMenuItem alloc]initWithTitle:[[rssOutputData objectAtIndex:i]xmltitle] image:[UIImage imageNamed:#"arrow.png"]
onCompletion:^(BOOL success, BTSimpleMenuItem *item) {
catLbl.text = [[rssOutputData objectAtIndex:i]xmltitle];
}];
[itemsArry addObject:item];
}
NSArray *itemSarry=[[NSArray alloc] initWithArray:itemsArry];
sideMenu = [[BTSimpleSideMenu alloc]initWithItem:itemSarry addToViewController:self];
[sideMenu toggleMenu];
}
This question already has answers here:
populating a tableview with data using JSON and AFNetworking NSDictionary
(1 answer)
JSON data is not loading in slow internet connection? [closed]
(1 answer)
Closed 8 years ago.
I'm building an article reading app.I'm parsing JSON data using NSData in UITableView.
I'm facing an issue that is data is not load in slow internet speed(2g or 3g)means UI is empty.I want to implement NSUrlConnection
but i'm new in iOS development unable to implement NSUrlConnection in my code.
this is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
BOOL myBool = [self isNetworkAvailable];
if (myBool)
{
#try {
// for table cell seperator line color
self.tableView.separatorColor = [UIColor colorWithRed:190/255.0 green:190/255.0 blue:190/255.0 alpha:1.0];
UIBarButtonItem *backbutton1 = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStyleBordered target:nil action:nil];
[[self navigationItem] setBackBarButtonItem:backbutton1];
_Title1 = [[NSMutableArray alloc] init];
_Author1 = [[NSMutableArray alloc] init];
_Images1 = [[NSMutableArray alloc] init];
_Details1 = [[NSMutableArray alloc] init];
_link1 = [[NSMutableArray alloc] init];
_Date1 = [[NSMutableArray alloc] init];
NSData* data = [NSData dataWithContentsOfURL:ysURL];
NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if(ys_avatars){
for (int j=0;j<ys_avatars.count;j++)
{
if( ys_avatars[j][#"title"]==[NSNull null] ){
[_Title1 addObject: #""];
}
else{
[_Title1 addObject:ys_avatars[j][#"title"]];
}
if( ys_avatars[j][#"author"]==[NSNull null] ){
[_Author1 addObject: #""];
}
[_Author1 addObject: ys_avatars[j][#"author"]];
if( ys_avatars[j][#"featured_img"]==[NSNull null] ){
[_Images1 addObject: #""];
}
else{
[_Images1 addObject: ys_avatars[j][#"featured_img"]];
}
if( ys_avatars[j][#"content"]==[NSNull null] ){
[_Details1 addObject: #""];
}else{
[_Details1 addObject:ys_avatars[j][#"content"]];
}
if( ys_avatars[j][#"permalink"]==[NSNull null] ){
[_link1 addObject: #""];
}
else{
[_link1 addObject:ys_avatars[j][#"permalink"]];
}
if( ys_avatars[j][#"date"]==[NSNull null] ){
[_Date1 addObject: #""];
}
else{
NSString *newStr=[ys_avatars[j][#"date"] substringToIndex:[ys_avatars[j][#"date"] length]-3];
[_Date1 addObject:newStr];
}
}
}
else
{
NSLog(#"asd");
}
}
#catch (NSException *exception) {
}
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *Cellidentifier1 = #"ysTableViewCell";
ysTableViewCell *cell1 = [tableView dequeueReusableCellWithIdentifier:Cellidentifier1 forIndexPath:indexPath];
long row = [indexPath row];
cell1.TitleLabel1.text = _Title1[row];
cell1.AuthorLabel1.text = _Author1[row];
NSString *StoryUrl = [_Images1[indexPath.row] stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
if(StoryUrl) {
NSArray *subStringsUrl = [yourStoryUrl componentsSeparatedByString:#"/"];
NSString *stripedName = [subStringsUrl lastObject];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* filePath =[documentsDirectory stringByAppendingPathComponent: [NSString stringWithFormat:#"%#",stripedName]];
if(filePath) {
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
if(image) {
ysTableViewCell *updateCell =(id)[tableView cellForRowAtIndexPath:indexPath];
if(updateCell)
updateCell.ThumbImage1.image=image;
cell1.ThumbImage1.image=image;
} else {
dispatch_queue_t taskQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(taskQ, ^{
NSURL *Imageurl = [NSURL URLWithString:yourStoryUrl];
NSData *data = [NSData dataWithContentsOfURL:Imageurl];
UIImage *images1 = [[UIImage alloc] initWithData:data];
NSData *imageData = UIImagePNGRepresentation(images1);
if (![imageData writeToFile:filePath atomically:NO])
{
NSLog((#"Failed to cache image data to disk"));
}
else
{
NSLog(#"the cachedImagedPath is %#",filePath);
}
dispatch_sync(dispatch_get_main_queue(), ^{
ysTableViewCell *updateCell =(id)[tableView cellForRowAtIndexPath:indexPath];
if(updateCell)
updateCell.ThumbImage1.image=images1;
cell1.ThumbImage1.image=images1;
});
});
}
return cell1;
}
Help is appreciated.
Thanks in advance.
This looks really messy, and i suggest you change your whole design.
A basic and cleaner way (but probably not the best/cleanest way) :
Create class to handle outside-of-view related work (JSON parsing here)
Call that class in viewDidLoad to start parsing
Call a method that refreshes your table view with the newly parsed data when the parsing is done (in the JSON class).
That way, the table view will load your placeholders first and then reload itself when it has the data.
In my opinion, a better way would be to populate it before loading it so there is no wait time.
Can you find what you need yourself, or code it alone? or do you need help? If so, with what?
EDIT : You could/should also use the AFNetworking framework that will make your life 10 times easier with JSON/Internet related code.
I usually create a class that handles the load of my data, whether from a URL or local store. You could use AFNetworking, but there is a ton of extra stuff you might not need. The basics of using NSUrlConnection is really easy.
Try this tutorial, it will help you to understand how Apple's implementation works before you add a third party library that masks it for you.
NSUrlConnection Tutorial
I have a string, for example "Soccer". Now I want to move every "e" by lets say 2 indexes(right word?), so my string looks like this = "erSocc". This has to work with whitespace and negative/- indexes.
I came a cross with this, not perfect working, solution:
NSString* text = #"Soccer";
NSString* sign = #"c";
int index = 1;
NSMutableArray* arrayText = [[NSMutableArray alloc]init];
NSMutableArray* arraySignNewPosition = [[NSMutableArray alloc]init];
NSMutableArray* arrayOldSignPosition = [[NSMutableArray alloc]init];
for(int i=0;i<(text.length);i++)
{
[arrayText addObject:[text substringWithRange:NSMakeRange(i, 1)]];
if ([[arrayText objectAtIndex:i]isEqualToString:sign])
{
[arrayOldSignPosition addObject:[NSNumber numberWithInt:i]];
if ((i+index)>(text.length-1))
{
int indexDifference = (i+index)-(text.length);
[arraySignNewPosition addObject:[NSNumber numberWithInt:indexDifference]];
}
else
{
[arraySignNewPosition addObject:[NSNumber numberWithInt:(i+index)]];
}
}
}
for (NSNumber* number in arraySignNewPosition)
{
[arrayText insertObject:sign atIndex:number.integerValue];
if(number.integerValue-index>0)
{
[arrayText removeObjectAtIndex:(number.integerValue-index)];
}
else
{
[arrayText removeObjectAtIndex:((arrayText.count-1)+(number.integerValue-index))];
}
}
I know the code is not working perfectly, but I would like to know if this is the right way or if there are some Cocoa functions I could use to accomplish my goal. Thanks for your time.
You're really just getting substrings and moving them around, so you could do something like this:
- (NSString *)shiftRight:(NSUInteger)places
{
NSAssert(places > 0, #"places must be greater than 0");
NSAssert(places < [self length], #"places must be less than the length of the string");
places = [self length] - places;
NSString *start = [self substringFromIndex:places];
NSString *end = [self substringToIndex:places];
return [start stringByAppendingString:end];
}
Here's a complete code listing, with examples.
I am trying to pull an LDAP "jpegPhoto" attribute from an openLDAP server using a iOS openLDAP framework. The framework pulls the data as a dictionary of NSStrings.
I need to convert the NSString of "jpegPhoto" (which also appears to be base64 encoded) to UIImage, with the end result being that I display the jpegPhoto as the user's image when they login.
More Info:
-(NSDictionary *)doQuery:(NSString *)query:(NSArray *)attrsToReturn {
...
while(attribute){
if ((vals = ldap_get_values_len(ld, entry, attribute))){
for(int i = 0; vals[i]; i++){
//Uncomment if you want to see all the values.
//NSLog(#"%s: %s", attribute, vals[i]->bv_val);
if ([resultSet objectForKey:[NSString stringWithFormat:#"%s",attribute]] == nil){
[resultSet setObject:[NSArray arrayWithObject:[NSString stringWithFormat:#"%s",vals[i]->bv_val]] forKey:[NSString stringWithFormat:#"%s",attribute]];
}else{
NSMutableArray *array = [[resultSet objectForKey:[NSString stringWithFormat:#"%s",attribute]] mutableCopy];
[array addObject:[NSString stringWithFormat:#"%s",vals[i]->bv_val]];
[resultSet setObject:array forKey:[NSString stringWithFormat:#"%s",attribute]];
}
}
ldap_value_free_len(vals);
};
ldap_memfree(attribute);
attribute = ldap_next_attribute(ld, entry, ber);
};
...
}
-(UIIMage *)getPhoto{
NSString *query = [NSString stringWithFormat:#"(uid=%#)",self.bindUsername];
NSArray *attrsToReturn = [NSArray arrayWithObjects:#"cn",#"jpegPhoto", nil];
NSDictionary *rs = [self doQuery:query:attrsToReturn];
NSString *photoString = [[rs objectForKey:#"jpegPhoto"] objectAtIndex:0];
NSLog(#"The photoString is: %i %#",[photoString length],#"characters long"); //returns 4
NSData *photoData = [NSData dataWithBase64EncodedString:photoString];
UIImage *userPhoto = [UIImage imageWithData:photoData];
return userPhoto;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.studentNameLabel.text = [NSString stringWithFormat:#"Hi %#!",[self.ldap getFullName]];
self.studentPhotoImage.image = [self.ldap getPhoto];
[self checkForProctor];
}
Try this code
NSData *dataObj = [NSData dataWithBase64EncodedString:beforeStringImage];
UIImage *beforeImage = [UIImage imageWithData:dataObj];
There are many similar questions in Stackoverflow.. Please refer the following links
UIImage to base64 String Encoding
UIImage from bytes held in NSString
(Since there has been no working code posted for getting the image data from LDAP, I wanted to add this answer for the benefit of future visitors.)
The missing piece was reading the binary data into an NSData object rather than an NSString when you have binary data that might contain NULL (zero) values within it, such as images or GUIDs.
value = [NSData dataWithBytes:vals[0]->bv_val length:vals[0]->bv_len];
+ (NSArray *)searchWithBaseDN:(const char *)baseDN andFilter:(const char *)filter andScope:(int)scope {
...
while(entry)
{
// create a dictionary to hold attributes
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
attribute = ldap_first_attribute(ld, entry, &ber);
while(attribute)
{
if ((vals = ldap_get_values_len(ld, entry, attribute)))
{
if (ldap_count_values_len(vals) > 1) {
NSMutableArray *values = [[NSMutableArray alloc] init];
for(int i = 0; vals[i]; i++) {
[values addObject:[NSString stringWithUTF8String:vals[i]->bv_val]];
}
[dictionary setObject:values forKey:[NSString stringWithUTF8String:attribute]];
} else {
NSObject *value = nil;
if (strcmp(attribute, "thumbnailPhoto") == 0 || strcmp(attribute, "objectGUID") == 0) {
value = [NSData dataWithBytes:vals[0]->bv_val length:vals[0]->bv_len];
} else {
value = [NSString stringWithFormat:#"%s", vals[0]->bv_val];
}
[dictionary setObject:value forKey:[NSString stringWithUTF8String:attribute]];
}
ldap_value_free_len(vals);
};
ldap_memfree(attribute);
attribute = ldap_next_attribute(ld, entry, ber);
};
...
}
I´m using this method to initialize an nsmutablearray
- (void)getAllContacts
{
Contact *contact = [[Contact alloc] init];
self.allContacts = [[NSMutableArray alloc] init];
int i=0;
for (i=0; i<5; i++)
{
contact.nome = [[NSString alloc] initWithFormat:#"Bruno %d", i];
[self.allContacts insertObject:contact atIndex:i];
}
}
Pretty straightforward! But right after, i do a for to print it´s elements like:
for (int i=0; i<[self.allContacts count]; i++)
{
Contact *c = [self.allContacts objectAtIndex:i];
NSLog(#"i=%d\nNome:%#", i, c.nome);
}
And it will show me 5 times the last element "Bruno 4". It doesn´t start from 0 and increments. What should i do to start from 0?
Try this:
- (void)getAllContacts
{
Contact *contact = nil;
self.allContacts = [NSMutableArray array];
int i=0;
for (i=0; i<5; i++)
{
contact = [Contact new];
contact.nome = [NSString stringWithFormat:#"Bruno %d", i];
[self.allContacts addObject:contact];
[contact release]
}
}
and please take a look at: Memoy Management
Because you are inserting the same object 5 times into the array. You need to create a new Contact object at every execution of the for loop.
What you are doing is you're actually adding one instance of the Contact class 5 times in the array and only changing nome property. Here is the correct way to do this:
- (void)getAllContacts
{
//alloc init returns a retained object and self.allContacts calls the setter, which additionally retains it.
self.allContacts = [[[NSMutableArray alloc] init] autorelease];
int i=0;
for (i=0; i<5; i++)
{
//Create the Contact object
Contact *contact = [[Contact alloc] init];
//Set the nome property
contact.nome = [NSString stringWithFormat:#"Bruno %d", i];
//Add the instance to the array
[self.allContacts addObject:contact];
//Release the instance because the array retains it and you're not responsible for its memory management anymore.
[contact release];
}
}