I have the following code,
NSString *string = "Some value";
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
self.parser = [[NSXMLParser alloc] initWithData:data];
self.parser.delegate = self;
[self.parser parse];
After I call "parse" method it immediately calls the delegate parseErrorOccurred and then calls delegate parserDidEndDocument.
I want only one method to be called.
Thanks in advance.
That delegate method is called when there is a fatal error, so there is an issue with the XML you are parsing.
The method has an NSError parameter, and that should give you a clue as to what is wrong with the XML.
You mean when an error occurs in parsing, you want only parseErrorOccurred() called and parserDidEndDocument() not called?
One possible solution: write parser.abortParsing() in parseErrorEccurred(). I'm not sure this would actually work, if it fails, try the solution below:
Use a flag in your class that wrap the parser. Set the flag to true in parseErrorEccurred(), then check this flag in parserDidEndDocument() (if it's false then execute other code).
Hope this will solve your problem. :)
Related
I am a beginner and trying hands on Json parsing. I am successfully parsing the data I need. But the problem is I don't know how to save and use that value in another function.
For now, I am doing everything in view controller.m for the sake of simplicity. Meaning all my code is in viewcontroller.m
Will later use a separate class.
I have a button and a label on my storyboard. So I am trying to fetch a value from Json .api and want to show that value in label. When user presses button, the parsing is triggered. As u can c below.
- (IBAction)btnPressed:(id)sender {
NSURL *url=[NSURL URLWithString:#"http:........myweb"];
NSURLRequest *request=[NSURLRequest requestWithURL:url];
connection=[NSURLConnection connectionWithRequest:request delegate:self];
if(connection)
{
webData=[[NSMutableData alloc]init];
self.outputLbl.text = [NSString stringWithFormat:#"%#", goldRate];
}
but the value printed in the label is null. when we fetch info and display in tableviews, we set delegates. but for a label we don't set delegate. So I am confused what the problem is? have i forgot to set a delegate or am i passing data incorrectly. pls the function below.
-(void) connectionDidFinishLoading:(NSURLConnection *)connection{
NSDictionary *allDataDictionary=[NSJSONSerialization JSONObjectWithData:webData options:0 error:nil];
NSArray *myDataArray=[allDataDictionary objectForKey:#"data"];
NSArray *myGoldArray=[myDataArray objectAtIndex:0];
goldRate=[[myGoldArray objectAtIndex:1] stringValue];
}
I have made this goldRate a global variable. But when I debug my program. In the connectionDidFinishLoading, the goldRate value is absolutely what I want. But when I try to use this goldRate in btnPressed function, in that function the value is null.
Please help.
When the button is pressed, you initiate a load. When the load completes (some time later) the connectionDidFinishLoading: method is called. But until that happens, "goldRate" will be nil (because it is not yet initialized).
Set the label in the connectionDidFinishLoading: instead of where you have it, and the value will show up.
Firstly, as I see many others seem to announce when they ask these types of questions, I'm a beginner to Objective C. I've come from a strong PHP background, so I do understand most programming concepts, and have been learning Obj C on and off for the past 12 months.
One of my first projects to get my feet wet with iOS Objective C was to integrate with the Magento SOAP API. Probably not the easiest thing to begin with, but nonetheless it's a good challenge.
I'm currently trying to integrate XMLReader (https://github.com/amarcadet/XMLReader). But XCode keeps throwing me an error:
ARC Semantic Issue: No known class method for selector 'dictionaryForNSXMLParser:'
Which refers to the following code:
NSDictionary *dict = [XMLReader dictionaryForNSXMLParser:parser];
I found some advice from another question:
How can you use AFNetworking or STHTTPRequest to make a request of a SOAP web service?
I've reverted my code to mirror exactly the examples provided in the question, so I've been wracking my brain to work this error out, but to no avail.
Any help is much appreciated, and I apologise if this is something stupid which I have overlooked. I've tried scouring google for similar issues, but they all seem to be leading to class methods being called on an instance, etc.
I was trying that code myself just a minute ago.
You have to add in XMLReader.h:
+(NSDictionary*)dictionaryForNSXMLParser:(NSXMLParser*)parser error:(NSError **)error;
Then in the XMLReader.m these two:
+ (NSDictionary *)dictionaryForNSXMLParser:(NSXMLParser *)xmlParser error:(NSError **)error
{
XMLReader *reader = [[XMLReader alloc] initWithError:error];
NSDictionary *rootDictionary = [reader objectWithNSXMLParser:xmlParser options:0];
return rootDictionary;
}
- (NSDictionary *)objectWithNSXMLParser:(NSXMLParser *)xmlParser options:(XMLReaderOptions)options
{
// Clear out any old data
self.dictionaryStack = [[NSMutableArray alloc] init];
self.textInProgress = [[NSMutableString alloc] init];
// Initialize the stack with a fresh dictionary
[self.dictionaryStack addObject:[NSMutableDictionary dictionary]];
[xmlParser setShouldProcessNamespaces:(options & XMLReaderOptionsProcessNamespaces)];
[xmlParser setShouldReportNamespacePrefixes:(options & XMLReaderOptionsReportNamespacePrefixes)];
[xmlParser setShouldResolveExternalEntities:(options & XMLReaderOptionsResolveExternalEntities)];
xmlParser.delegate = self;
BOOL success = [xmlParser parse];
// Return the stack's root dictionary on success
if (success)
{
NSDictionary *resultDict = [self.dictionaryStack objectAtIndex:0];
return resultDict;
}
return nil;
}
After that remember to import XMLReader.h and use the method. Notice that it has the error handling in it so if you copy pasted the code it's not same.
[XMLReader dictionaryForNSXMLParser:parser error:nil];
If it still doesn't work try cleaning the project with CMD+SHIFT+K and building it again.
I've been stuck on this for approximately two weeks. I hate posting things that have been asked a lot but I really have gone through them all.
I used Ray Wenderlich's tutorial for saving data in an iPhone app.
http://www.raywenderlich.com/tutorials
So that is the setup I have going on in my app. I'm saving very simple objects. My Card object consists of a name, type, and image. That's all. So the tutorial is quite close to mine. Which is making this more frustrating.
The thing is, I have some NSLog statements in there for loading. I have it displaying the folder it's using to load and what objects it does load. Right now it is displaying this.
Loading cards from /Users/zach/Library/Application Support/iPhone Simulator/7.0.3-64/Applications/E3DB01FD-A37E-4A69-840B-43830F2BDE2C/Library/Private Documents
2013-11-04 00:02:50.073 CardMinder[84170:a0b] ()
So it seems to be trying to load them, but there's nothing there to load. Here is my function to save data.
- (void)saveData {
if (_data == nil) return;
[self createDataPath];
NSString *dataPath = [_docPath stringByAppendingPathComponent:kDataFile];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:_data forKey:kDataKey];
[archiver finishEncoding];
NSLog(#"%#",dataPath);
NSLog(#"%#",data);
[data writeToFile:dataPath atomically:YES];
}
Which is really just what's posted in that tutorial. I know if you feel generous enough to help me out i'll have to post some more code but I don't want to flood the post with useless stuff so just let me know and i'll get it out here.
I really appreciate anyone that can help, I have recently entered the desperation state and need help.
Thanks
UPDATE
NSError *error;
[data writeToFile:dataPath options:NSDataWritingAtomic error:&error];
NSLog(#"error: %#", error.localizedFailureReason);
These are the methods for the CardData class. I'm doing the name, type, and a bool here.
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_name forKey:kNameKey];
[aCoder encodeObject:_cardType forKey:kTypeKey];
[aCoder encodeBool:_checkedOut forKey:kOutKey];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
NSString *name = [aDecoder decodeObjectForKey:kNameKey];
NSString *cardType = [aDecoder decodeObjectForKey:kTypeKey];
BOOL checkedOut = [aDecoder decodeBoolForKey:kOutKey];
return [self initWithName:name cardType:cardType _Bool:checkedOut];
}
UPDATE 2
I just put some more NSLog statements in and I found out that when I press the "Save card" button in my app, it doesn't seem to execute the saveData function at all. I have log statements galore in that saveData function and when I click the saveCard button it doesn't show any of those logs. Why would that be happening?
This is my saveButton code.
- (IBAction)saveNewCard:(id)sender
{
NSString *cardName = self.nameField.text;
_cardDoc.data.name = cardName;
CardDoc *newCard = [[CardDoc alloc] initWithName:cardName cardType:cardTypeString _Bool:NO image:chosenIcon];
[_cardDoc saveData];
NSLog(#"Card save button pressed!");
CardViewController *cardViewController = (CardViewController *)[self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2];
[cardViewController.cards addObject:newCard];
[self.navigationController popToRootViewControllerAnimated:YES];
}
You should use writeToFile:options:error: instead of writeToFile:atomically:; that will give you an error message that should prove helpful. (The equivalent to atomically:YES is the option constant NSDataWritingAtomic.) Make sure you're getting back a return value of YES; if not, the error should be set.
If you're getting a value of NO but the error is not set, it means you're messaging nil. A quirk of Objective-C is that messaging nil is completely valid. If the method is defined to return something, you'll even get a result: 0 or equivalent (NO, nil, etc.)
In this case, you're messaging _cardDoc. There's no return result to detect. This is a bit harder to defensively code around, but [_cardDoc saveData] is actually [nil saveData]. The debugger will just breeze past the line.
Generally, if something absolutely should not be nil, you can use NSAssert:
NSAssert(_cardData, #"_cardData should not be nil");
[_cardData saveData];
But use this sparingly; you'll probably come to usually appreciate this behaviour.
A few things.
Post the results of your log statements so we know what you are seeing.
In order for your approach to work, your _data object needs to conform to the NSCoding protocol. That means you need to add the protocol declaration to your interface, and implement the methods encodeWithCoder and initWithCoder.
In those methods you need to save all the state data for your object / load the state back into your object.
Those methods are the most likely source of problems with your code. Post those methods if you need help with them, and walk though them in the debugger.
You might also look at the NSKeyedArchvier class method archivedDataWithRootObject. That method takes an object and encodes it into an NSData object in one step. The method archiveRootObject:toFile: take it a step further, and writes the data directly to a file for you.
NSKeyedUnarchiver has the corresponding methods unarchiveObjectWithData and unarchiveObjectWithFile to recreate your object from data/a file.
I'm basically implementing a fancier NSURLConnection class that downloads data from a server parses it into a dictionary, and returns an NSDictionary of the data. I'm trying add a completion block option (in addition to a delegate option), but it crashes anytime I try to store that data in another class.
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
contentDictionary_ = data;
}];
I can NSLog that data just fine, and basically do whatever I want with it, but as soon as I try to save it into another variable it crashes with a really obscure message.
EDIT: the crash message is EXC_BAD_ACCESS, but the stack trace is 0x00000000 error: address doesn't contain a section that points to a section in a object file.
I'm calling this function in the init method of a singleton. It DOES let me save the data if I set this in the completion block.
[SingletonClass sharedInstance].contentDictionary = data
But then the app gets stuck forever because sharedInstance hasn't returned yet, so the singleton object is still nil, so sharedInstance in the completion block calls init again, over and over.
EDIT 2: The singleton code looks like this:
+ (SingletonClass*)sharedInstance {
static SingletonClass *instance;
if (!instance) {
instance = [[SingletonClass alloc] init];
}
return instance;
}
- (id)init {
self = [super init];
if (self) {
dataFetcher_ = [[DataFetcher alloc] init];
NSString *testURL = #"..."
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
[SingletonClass sharedInstance].contentDictionary = data;
}];
}
return self;
}
Like I said, this works fine but repeats the initialize code over and over until the app crashes. This only happens the first time I run the app on a device, because I cache the data returned and it doesn't crash once I have the data cached. I would like to be able to just say self.contentDictionary = data, but that crashes.
Specify a variable to be used in the block with the __block directive outside of the block:
__block NSDictionary *contentDictionary_;
[dataFetcher_ fetchDataWithURL:testURL completionHandler:^(NSDictionary *data, NSInteger error) {
contentDictionary_ = data;
}];
You're invoking recursion before ever setting the "instance". (which I now see you understand from OP).
In your block, you can use the ivar or an accessor instead of
[SingletonClass sharedInstance].contentDictionary
use:
_contentDictionary = [data copy]; or self.contentDictionary=data;
assuming that the ivar backing the contentDictionary property is _contentDictionary.
It sounds like you tried self.contentDictionary and it failed? I got it to work in a test, with ARC turned, so there may be something about your dataFetcher that is affecting this. In my test dataFetcher just returns a dictionary with a single element.
Turns out the issue was with a bunch of different parts. My URL was empty sometimes, and my data fetcher would just fail immediately and call the completion block. In my completion block I hadn't included any error handling, so if the singleton class hadn't initialized, it would repeat forever. With a real URL this doesn't happen.
I still would like to figure out why it crashes when I try to assign the data to an ivar, though.
I am using the class XMLReader to parse some XML from a URL. The XML is successfully parsed sometimes, and sometimes I get:
Error Domain=NSXMLParserErrorDomain Code=4 "The operation couldn’t be completed. (NSXMLParserErrorDomain error 4.)"
The parse is usually successful the first time I run it, after changing something, and it fails after that until I change something else. For example, in the code below, I tried commenting out the [parser release] line, and it parsed successfully. Then I ran it again and back to error code 4.
I log the same input data every time, success or fail.
Any ideas what is going wrong here? I can paste in more code if that would help, but I have isolated the error to be within the NSXMLParser parse method (called in the code below), because it always receives the same data.
Thanks!
edit: I know that error code 4 is an empty document error. But I know my NSData is not empty. So there is something else happening here
- (NSDictionary *)objectWithData:(NSData *)data
{
//data always makes it here, the same data gets logged regardless of parse success
//NSLog(#"%#",data);
// Clear out any old data
[dictionaryStack release];
[textInProgress release];
dictionaryStack = [[NSMutableArray alloc] init];
textInProgress = [[NSMutableString alloc] init];
// Initialize the stack with a fresh dictionary
[dictionaryStack addObject:[NSMutableDictionary dictionary]];
// Parse the XML
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser setShouldResolveExternalEntities:NO];
BOOL success = [parser parse];
[parser release];
// Return the stack's root dictionary on success
if (success)
{
NSDictionary *resultDict = [dictionaryStack objectAtIndex:0];
return resultDict;
}
return nil;
}
The issue isn't whether data is correct at the beginning of your method objectWithData; it is whether data is correct through out the parse method computation. You should check that integrity of data after the parser completes.
In all likelihood, data is returned by the XMLReader but you are not retaining it properly. Occasionally the computationally intensive parser forces a garbage collection (or pool reclamation) and data gets corrupted.
Just do a [data retain] at the start of your objectwithData method; the problem will then disappear.