Nach etwas Themen Abstinenz gibt es wieder über Fortschritte bezogen auf meine erste iPhone App zu berichten. Vorweg sei gesagt, XML Dateien auf dem iPhone zu parsen is a pain in the ass! Und ein TableView Controller in einem TabBarController ebenfalls, wenn man ein Brett vor dem Kopf hat und übersieht, dass man im InterfaceBuilder vergessen hat, die Klasse der TableView richtig zu setzen! Kostet beides dann schnell mal 2 komplette Abende Freizeit-Opferung. Aber ohne Hobel keine Späne (oder so ähnlich ;)) und so bin ich nun froh, endlich die Liste der POI’s aus einer XML Datei parsen zu können und sie auch auf dem Tab für die Übersicht der Stadtführungs-Stationen darstellen zu können; natürlich mit kleinem Bildchen und Sub-Title präsentiert. Allerdings muss ich nochmal los ziehen und für jeden Streckenpunkt ein vernünftiges Bild machen.

WPOIs_XML_Screenshotie zuletzt angekündigt, habe ich mit der App unter Xcode4.2 (beta) komplett neu angefangen. Ich denke nun erst recht, dass das die richtige Entscheidung war. Die angedachten Features für das erste Release (Mehrsprachigkeit und iPad Unterstützung) sind nach anfänglichen Schwierigkeiten ebenfalls im Griff. In Büchern und Videos sieht es halt immer sehr klar und einfach aus, aber in der Kombination mit verschiedenen Ansprüchen und in konkreten Details offenbaren sich dann doch oft Hürden die zu meistern sind.
Folgend möchte ich kurz noch die einfachste Möglichkeit darlegen, wie man eine XML Datei parsen kann. Ja, ich weiß, einen separaten Delegate für den XML Parser und das ganze verpackt in einer eigenen Klasse wäre schöner und objektorientierter gewesen, überstieg aber leider meinen derzeitigen Wissensstand und mehr als einen ganzen Abend war ich einfach nicht bereit für diesen Baustein an Zeit zu opfern. Wie man mit mehreren Delegates arbeitet, werde ich mir bei Gelegenheit dann nochmal ansehen.

Im AppDelegate Header File:

// Variablen setzen
NSMutableArray *pois;                   // POI array
PointOfInterest *aPOI;                  // one POI for XML parsing
NSMutableString *currentElementValue;   // string for XML parsing

Im AppDelegate Implementation File:

// XML parsen
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:@"POIs"])
{
if (!pois)
{
// Initialize the POIs array
pois = [[NSMutableArray alloc] init];
NSLog(@"XML File POIs found");
}
}
else if([elementName isEqualToString:@"POI"])
{
if (!aPOI)
{
// Initialize the POI
aPOI = [[PointOfInterest alloc] init];
}
// Extract the ID attribute here
aPOI.poiID = [[attributeDict objectForKey:@"id"] integerValue];
NSLog(@"XML Reading POI: %i", aPOI.poiID);
}
}

– (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if(!currentElementValue)
{
// Initialize String for collecting Data
currentElementValue = [[NSMutableString alloc] initWithString:string];
}
else
{
// Collect Data
[currentElementValue appendString:string];
}
}

– (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:@“POIs“])
{
// There is nothing to do if we encounter the end of POIs XML File
return;
}
// If we encounter the end of POI element, we add the POI to the array
if([elementName isEqualToString:@“POI“])
{
[pois addObject:aPOI];
aPOI = nil;
}
else
{
// eleminating useless whitespace
NSString *trimmedString = [currentElementValue
stringByTrimmingCharactersInSet:[NSCharacterSet
whitespaceAndNewlineCharacterSet]];
[aPOI setValue:trimmedString forKey:elementName];
trimmedString = nil;
}
currentElementValue = nil;
}

Für das TabelView dann in der „cellForRowAtIndexPath“ Methode folgende Hilfsmethode um auf das Array aus dem App Delegate zugreifen zu können:

// get Pointer to the POI Array out of the AppDelegate
- (NSMutableArray *)thePOIs
{
NSMutableArray *thePOIs = nil;
id appDelegate = [UIApplication sharedApplication].delegate;
if ([appDelegate respondsToSelector:@selector(pois)])
{
thePOIs = [appDelegate pois];
}
return thePOIs;
}

Dann kann man zum Beispiel hiermit die Dateien abgreifen:

cell.textLabel.text = [[self.thePOIs objectAtIndex:indexPath.row] title];

Wer sich jetzt wundert, warum ich soviele Informationen preisgebe, dem sei folgendes gesagt: Meine App ist im Prinzip nur eine Hülle, also ein Grundgerüst für die eigentliche Stadtführung und vermutlich von einem erfahrenen Programmierer in einer Woche programmierbar. Die eigentliche Arbeit und das KnowHow, um das es geht, steckt in der Stadtführung selbst. Also den Texten und Tonaufnahmen die später enthalten sein werden. Ausserdem ist es mir nur durch die Offenheit des Netzes und die vielen kleinen Hilfen in Foren, Blogs und Webseiten mit Tutorials überhaupt möglich, so einer App zu erstellen. Daher gebe ich gerne Wissen weiter an andere Anfänger die vor ähnlichen Fragestellungen stehen. Das meiste hier steht sowieso in der Apple Doku oder lässt sich woanders nachschlagen – von daher: Nothing special!