Add a field to each object inside an array - ios

I'm parsing XML data of a third party provider.
They include all the information i need for each Team, except their logo.
I have all the logos imported into my project.
Is there a way to add a logo field to each team?
This is my parse method, works perfectly
-(void) parseXML{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"apikeygoeshere"]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *xmlString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSDictionary *xml = [NSDictionary dictionaryWithXMLString:xmlString];
NSMutableArray *items = [xml objectForKey:#"TeamLeagueStanding"];
NSString *nullentry = #""; // custom code for specific reason
NSString *nullentry2 = #""; // custom code for a specific reason
[items insertObject:nullentry atIndex:0]; // custom code for a specific reason
[items insertObject:nullentry2 atIndex:1]; // custom code for a specific reason
[self setTableData:items];
}
This is my cellForRowAtIndexPath method, as you will see my logos are being feed from an array that i have inside my code, but if the team that is in 1st place drops to 2nd, the logos won't change position
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"StandingsIdent";
StandingsViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *item = [tableData objectAtIndex:[indexPath row]];
if ([item isKindOfClass:[NSDictionary class]]) {
long row = [indexPath row];
cell.cellTeamName.text = [item objectForKey:#"Team"];;
cell.cellTeamLogo.image = [UIImage imageNamed:_teamLogos[row]];
cell.cellTeamPosition.text = _teamPosition[row];
cell.cellPlayed.text = [item objectForKey:#"Played"];
cell.cellWins.text = [item objectForKey:#"Won"];
cell.cellTies.text = [item objectForKey:#"Draw"];
cell.cellLoses.text = [item objectForKey:#"Lost"]; ;
cell.cellPoints.text = [item objectForKey:#"Points"];
cell.cellInfo.text = _infoLeague[row];
}
else {
}
}
Is it possible to somehow add these logos to each team? so when team "x" moves the logo does too.
Here is the xml data structure after being parsed:
{
Draw = 10;
"Goal_Difference" = "-17";
"Goals_Against" = 39;
"Goals_For" = 22;
Lost = 11;
NumberOfShots = 395;
Played = 25;
PlayedAtHome = 13;
PlayedAway = 12;
Points = 22;
RedCards = 5;
Team = Partick;
"Team_Id" = 561;
Won = 4;
YellowCards = 41;
}
Thanks ;)

You receive an NSDictionaryfor each team - this is an immutable object so you can't change it. But, you can create a mutable copy of it (so you will have an NSMutableDictionary).
When you do:
NSMutableArray *items = [xml objectForKey:#"TeamLeagueStanding"];
it's unlikely that you actually get a mutable array back (though you might). Again though, you can create a mutable copy if not.
Then, iterate through the teams, create a mutable copy of the dictionary, add the key/value pair that you need for the logo and then replace the original entry in the array with your updated copy.
Or, an alternate approach:
Store your logos in a different dictionary, where the keys are the team names (or unique ids) and the values are the logo image names. Then, instead of _teamLogos[row] you would use _teamLogos[[item objectForKey:#"Team"]] to get the image name.

Related

YapDatabase sorting confounds editing update

In a UITableViewController I use YapDatabase with Mantle sorting the following way:
YapDatabaseViewSorting *viewSorting = [YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(NSString *group, NSString *collection1, NSString *key1, XQBuilding *object1, NSString *collection2, NSString *key2, XQBuilding *object2) {
if ([group isEqualToString:XQBuildingsViewGroupName]) {
return [[object1 name] compare:[object2 name] options:NSNumericSearch];
}
if ([group isEqualToString:XQPicturesGroup]) {
return [[object1 updatedAt] compare:[object2 updatedAt]];
}
return NSOrderedSame;
}];
YapDatabaseViewOptions *options = [[YapDatabaseViewOptions alloc] init];
options.isPersistent = NO;
YapDatabaseView *databaseView = [[YapDatabaseView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:#"" options:options];
Although the used option I have sometimes (when an edited name changes the edited item order in the list) incorrect indexPath on reading:
- (UITableViewCell*)editableTableView:(UITableView *)tableView simpleCellForIndexPath:(NSIndexPath *)indexPath
{
__block XQBuilding *building;
__block NSNumber *gla;
[self.readConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
building = [[transaction ext:XQBuildingListYapName] objectAtIndexPath:indexPath withMappings:self.tableViewAnimator.viewMappings];
gla = [building glaWithTransaction:transaction];
}];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"BuildingCell"];
cell.textLabel.text = building.name;
cell.detailTextLabel.text = [NSString stringWithFormat: #"%# sqm", formatDecimal(gla)];
return cell;
}
i. e. building in such case is a different one than was edited. How to get a correct indexPath according to actual sorting?
There are plenty of things that should be correctly coded to make it work properly!
As I can see from your code you use YapDatabaseViewMappings. Do you use LongLivedReadTransactions and do you subscribe to YapDatabaseModifiedNotification to properly modify the table on database changes?
There is an simplified example that shows how to use all these things to update your UITableView in real time right after database is updated.

NSDictionary constant looping

SO I have a program which calls the FlickR API, gets the URL's puts them into a dictionary and then assigns them into a table view, using an image view.
NSArray *photos = [self.flickr photosForUser:#"James Kinvig"];
int countAttempts = 0;
[[self.flickr photosForUser:#"James Kinvig"]count];
for (int i = 0; i < [[self.flickr photosForUser:#"James Kinvig"]count]; i++) {
for(NSDictionary *dictionary in photos) {
countAttempts++;
NSString *farmId = [dictionary objectForKey:#"farm"];
NSString *serverId = [dictionary objectForKey:#"server"];
NSString *photoId = [dictionary objectForKey:#"id"];
NSString *secret = [dictionary objectForKey:#"secret"];
self.url= [NSURL URLWithString:[NSString stringWithFormat:#"http://farm%#.staticflickr.com/%#/%#_%#.jpg", farmId, serverId, photoId, secret]];
//NSLog(#"self.url = %#", self.url);
NSLog(#"count = %d", countAttempts);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:self.url];
dispatch_sync(dispatch_get_main_queue(), ^{
UIImage *img = [UIImage imageWithData:imgData];
cell.imageView.image = img;
[cell setNeedsLayout];
});
});
}
}
return cell;
}
This is the method it calls, photosForUser:
- (NSMutableArray *) photosForUser: (NSString *) friendUserName
{
NSString *request = [NSString stringWithFormat: #"https://api.flickr.com/services/rest/?method=flickr.people.findByUsername&username=%#", friendUserName];
NSDictionary *result = [self fetch: request];
NSString *nsid = [result valueForKeyPath: #"user.nsid"];
request = [NSString stringWithFormat: #"https://api.flickr.com/services/rest/?method=flickr.photos.search&per_page=%ld&has_geo=1&user_id=%#&extras=original_format,tags,description,geo,date_upload,owner_name,place_url", (long) self.maximumResults, nsid];
result = [self fetch: request];
return [result valueForKeyPath: #"photos.photo"];
}
Which does a fetch to the flickr API.
What is happening though is that is stuck in an eternal loop. Even with the for statement being less than the count, it still eternal loops. I have NSLog'd the count of the FlickR photos and it = 11.
This may have something to do with it, but whenever I press the button to take me to the table view controller, I get a HUGE lag, close to a minute, and nothing is being calculated (photo-wise) as I've done a count++
Thanks
let me understand this.. By the last line of your first block of code, I conclude that that is the uitableview dataSource method, cellForRowAtIndexPath.. what doesn't really makes sense.. you you have a fetch there, you have A loop inside a loop, that is setting many images (by download them) in one single imageView, and this is happening for all your visible cells at the same time. This will never work!
The solution is:
1 - remove this method from the cellForRow, this is not the place to request the images
2 - create another method that will fetch the content
3 - create a method that will do your loops and store the images on the array so you don't need to do that many times, only one..
4 - reload the tableview after you finish the step 3
5 - use the array of images that is already done to set your images by indexPath.row in your cell..
6 - I recommend you to use a Library for imageCache (i.e https://github.com/rs/SDWebImage)
NSArray *photos = [self.flickr photosForUser:#"James Kinvig"];
for (int i = 0; i < [[self.flickr photosForUser:#"James Kinvig"] count]; i++)
{
for (NSDictionary *dictionary in photos)
{
You have two nested loops iterating over the same collection. This turns what should be an O(n) operation into O(n^2) and explains why your process is taking a very long time.
Since the loop bodies never use i, I would fix it by getting rid of the outer loop:
NSArray *photos = [self.flickr photosForUser:#"James Kinvig"];
for (NSDictionary *dictionary in photos)
{

Adding a logo to a data structure

I have a tableviewcell that has an UIImage for a team logo, I'm getting my data, as team name, points, etc, from a third party XML feed, which does not have the logo data included.
So I need to add this info to that data.
The problem is that i do not know how to do that.
I have my parseXML method which works, everything is working, but my uiimage for logo is being populated by a hard coded array that has all the .png files, the only thing is that if team in number1 place drops to number2, the logos won't update.
Please see my code below, I have included my logos array and the xml data structure after being parsed.
Can anyone provide a sample code of how I can achieve a solution to my problem? I've been stuck in here for a while.
-(void) parseXML{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"apikeygoeshere"]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *xmlString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSDictionary *xml = [NSDictionary dictionaryWithXMLString:xmlString];
NSMutableArray *items = [xml objectForKey:#"TeamLeagueStanding"];
NSString *nullentry = #""; // custom code for specific reason
NSString *nullentry2 = #""; // custom code for a specific reason
[items insertObject:nullentry atIndex:0]; // custom code for a specific reason
[items insertObject:nullentry2 atIndex:1]; // custom code for a specific reason
[self setTableData:items];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"StandingsIdent";
StandingsViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *item = [tableData objectAtIndex:[indexPath row]];
if ([item isKindOfClass:[NSDictionary class]]) {
long row = [indexPath row];
cell.cellTeamName.text = [item objectForKey:#"Team"];;
cell.cellTeamLogo.image = [UIImage imageNamed:_teamLogos[row]];
cell.cellTeamPosition.text = _teamPosition[row];
cell.cellPlayed.text = [item objectForKey:#"Played"];
cell.cellWins.text = [item objectForKey:#"Won"];
cell.cellTies.text = [item objectForKey:#"Draw"];
cell.cellLoses.text = [item objectForKey:#"Lost"]; ;
cell.cellPoints.text = [item objectForKey:#"Points"];
cell.cellInfo.text = _infoLeague[row];
}
else {
}
}
LOGO ARRAY
_teamLogos = #[#"",
#"",
#"1478.png",
#"1487.png",
#"1489.png",
#"1494.png",
#"1474.png",
#"2390.png",
#"2433.png",
#"1488.png",
#"1481.png",
#"2383.png",
#"1476.png",
#"1495.png",
#"729500.png",
#"2386.png",
#"2445.png",
#"2393.png",
#""];
XML DATA STRUCTURE AFTER BEING PARSED
Draw = 10;
"Goal_Difference" = "-17";
"Goals_Against" = 39;
"Goals_For" = 22;
Lost = 11;
NumberOfShots = 395;
Played = 25;
PlayedAtHome = 13;
PlayedAway = 12;
Points = 22;
RedCards = 5;
Team = Partick;
"Team_Id" = 561;
Won = 4;
YellowCards = 41;
}
Instead of putting your hard coded image names in an array, simply name the images the same as the team name or better yet the team id.
Then you can do:
cell.cellTeamLogo.image = [UIImage imageNamed:item[#"Team_Id"]];
This keeps things nice and simple. If you add a new team you simply add a new image with the same team id (with the .png extension added).

NSMutableArray only has copies of the last object

I am using NSXML to parse out an XML document and add the results to an array of objects. The array has the correct number of objects, but they are full of data from the last object.(i.e. the object at index 0 has the same data as at index 3). I am getting good data back from my server.
//set up my objects and arrays higher in my structure
SignatureResult *currentSignatureResult = [[SignatureResult alloc]init];
Document *currentDoc = [[Document alloc]init];
Role *currentRole = [[Role alloc]init];
NSMutableArray *roleArray = [[NSMutableArray alloc] init];
NSMutableArray *doclistArray2 = [[NSMutableArray alloc] init];
.....there is more parsing up here
//role is defined as an NSXML Element
for (role in [roleList childrenNamed:#"role"]){
NSString *firstName =[role valueWithPath:#"firstName"];
NSString *lastName = [role valueWithPath:#"lastName"];
currentRole.name = [NSString stringWithFormat:#"%# %#",firstName, lastName];
for (documentList2 in [role childrenNamed:#"documentList"])
{
SMXMLElement *document = [documentList2 childNamed:#"document"];
currentDoc.name = [document attributeNamed:#"name"];
[doclistArray2 addObject:currentDoc];
}
currentRole.documentList = doclistArray2;
[roleArray addObject:currentRole];
///I've logged currentRole.name here and it shows the right information
}//end of second for statemnt
currentSignatureResult.roleList = roleArray;
}
///when I log my array here, it has the correct number of objects, but each is full of
///data from the last object I parsed
The cause is that the addObjects: retains for your currentRole object and not creates a copy from that. You can create your new currentRole object inside of the for or you can create a copy from that and add it to the array.
I recommend the following:
for (role in [roleList childrenNamed:#"role"]){
Role *currentRole = [[Role alloc] init];
NSString *firstName =[role valueWithPath:#"firstName"];
NSString *lastName = [role valueWithPath:#"lastName"];
currentRole.name = [NSString stringWithFormat:#"%# %#",firstName, lastName];
for (documentList2 in [role childrenNamed:#"documentList"])
{
SMXMLElement *document = [documentList2 childNamed:#"document"];
currentDoc.name = [document attributeNamed:#"name"];
[doclistArray2 addObject:currentDoc];
}
currentRole.documentList = doclistArray2;
[roleArray addObject:currentRole];
///I've logged currentRole.name here and it shows the right information
[currentRole release];
}//end of second for statemnt

tableView crashes when adding the first item (and also when deleting last item)

After cleaning up my code and making it ready for deployment I encountered a weird problem. When I try to add a contact to my tableview it always crashes when the empty is array. After that its not problem to add as many as you want. And also when I delete a contact and the array is empty, it also crashes with the same error message:
Terminating app due to uncaught exception 'NSRangeException', reason:
' -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
Here is what I do: I add a contact via php to my database, read it out with xml and give it back to the app so the user can instantly see what happened (adding a contact that is). There is a lot of code involved so I stick to the basics for now.
When the view loads the array gets allocated. User can add a contact. While adding the contact a php file is called that creates the xml file where the contacts are saved.
Here is where I get the object back and paste it into my array and display it
self.filteredListContent = [NSMutableArray arrayWithCapacity:[self.tabelle count]];
self.tableView.scrollEnabled = YES;
numbers = [[NSMutableArray alloc] init];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *fileName = [prefs stringForKey:#"number"];
NSString *cc = [prefs stringForKey:#"Code"];
NSString *urlString = [NSString stringWithFormat: #"http://myserver/%#%#.xml", cc, fileName];
NSURL *url = [NSURL URLWithString: urlString];
DataFileToObjectParser *myParser = [[DataFileToObjectParser alloc] parseXMLAtUrl:url toObject:#"contacts" parseError:nil];
for(int i = 0; i < [[myParser items] count]; i++) {
contacts *new = [[Telefonbuch alloc] init];
new = (contacts *) [[myParser items] objectAtIndex:i];
[numbers addObject:new];
[self.tableView reloadData];
And here is how it gets displayed, nothing special:
NSString *TableText = [[NSString alloc] initWithFormat:#"%#", [[numbers objectAtIndex:indexPath.row] fname]];
NSString *TableText2 = [[NSString alloc] initWithFormat:#"%#", [[numbers objectAtIndex:indexPath.row] lname]];
NSString *cellValue = [NSString stringWithFormat:#"%# %#", TableText, TableText2];
cell.textLabel.text = cellValue;
Your loop sounds strange.
for(int i = 0; i < [[myParser items] count]; i++) {
contacts *new = [[Telefonbuch alloc] init];
new = (contacts *) [[myParser items] objectAtIndex:i];
[numbers addObject:new];
[self.tableView reloadData];
First avoid to use new keyword (as user1118321 already suggested).
Then, what is contacts? Is contacts a superclass of type Telefonbuch?
What do these two lines mean?
contacts *new = [[Telefonbuch alloc] init];
new = (contacts *) [[myParser items] objectAtIndex:i];
You alloc-init an instance of Telefonbuch class to a new (avoid this) variable but then you assign that variable to another object. Why?
The right code could be like the following.
for(int i = 0; i < [[myParser items] count]; i++) {
Telefonbuch* newContact = (Telefonbuch*) [[myParser items] objectAtIndex:i]; // or contacts* newContact = (contacts*) [[myParser items] objectAtIndex:i];
// maybe it could be better to rename contacts with a capital letter
[numbers addObject:newContact];
}
[self.tableView reloadData];
Some notes
If you want to add or delete item to a table view, you need to do it in 2 stages:
1) Deal with your model
[yourModel removeObjectAtIndex:row];
2) Deal with your table’s animation
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
If you need to download content, maybe you could consider to do it asynchrously without blocking the main thread. In particular, this is could be the synchronous call that could be a blocking one (since I don't have any details I'm only supposing it).
DataFileToObjectParser *myParser = [[DataFileToObjectParser alloc] parseXMLAtUrl:url toObject:#"contacts" parseError:nil];
If you don't use ARC, pay attention to memory management.
Hope it helps.

Resources