Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

bmasar

macrumors newbie
Original poster
Apr 8, 2013
12
0
In essence, this is a sports scoreboard app for iOS. I need to allow users to select a value for "Round", then display only the cells with that value. (For reference, currently my table returns every game of the season so far, I'd like to display them one round at a time.)

I wasn't having any success with simple things like if statements, so I'm turning to you guys.

Here's my code,

XMLParser.m:

Code:
#import "XMLParser.h"

@implementation XMLParser
@synthesize matchups = _matchups;

NSMutableString *currentNodeContent;
NSXMLParser     *parser;
Soccer          *currentMatchup;
bool            isStatus;

-(id) loadXMLByURL:(NSString *)urlString
{
  _matchups = [[NSMutableArray alloc] init];
  NSURL *url = [NSURL URLWithString:urlString];
  NSData *data = [[NSData alloc] initWithContentsOfURL:url];
  parser = [[NSXMLParser alloc] initWithData:data];
  parser.delegate = self;
  [parser parse];
  return self;
}

-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
  currentNodeContent =(NSMutableString *) [string stringByTrimmingCharactersInSet:       [NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
  if ([elementName isEqualToString:@"Date"]) 
  {
      currentMatchup = [Soccer alloc];
      isStatus = YES;
  }
}

-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
  if (isStatus) {
      if ([elementName isEqualToString:@"Round"])
      {
          currentMatchup.round = currentNodeContent;
      }
      if ([elementName isEqualToString:@"HomeTeam"])
      {
          currentMatchup.homeTeam = currentNodeContent;
      }
  // etc. etc., there are a lot of elements loaded
  }

 if ([elementName isEqualToString:@"Match"])
  {
      [self.matchups addObject:currentMatchup];
      if (currentMatchup.timeElapsed == nil)
      {
          currentMatchup.timeElapsed = @"F";
      }
      currentMatchup = nil;
      currentNodeContent = nil;
  }
}

@end
 
I wasn't having any success with simple things like if statements, so I'm turning to you guys.

What do you expect to have happen? What would you like to have happen? What is happening? How do the three differ from each other (if at all?)
 
What do you expect to have happen? What would you like to have happen? What is happening? How do the three differ from each other (if at all?)

The XML parser is loading fine, but it is outputting the score of every soccer game soccer played so far this season in Scotland. I would like to display the score one round at a time. "Round" is an element (numbered 1-22) within the parent node in the XML source I'm grabbing. There are also several other elements for each game (HomeTeam, AwayTeam, HomeGoals, AwayGoals, etc.)

I was hoping I could solve the problem by inserting an if statement along these lines:
Code:
if ([elementName isEqualToString:@"Round"])
{
     currentMatchup.round = currentNodeContent;
         if ([currentMatchup.round = selectedRound])
         {
              Load the rest of the games for that round
         }
}
In the above case, selectedRound would be a string whose value would be pre-determined with a picker.

The problem with the code above was that the array wouldn't get filled at all. After some research, I discovered that I would probably need to sort my array using a more sophisticated approach. That's where I'm at right now, because I'm still learning the ropes.
 
Why not apply the filtering after you have parsed the xml. I am assuming that your xml parses out into an array of "games". Why not filter the array after it is parsed out. You could use something like

Code:
NSPredicate *pred = [NSPredicate predicateWithFormat:@"round == %@", selectedValue];
NSArray *filteredArray = [baseArray filteredArrayUsingPredicate:pred];

This is assuming your "games" class has a property called round

Note: This is just from memory but you can consult the predicate and array class documentation
NSPredicate documentation

NSArray documentationl
 
Why not apply the filtering after you have parsed the xml. I am assuming that your xml parses out into an array of "games". Why not filter the array after it is parsed out. You could use something like

Code:
NSPredicate *pred = [NSPredicate predicateWithFormat:@"round == %@", selectedValue];
NSArray *filteredArray = [baseArray filteredArrayUsingPredicate:pred];

This is assuming your "games" class has a property called round

Note: This is just from memory but you can consult the predicate and array class documentation
NSPredicate documentation

NSArray documentationl

I've been tinkering with this for a bit and I can't get it to work right. This is what I've got so far:
Code:
- (void)filterResultsByRound
{
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"round == %@",roundSelected];
    NSArray *filteredArray = [matchups filteredArrayUsingPredicate:pred];
}

