NSXMLParser can't parse special characters (accents) - xcode

I'm using NSXMLParser to parse an xml from a url (my code is almost exactly the same as here)
Some of the elements contain special characters like "á" which causes a word lik ándre to split into two (á and ndre).
Here is my loadXMLByURL
-(id) loadXMLByURL:(NSString *)urlString{
tickets = [[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;}
I'm pretty sure it's because the encoding is not set (I think it needs to be NSUTF8StringEncoding) but I'm not sure where/how to apply it.
[UPDATE]
Rest of my code...
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementname isEqualToString:#"ticket"])
{
currentTicket = [Ticket alloc];
}
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"name"])
{
currentTicket.name = currentNodeContent;
}
else if ([elementname isEqualToString:#"title"])
{
currentTicket.title = currentNodeContent;
}
else if ([elementname isEqualToString:#"status"])
{
currentTicket.status = currentNodeContent;
}
else if ([elementname isEqualToString:#"ticket"])
{
[tickets addObject:currentTicket];
[currentTicket release];
currentTicket = nil;
[currentNodeContent release];
currentNodeContent = nil;
}
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
[UPDATE 2]
Sample xml...
<RB>
<list>
<ticket>
<name>Andrew Ford</name>
<title>3rd release</title>
<status>1</status>
</ticket>
<ticket>
<name>David Jenkins</name>
<title>3rd release</title>
<status>0</status>
</ticket>
<ticket>
<name>Luis gomez ándre</name>
<title>3rd release</title>
<status>1</status>
</ticket>
</list>
</RB>

I would load the url to an NSString and then convert like this.
-(id) loadXMLByURL:(NSString *)urlString{
tickets = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:urlString];
NSError *error;
NSString * dataString = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
return self;
}
EDIT:
Part of the problem may be that your parser:foundCharacters: method is assigning to your currentNodeContent instead of appending. See the Apple Doc at the following link.
http://developer.apple.com/library/ios/#documentation/cocoa/reference/NSXMLParserDelegate_Protocol/Reference/Reference.html
From the doc:
Because string may be only part of the total character content for the current element, you should append it to the current accumulation of characters until the element changes.

Found the problem! It is indeed in found characters. You should change your code to this:
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
NSLog(#"found characters: %#", string);
if (!currentNodeContent) {
currentNodeContent = [[NSMutableString alloc] init];
}
[currentNodeContent appendString:string];
}
I was having the same problem before, and the above code has fixed it.

Use
NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
and to get the string from it, do this:
NSString *theXML = [[NSString alloc] initWithBytes:[data mutableBytes]
length:[data length]
encoding:NSUTF8StringEncoding];
Then you can parse the xml in your NSXMLParserDelegate methods.
Hope this helps.

Related

Stop Activity Indicator while no row in Xcode

I am using the code below to retrieve data from Url via PHP file, the tableview controller will start the Activity Indicator. What I am trying to do is to stop the indicator As soon as there are no data has been loaded to the tableview.
Her is the code;
NSString *urlString = [NSString stringWithFormat:#"http:/MyWebSite/ChoseMyLike.php?userName=%#", myString];
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!data) {
NSLog(#"connection error: %#", error);
return;
}
NSError *parseError;
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&parseError];
if (!json) {
NSLog(#"JSON Parsing error: %#", parseError);
NSLog(#"data = %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
return;
}
NSMutableArray *results = [[NSMutableArray alloc]init];
for (int i = 0; i < json.count; i++) {
NSString *cQasidaName = json[i][#"qasidaName"];
NSString *cQasidaShaerName = json[i][#"qasidaShaerName"];
NSString *cQasidaBody = json[i][#"qasidaBody"];
NSString *cQasidaDate = json[i][#"myDate"];
NSString *cQasidaTime = json[i][#"myTime"];
NSString *cQasidaRate = json[i][#"myRate"];
NSString *cQasidaId = json[i][#"qasidaId"];
NSString *cQasidaUserName = json[i][#"userName"];
NSString *cTheUserId = json[i][#"theUserId"];
NSString *cTheUserNameArabic = json[i][#"userNameArabic"];
[results addObject:[[ListOfObjects alloc] initWithQasidaName: (NSString *) cQasidaName andQasidaShaerName: (NSString *) cQasidaShaerName andQasidaBody: (NSString *) cQasidaBody andQasidaDate: (NSString *) cQasidaDate andQasidaTime: (NSString *) cQasidaTime andQasidaRate: (NSString *)cQasidaRate andQasidaId:cQasidaId andQasidaUserName:cQasidaUserName andTheUserId:cTheUserId andTheUserNameArabic:cTheUserNameArabic]];
}
dispatch_async(dispatch_get_main_queue(), ^{
self.listArray = results;
[self.tableView reloadData];
[spinner stopAnimating];
});
}];
[task resume];
}
At the moment the indicator is not stopping. What do I need to do?
Thanks
Simply call [spinner stopAnimating]; for all return paths:
if (!data) {
NSLog(#"connection error: %#", error);
[spinner stopAnimating];
return;
}
and
if (!json) {
NSLog(#"JSON Parsing error: %#", parseError);
NSLog(#"data = %#", [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]);
[spinner stopAnimating];
return;
}
Note: it's much easier to have a single return statement and then you can always call [spinner stopAnimating]; at the end of the method... consider restructuring your code.

XCode 6.1 iOS 8.1 crashing- NSXMLParser does not support reentrant parsing

I met a problem with NSXMLParser. My source is run well in iOS7/XCode 5, but crashing in iOS8.1/XCode 6. Crashing error is:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSXMLParser does not support reentrant parsing.'
I tried with other solution in this post, but error still happened. Anyone can help me more?
My source like this
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate, NSXMLParserDelegate>
{
NSXMLParser *xmlParser_;
...
}
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self parseData];
....
}
- (void)parseData
{
titleList_ = [[NSMutableArray alloc] init];
NSString *filePath = [[NSBundle mainBundle] pathForResource:fDetail ofType:fXML];
if (filePath)
{
NSString *myText = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
if (myText)
{
countPage_ = 2;
NSData *xmlData = [myText dataUsingEncoding:NSUTF16StringEncoding];//NSUTF8StringEncoding];
xmlParser_ = [[NSXMLParser alloc] initWithData:xmlData];
xmlParser_.delegate = self;
[xmlParser_ parse];
}
}
}
The same code works fine for me in iOS 8/XCode 6.
Here is my code:
NSString *elementname;
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self parseData];
return YES;
}
- (void)parseData
{
titleList_ = [[NSMutableArray alloc] init];
NSString *fDetail = [NSString stringWithFormat:#"sample"];
NSString *filePath = [[NSBundle mainBundle] pathForResource:fDetail ofType:#"xml"];
if (filePath)
{
NSString *myText = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
if (myText)
{
countPage_ = 2;
NSData *xmlData = [myText dataUsingEncoding:NSUTF16StringEncoding];//NSUTF8StringEncoding];
xmlParser_ = [[NSXMLParser alloc] initWithData:xmlData];
xmlParser_.delegate = self;
[xmlParser_ parse];
}
}
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
elementname = elementName;
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
elementname = elementName;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if([elementname isEqualToString:#"CatalogId"])
{
int64_t cId = [string longLongValue];
NSLog(#"%lld",cId);
}
}
#end

Add transition effect in a slideshow

I'm trying to add a transition effect to my slideshow: here's my code
- (void)viewDidLoad
{
[super viewDidLoad];
photos = [[NSMutableArray alloc] init];
NSXMLParser *photoParser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL
URLWithString:#"http://localhost/index.xml"]];
[photoParser setDelegate:(id)self];
[photoParser parse];
currentImage = 0;
NSURL *imageURL = [NSURL URLWithString:[photos objectAtIndex:0]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
[imgView setImage:[UIImage imageWithData:imageData]];
timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
target: self
selector: #selector(handleTimer:)
userInfo: nil
repeats: YES];
}
- (void) handleTimer: (NSTimer *) timer {
currentImage++;
if ( currentImage >= photos.count )
currentImage = 0;
NSURL *imageURL = [NSURL URLWithString:[photos objectAtIndex:currentImage]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
[imgView setImage:[UIImage imageWithData:imageData]];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
if ( [elementName isEqualToString:#"photo"]) {
[photos addObject:[attributeDict objectForKey:#"url"]];
}
}
I think I should use
[UIView transitionFromView:currentView toView:nextView duration:1.0
options:UIViewAnimationOptionTransitionCrossDissolve completion:nil];
but I don't how to fill the transitionFRomView: and toView: fields. I only have one view
Thanks for your help
You can use the method transitionWithView:duration:options:animations:completion: for which the documentation says:
You can use this block to add, remove, show, or hide subviews of the specified view.
This is an example of how you can use that method:
- (void) handleTimer: (NSTimer *) timer {
currentImage++;
if (currentImage >= photos.count) currentImage = 0;
NSURL *imageURL = [NSURL URLWithString:[photos objectAtIndex:currentImage]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL]
[UIView transitionWithView:containerView
duration:2
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[imgView removeFromSuperview];
imgView = // create a new image view
[imgView setImage:[UIImage imageWithData:imageData]];
[containerView addSubview:toView];
} completion:NULL];
}
Where containerView is the parent view of imgView and most likely is self.view.

Memory Leak Using NSXMLParser in NSConcreteMapTable

I'm using NSXMLParser and I get a memory leak that points to NSConcreteMapTable, whatever that is:
The leak occurs at this line of code when calling the parser from my AppDelegate.m:
I have searched for a solution and can't see what I'm doing wrong.
Here is my code.
Any help is greatly appreciated.
lq
// * * * XMLParser.h * * *
#import <Foundation/Foundation.h>
#protocol NSXMLParserDelegate;
#interface XMLParser : NSObject
<NSXMLParserDelegate>
{
NSMutableArray *xmlArray;
BOOL storingCharacters;
float xmlDataVersion;
}
#property (nonatomic, retain) NSMutableArray *xmlArray;
#property (nonatomic) BOOL storingCharacters;
#property (nonatomic, assign) float xmlDataVersion;
-(BOOL)parseXMLFileAtURL:(NSURL *)URL parseError:(NSError **)error;
#end
// * * * XMLParser.m * * *
#import "XMLParser.h"
#implementation XMLParser
#synthesize xmlArray;
#synthesize storingCharacters;
#synthesize xmlDataVersion;
- (BOOL)parseXMLFileAtURL:(NSURL *)URL parseError:(NSError **)error {
BOOL result = YES;
if (xmlArray == nil) {
// this array holds row data extracted from the XML parser didStartElement method
xmlArray = [[NSMutableArray alloc] init];
}
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];
if (parser != nil) {
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[[parser setShouldResolveExternalEntities:NO];
}
[parser parse];
if (parseError && error) {
*error = parseError;
result = NO;
}
[parser release];
return result;
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if (qName) {
elementName = qName;
}
// Check the data version of the XML Data against my stored value
if ([elementName isEqualToString:#"data"]) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
self.xmlDataVersion = [[attributeDict objectForKey:#"version"] floatValue];
float storedDataVersion = [userDefaults floatForKey:kDataVersion];
if (self.xmlDataVersion <= storedDataVersion) {
// - - - - -> Abort parsing if the same or earlier data versions
[parser abortParsing];
}
}
if ([elementName isEqualToString:#"FirstColumnName"]) {
storingCharacters = YES;
} else if ([elementName isEqualToString:#"SecondColumnName"]) {
storingCharacters = YES;
// ... total of 16 elements
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (storingCharacters) {
[self.xmlArray addObject:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if (qName) {
elementName = qName;
}
// - - - - -> If at the end of a data row, save changes to object model
if ([elementName isEqualToString:#"ROW"]) {
// - - - - -> Make sure the data has the required number of elements before taking any action
if ([self.xmlArray count] == 16) {
// … //Store or Update Data in SQLite store depending on data values
}
[self.xmlArray removeAllObjects];
}
storingCharacters = NO;
}
-(void)dealloc {
[xmlArray release];
[super dealloc];
}
// * * * AppDelegate.m * * *
#import "XMLParser.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSURL *xmlURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"FileName" ofType:#"xml"]];
NSError *parseError = nil;
XMLParser *xmlParse = [[XMLParser alloc] init];
[xmlParse parseXMLFileAtURL:xmlURL parseError:&parseError];
[xmlParse release];
. . .
}
I found a solution in another SO post:
Use:
NSData * dataXml = [[NSData alloc] initWithContentsOfURL:URL];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:dataXml];
[dataXml release];
Instead of:
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:URL];
The leak goes away.
This is probably a leak in Apple code, since Foundation is reported as "Responsible Library". There's probably nothing you can do, but to report the bug to apple.

NSURLConnection - Is it possible to wait/block on a request?

I need to wait for a response from a SOAP webservice, I am calling via a NSURLConnection as I need to manipulate the data being returned and then return it from my class to the calling class..
Here is my code:
#import <Foundation/Foundation.h>
#interface UsersBLL : NSObject {
NSMutableData *webData;
NSMutableString *soapResults;
NSXMLParser *xmlParser;
BOOL *recordResults;
NSNumber *EmailCount;
}
#property(nonatomic, retain) NSMutableData *webData;
#property(nonatomic, retain) NSMutableString *soapResults;
#property(nonatomic, retain) NSXMLParser *xmlParser;
-(int)checkEmailAddress:(NSString*)emailAddress;
#end
#import "UsersBLL.h"
#implementation UsersBLL
#synthesize webData;
#synthesize soapResults;
#synthesize xmlParser;
-(id)init {
self = [super init];
return self;
}
-(int)checkEmailAddress:(NSString*)emailAddress {
// Build the SOAP envelope
NSString *soapMessage = [NSString stringWithFormat:
#"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
"<soap:Body>\n"
"<CheckEmailAddress xmlns=\"http://tempuri.org/\">\n"
"<EmailAddress>%#</EmailAddress>\n"
"</CheckEmailAddress>\n"
"</soap:Body>\n"
"</soap:Envelope>\n", emailAddress];
NSLog(soapMessage);
NSURL *url = [NSURL URLWithString:#"http://photoswapper.mick-walker.co.uk/UsersService.asmx?op=CheckEmailAddress"];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength = [NSString stringWithFormat:#"%d", [soapMessage length]];
[theRequest addValue: #"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[theRequest addValue: #"http://tempuri.org/CheckEmailAddress" forHTTPHeaderField:#"SOAPAction"];
[theRequest addValue: msgLength forHTTPHeaderField:#"Content-Length"];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if( theConnection )
{
webData = [[NSMutableData data] retain];
}
else
{
NSLog(#"theConnection is NULL");
}
NSLog(#"%#", EmailCount);
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength: 0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[webData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"ERROR with theConenction");
[connection release];
[webData release];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"DONE. Received Bytes: %d", [webData length]);
NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
NSLog(theXML);
[theXML release];
if( xmlParser )
{
[xmlParser release];
}
xmlParser = [[NSXMLParser alloc] initWithData: webData];
[xmlParser setDelegate: self];
[xmlParser setShouldResolveExternalEntities: YES];
[xmlParser parse];
[connection release];
[webData release];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *)qName
attributes: (NSDictionary *)attributeDict
{
if( [elementName isEqualToString:#"CheckEmailAddressResult"])
{
if(!soapResults)
{
soapResults = [[NSMutableString alloc] init];
}
recordResults = TRUE;
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if( recordResults )
{
[soapResults appendString: string];
}
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if( [elementName isEqualToString:#"CheckEmailAddressResult"])
{
recordResults = FALSE;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc]init];
EmailCount = [formatter numberFromString:soapResults];
[formatter release];
[soapResults release];
soapResults = nil;
}
}
#end
CheckEmailAddress is declared as returning an integer value (I know it returns nothing in the sample above).
What I ideally want, is through the CheckEmailAddress method, return the value retrieved from the web service. However as the call NSURLConnection does not wait until the request has completed, I cannot do it.
I would be grateful if anyone could give me any potential ideas for workarounds.
The simplest solution would be using [NSURLConnection sendSynchronousRequest:returningResponse:error:].
It does not allow as much control as the approach you've taken, but is usually enough for most applications.
I have just posted a solution which wraps an asynchronous NSURLConnection to be able to block the calling thread. In case you need more control than the standard [NSURLConnection sendSynchronousRequest:returningResponse:error:] you can check out this link on StackOverflow:
NSURLConnection blocking wrapper implemented with semaphores
You have two choices:
Use +[NSURLConnection sendSynchronousRequest:returningResponse:error:]
Schedule the connection in a custom runloop mode, and run the loop in that mode until the data arrives or you have need to cancel the connection
It all depends on the level of asynchronism you need:
If it's OK to stay blocked during the whole request you may want to use
+[NSURLConnection sendSynchronousRequest:returningResponse:error:]
But, as suggested by Wade, be careful to add a timeout to your NSURLRequest, otherwise the connection might blocks and your application will hang.
If not, you can simply use the NSNotificationCenter. But you must be careful with race conditions over your data, specially if you are handling multiple requests

Resources