but no matter what I put for my baseArray, I get an error "Use of undeclared identifier". I can't figure out why it isn't working, matchups is called within the same implementation file several times without issues. For example to load my table:

Code:
Soccer *currentMatchup = [[xmlParser matchups] objectAtIndex:indexPath.row];

and for numberOfRowsInSection:

Code:
return [[xmlParser matchups] count];

Just in case, here is the property:

Code:
@property (strong, readonly) NSMutableArray *matchups;

and it does get synthesized. Any thoughts?
 
Which line is giving you this error?

Code:
NSArray *filteredArray = [matchups filteredArrayUsingPredicate:pred];

and it's referring to matchups, which is the name of the NSMutableArray that my parser is loading my info into. Yet I'm getting that error, and I don't know why. The same array (matchups) is being called successfully two lines below it when creating my tableView.
 
You need to specify the name of the round that you want to display. That's what the roundSelected parameter is. That's the cause of the syntax error.
 
You need to specify the name of the round that you want to display. That's what the roundSelected parameter is. That's the cause of the syntax error.

roundSelected is specified and given a value elsewhere. matchups is getting the error.
 
I'm sorry, I don't follow.

Although you should probably go through the accessor methods like dejo suggested, you could also use the ivar directly, _matchups (note the underscore at the front of the name.)

You should still learn about properties, accessors (getters and setters), ivars, and the differences between all of them, though,
 
I've tried all of these options, there must be something else wrong that I need to get to the bottom of. I'm getting an empty filteredArray.
 
Did you fix your error? If so, how? Have you done any debugging? If so, what? For example, how many objects are in matchups when you call filteredArrayUsingPredicate: on it?
 
Did you fix your error? If so, how? Have you done any debugging? If so, what? For example, how many objects are in matchups when you call filteredArrayUsingPredicate: on it?

Yes, the error is gone, but I'm not sure if I really solved that problem. I declared and synthesized NSMutableArray matchups in ButtonViewController. But it had already been declared and synthesized in my XMLParser object, and the parser object is imported into ButtonViewController. So I thought it shouldn't need to be declared and synthesized again, correct?

So perhaps my NSDelegate method is reading from an empty array, which is why filteredArray isn't being filled. Or it could be something wrong with the filteredArray, or the filter criteria, or whatever else. This whole exercise is extremely frustrating because I don't know any programers personally to sit down with me and point things out.

To answer your other question, the parser loads 192 total cells, one for each individual soccer game, and each game has 13 elements loaded.

192 parent nodes, 13 elements within each node being grabbed.
 
Yes, the error is gone, but I'm not sure if I really solved that problem. I declared and synthesized NSMutableArray matchups in ButtonViewController. But it had already been declared and synthesized in my XMLParser object, and the parser object is imported into ButtonViewController. So I thought it shouldn't need to be declared and synthesized again, correct?
Correct. My apologies, I misunderstood your earlier post. I thought you already had matchups as a property in your ButtonViewController but, now that I've looked at it again, I see that you meant it is a property of your XMLParser class. In which case, redeclaring it does no good, unless you plan to assign it a value. You are referencing it using [xmlParser matchups] elsewhere; you could probably get-by doing the same thing for the filtering. However, it is probably not a great approach to be constantly referencing the property of another class instance.

Also, in the future, it might help if you provided more context for each line-of-code by including the entire method or interface they in which they reside, like you helpfully did with your first post.

So perhaps my NSDelegate method is reading from an empty array, which is why filteredArray isn't being filled. Or it could be something wrong with the filteredArray, or the filter criteria, or whatever else. This whole exercise is extremely frustrating because I don't know any programers personally to sit down with me and point things out.
Seems like you're the programmer, in this case. :)

Are you not comfortable with that role?

To answer your other question, the parser loads 192 total cells, one for each individual soccer game, and each game has 13 elements loaded.

192 parent nodes, 13 elements within each node being grabbed.
That doesn't really answer my question. I was not really asking about what XMLParser does; I was asking about verifying the contents of your matchups variable at the time you call the filteredArrayUsingPredicate: method on it. This could be done with a NSLog call, or even by using a breakpoint at that line, and using the debugger panel to examine the matchups variable/property.
 
I am comfortable being the programmer because its my pet project. I'd much rather accomplish this goal on my own, but it can be frustrating at times.

Anywho, just to be clear, matchups is the name of the NSMutableArray, currentMatchup is the name used to access each game, and round is the element to be filtered based on value.

The array is working because I output all the games successfully in the ButtonViewController, and I've specifically tested round to make sure it is holds data.
 
Last edited:
Alright, so what isn't working right now? What does your current code look like for filtering? Where have you placed it? In other words, give us an idea on where you're still stuck. Any information you can provide on debugging you've done is also helpful.
 
Last edited:
Alright, so what isn't working right now? What does your current code look like for filtering? Where have you placed it? In other words, give us an idea on where you're still stuck. Any information you can provide on debugging you've done is also helpful.

So the parser is in my first post.

Here is my ButtonViewController.m (edited for relevancy):
Code:
#import "ButtonViewController.h"
#import "ViewController.h"
#import "XMLParser.h"

@interface ButtonViewController ()

@end

@implementation ButtonViewController

XMLParser *xmlParser;
//various UILabels and Frames
int tagNumber;

NSString *roundSelected = @"3";

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    NSLog(@"About to retrieve matchups");
    xmlParser = [[XMLParser alloc] loadXMLByURL:@"http://masarvideo.com/kids/ss/GetHistoricMatchesByLeagueAndSeason.xml"];
    
    self.title = @"Matchups";
    
    [self filterResultsByRound];
    [self addMyButton];
	// Do any additional setup after loading the view, typically from a nib.
}

/*- (void)filterResultsByRound
{
    NSArray *filteredArray = [[NSArray alloc] init];
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"round == %@", roundSelected];
    filteredArray = [self.matchups filteredArrayUsingPredicate:pred];
}*/

- (void)addMyButton
{
    NSInteger buttonCount = [[xmlParser filteredArray] count];
    NSLog(@"array %lu",(unsigned long)[[xmlParser filteredArray] count]);
    int indexNumber = 0;
    
    
    CGRect buttonFrame = CGRectMake(30,5,260,60);
    
    for (int index = 0; index <buttonCount; index++) {
        Soccer *currentMatchup = [[xmlParser filteredArray] objectAtIndex:indexNumber];
        
        //UIButtons (named scoreBox) and UILabels generated and loaded here.
        
        [self.scroller addSubview:scoreBox];
        buttonFrame.origin.y+=buttonFrame.size.height+5.0f;
        indexNumber++;
    }
    
    CGSize contentSize = self.scroller.frame.size;
    contentSize.height = buttonFrame.origin.y;
    [self.scroller setContentSize:contentSize];
    
}


- (void)buttonPressed:(UIButton *)sender
{
    sender.highlighted = NO;
    sender.selected = !sender.selected;
    tagNumber = sender.tag;
    NSLog(@"tag %d",tagNumber);
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

As far as debugging, I've tried various iterations of matchups including self.matchups and _matchups.

I've tried putting the filtering code in viewDidLoad and as a separate method.

I've tried creating filteredArray as an NSArray and NSMutableArray.

Finally, I've tried to address round by trying currentMatchup.round and self.round, as well as trying different combinations of quotation marks (" & ') in the comparison statement, and removing roundSelected and using a simple string ("3").

In all events, the NSLog(@"array %lu",(unsigned long)[[xmlParser filteredArray] count]) returns 0. The log for xmlParser matchups returns 192.
 
Last edited by a moderator:
So the parser is in my first post.
...
Code:
...
- (void)addMyButton
{
    [b]NSInteger buttonCount = [[xmlParser filteredArray] count];[/b]
    NSLog(@"array %lu",(unsigned long)[[xmlParser filteredArray] count]);
    int indexNumber = 0;
    
...
The parser code in your first post doesn't have a filteredArray property or method. So the code hilited in bold is referring to something you haven't shown yet.

Please post accurate code for XMLParser, both the .m and .h files.


In all events, the NSLog(@"array %lu",(unsigned long)[[xmlParser filteredArray] count]) returns 0.
What does [xmlParser filteredArray] return? Is it an object or is it nil?

In Objective-C, sending a message to nil generally returns 0. So it may be the filteredArray is an actual object whose count is 0, or it may be nil and it's returning 0 for that reason.


In the posted code for XMLParser, you have this:
Code:
      currentMatchup = [Soccer alloc];
Nowhere do I see a call to an init method, which means it may be wrong (incorrectly or incompletely initialized). Please post accurate code for the Soccer class.
 
Last edited:
Hmm, it seems you are just trying fixes without really understanding what kind of a difference they will make. How comfortable are you with the fundamentals of Objective-C programming? For example, can you explain the difference between calling [xmlParser matchups] and using self.matchups (which is really just syntactic-sugar for [self matchups])?
 
The parser code in your first post doesn't have a filteredArray property or method. So the code hilited in bold is referring to something you haven't shown yet.

Please post accurate code for XMLParser, both the .m and .h files.

Correct, filteredArray shouldn't be called by XMLParser, the code I highlighted is incorrect on my part. Read below for more on that.


What does [xmlParser filteredArray] return? Is it an object or is it nil?

In Objective-C, sending a message to nil generally returns 0. So it may be the filteredArray is an actual object whose count is 0, or it may be nil and it's returning 0 for that reason.

Sounds about right, not the correct coding by me.

In the posted code for XMLParser, you have this:
Code:
      currentMatchup = [Soccer alloc];
Nowhere do I see a call to an init method, which means it may be wrong (incorrectly or incompletely initialized). Please post accurate code for the Soccer class.

That is a bit of a head scratcher that I had to revisit. The basis for my parser came from this tutorial and he didn't initialize either. It seems to work still, but I added it anyways.

As for the [[xmlParser filteredArray] count], that was an attempt to piggy back on [[xmlParser matchup] count] (which also stems in part from the tutorial linked above) that was correctly creating the proper number of UIButton scoreBox, though admittedly it's probably not the most efficient way.

I think this really boils down to not understanding why matchups is returning an "undeclared identifier" error in my ButtonViewController when it's declared and synthesized in XMLParser .h and .m, which is then imported into ButtonViewController.

----------

Hmm, it seems you are just trying fixes without really understanding what kind of a difference they will make. How comfortable are you with the fundamentals of Objective-C programming? For example, can you explain the difference between calling [xmlParser matchups] and using self.matchups (which is really just syntactic-sugar for [self matchups])?

True, I got to the point of frustration and tried plug and play, some of which were suggested in this thread. It's a bit like learning French from a book, then trying to write and essay in French with only spellcheck to help you.

I do recognize the difference between [xmlParser matchups] and self.matchups, but the latter was still giving me errors unless I re-declared and re-synthesized, which is not the correct thing to do, while the former was working (gleaned from here). Where I'm at in my Objective-C learning, when something works—try to replicate it.
 
It's a bit like learning French from a book, then trying to write and essay in French with only spellcheck to help you.
What resources have you been using to "learn French from a book", as it were? That is, how have you been educating yourself on the fundamentals of Objective-C / iOS programming?

I do recognize the difference between [xmlParser matchups] and self.matchups,
You do? Good. Explain it to me then. Pretend I know less about Objective-C than you.

...while the former was working...
It was working? Is it no longer working? Or are you no longer using it? If so, why did you change it?

Where I'm at in my Objective-C learning, when something works—try to replicate it.
Perhaps your next step should be to learn how to set up breakpoints, step-through, -into and -out-of code and learn to use the debugging panel to examine variables' values whilst stepping along.
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.