I am trying to parse JSON with iOS5's NSJSONSerialization. This code CORRECTLY parse the data I wanted for the first time, BUT THEN THE DATA JUST STAYED THE SAME. The JSON on the URL already changed, but the code kept giving me the first data it parsed, which is now incorrect. No matter how many times I "build and run", it keeps giving me the same thing.
When I copied the code to the new project, it works, again, for the first time, and then does the same thing.
I have no idea where the problem is, maybe cache?
Thanks for you help!!!
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSArray* allDepartures = [json objectForKey:#"departures"];
NSLog(#"departures: %#", allDepartures);
NSDictionary* stops = [allDepartures objectAtIndex:0];
NSNumber* time = [stops objectForKey:#"expected_mins"];
NSString* name = [stops objectForKey:#"headsign"];
nameLabel.text = [NSString stringWithFormat:#"%#",name];
timeLabel.text = [NSString stringWithFormat:#"%i",[time intValue]];
}
- (IBAction)getInfo:(id)sender {
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: myURL];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES];
});
How do you set "myURL", only in viewDidLoad? After a while if the OS calls viewDidUnload and you run the app again, viewDidLoad is called and myURL is set again. If thats the case you should set myURL every time you call getInfo.
Related
Aloha,
I've come across a problem in iOS 6.1.3 reading webarchive files where -occasionally- the WebResourceData returns a null.
These are known good files (created in TextEdit) stored inside the bundle, and usually read fine. It's just that every so often, they don't.
In a simple test shown below, I read 3 different files over and over until I spot an error. For iOS 6.1.3, I hit an error between 1 and 200 iterations, every time I run the test. I've run this on various devices and the simulator with the same results.
// trying to find out why reading webarchives occasionally
// returns with empty WebResourceData from known good files.
//
- (BOOL) testMe {
NSMutableDictionary *plist = [[NSMutableDictionary alloc] initWithCapacity:100] ;
int iteration = 1;
BOOL ok = TRUE;
// keep going until we have an error
while (ok) {
NSArray *fileNames = #[#"file1",
#"file2",
#"file3" ];
// LOOP through the webArchives...
//
for (NSString *webarchiveName in fileNames) {
// get the webarchive template
//
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:webarchiveName withExtension:#"webarchive"];
//
NSData *plistData = [NSData dataWithContentsOfURL:fileURL];
NSString *error;
NSPropertyListFormat format;
//
plist = (NSMutableDictionary *)[NSPropertyListSerialization propertyListFromData:plistData
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&error];
// check to see if it loaded
//
//
if(!plist){
NSLog(#"ERROR: did not load %#", webarchiveName);
}else{
NSData *foundWebResourceData = [[plist objectForKey:#"WebMainResource"] objectForKey:#"WebResourceData"];
NSString *foundHTML = [NSString stringWithUTF8String:[foundWebResourceData bytes]];
if (foundHTML == NULL) {
NSLog(#" %# descr = %#", webarchiveName, foundHTML);
[errorOutlet setText:[NSString stringWithFormat:#"%# returned with no content (null) in WebResourceData", webarchiveName]];
ok = FALSE;
}
} //---- end of if plist exists
} // loop through all 3 files
[countOutlet setText:[NSString stringWithFormat:#"%d", iteration]];
++ iteration;
} // keep looping until error
return ok;
} // end of testMe
The error shows up in these two lines:
NSData *foundWebResourceData = [[plist objectForKey:#"WebMainResource"] objectForKey:#"WebResourceData"];
NSString *foundHTML = [NSString stringWithUTF8String:[foundWebResourceData bytes]];
but it's not consistent. I've isolated the code by making a new xcode project where this is the only activity, other than displaying the iteration count and a retest button. The files always load, and always have a WebMainResource with a WebResourceData key.
A possible clue is that if I instead insert the code into ViewDidLoad, it runs for many more iterations but still finds a null. Calling [self testMe] from a button action hits an error much faster...not sure why.
I'm a bit at a loss, and hoping that it's not an iOS bug, but rather something basic I'm just missing. Any help would be appreciated.
You might try using the NSString initializer designed for reading NSData:
NSString *foundHTML = [[NSString alloc] initWithData:foundWebResourceData encoding:NSUTF8StringEncoding];
I am new to iOS. I have some problem do you have some solution for it? I am not able to print the json value in textfield
This is my contactViewController.m file:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
fname.text=#"Hello";
}
After the view is loaded hello value is shown in the text box, but when I call list button to get the json values:
-(IBAction)list:(id)sender{
vedant=[[WebserviceViewController alloc]init];
[vedant listContacts];
}
Then from webserviceViewController.m I pass the jsonResponse to the same file i.e contactViewController.m and parse the json value and print it but it does not shows the value in text field
-(void)allContacts:(NSString *)JSONResponse{
NSLog(#"%#",JSONResponse);
NSData *jsonData = [JSONResponse dataUsingEncoding:NSASCIIStringEncoding];
//
NSError *err;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&err];
int accCount =[json count];
for(int i=0;i<accCount;i++){
NSData *jsonData = [JSONResponse dataUsingEncoding:NSASCIIStringEncoding];
//
// NSLog(#"%#",JSONResponse);
NSError *err;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&err];
NSString *firstName = [NSString stringWithFormat:#"%#", [[json objectAtIndex:i] objectForKey:#"first_name"]];
NSLog(#"%#",firstName); //this prints the actual first name in console But
fname.text=firstName;
NSLog(#"%#",fname.text); //it prints "(Null)" in console
}
}
Do I need to create delegate to pass value?
Yes, then any help of creating such delegate some article or example.
Check if fname isn't nil. If you forgot to properly connect it in Interface Builder, fname will be nil and all messages sent to it (like setText:) will be ignored.
Please First check that you have bind your textfield with your fname variable. If yes then try to assign value using this:
fname.text=[NSString stringWithFormat:#"%#",firstName];
Just try this. I hope this will work for you.
Get your first name(i.e value from json) into global value and assign that global value into your textfield . It is so simple. if you still have doubts than reply i'll post sample code
txtName.text=firstname; OR
txtName.text=[NSString stringWithFormat:#"%#",firstName];
Using Delegate i solved this problems
In my contactViewController.m file:
-(IBAction)list:(id)sender{
vedant=[[WebserviceViewController alloc]init];
vedant.delegate=self;
[vedant listContacts];
}
In webserviceViewController.h I created my own delegate
#protocol WebservicesDelegate <NSObject>
-(void)allContacts:(NSString *)JSONResponse;
#end
then in webserviceViewController.m file after getting the json response from backend i am sending the response back to my contactViewController.m file using this code
[self.delegate allContacts:jsonResponse];
the compiler comes back to allContacts function in contactViewController.m file this code is same i didn't change the code now it prints the value.
-(void)allContacts:(NSString *)JSONResponse{
NSLog(#"%#",JSONResponse);
NSData *jsonData = [JSONResponse dataUsingEncoding:NSASCIIStringEncoding];
//
NSError *err;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&err];
int accCount =[json count];
for(int i=0;i<accCount;i++){
NSData *jsonData = [JSONResponse dataUsingEncoding:NSASCIIStringEncoding];
//
// NSLog(#"%#",JSONResponse);
NSError *err;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&err];
NSString *firstName = [NSString stringWithFormat:#"%#", [[json objectAtIndex:i] objectForKey:#"first_name"]];
NSLog(#"%#",firstName); //this prints the actual first name in console But
fname.text=firstName;
NSLog(#"%#",fname.text); //it prints "(Null)" in console
}
}
I think because i was trying to get data from different view and wanted to display the data in another view was the problem.
But by creating the delegate the problem was solves and i can easily send the data from one view to another view.
What is the easiest way to write a JSON data/NSDictionary and read it back again? I know there's NSFileManager, but is there an open source framework library out there that would make this process easier? Does iOS5 NSJSONSerialization class supports writing the data to disk?
Yup, NSJSONSerialization is the way to go:
NSString *fileName = #"myJsonDict.dat"; // probably somewhere in 'Documents'
NSDictionary *dict = #{ #"key" : #"value" };
NSOutputStream *os = [[NSOutputStream alloc] initToFileAtPath:fileName append:NO];
[os open];
[NSJSONSerialization writeJSONObject:dict toStream:os options:0 error:nil];
[os close];
// reading back in...
NSInputStream *is = [[NSInputStream alloc] initWithFileAtPath:fileName];
[is open];
NSDictionary *readDict = [NSJSONSerialization JSONObjectWithStream:is options:0 error:nil];
[is close];
NSLog(#"%#", readDict);
It seems you must ensure that the directories in the path exist, otherwise your app will hang and consume 100% of your CPU when using + writeJSONObject:toStream:options:error:. The file itself will be created by the stream.
When I NSLog the contents of an NSMutableArray I get null. I believe I know what the issue is.
I'm having a bit of trouble trying to figure out how to remove "//" at the beginning of this JSON output. If you load http://www.google.com/finance/info?infotype=infoquoteall&q=AAPL,C into your browser you'll see the "//" at the beginning. I believe that the "//" is what is causing the array to return null. How could I go about removing the two dashes? Below is I have what I've done thus far...
NSString *url = #"http://www.google.com/finance/info?infotype=infoquoteall&q=C,JPM,AIG,AAPL";
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString: url]];
//parse out the json data
NSError* error;
NSMutableArray* json = [NSJSONSerialization
JSONObjectWithData:data //1
options:kNilOptions
error:&error];
NSLog(#"json is %#", json); //returns "json is (null)"
You can try this:
NSData *newData = [data subdataWithRange:NSMakeRange(4, [data length] -4)];
This gets rid of the first four characters. There was a control character the two slashes and a space before the first "[", and this gets rid of those. I tried this but the data still had a flaw in it further on.
I am trying to figure out how to cache a list of comic titles that I want to use in a uitableview, and will be updated roughly every week, so instead of loading the list every time the app is launched from the web-server I want to hold onto a cache.. only problem is I'm finding the documentation hard to come across for caching lists like this.
Any example code or suggestions would be greatly appreciated :)
You fetch the XML
You parse it to a NSDictionary using NSXMLParser
You serialize and store the dictionary.
#implementation NSDictionary(BinaryPlist)
- (BOOL)writeToBinaryFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile {
NSString *errorString = nil;
NSData *data = [NSPropertyListSerialization dataFromPropertyList:self format:NSPropertyListBinaryFormat_v1_0
errorDescription:&errorString];
if (errorString) {
return NO;
}
return [data writeToFile:path atomically:useAuxiliaryFile];
}
#end
Then you can define for how long you should consider the cache fresh, or, alternatively, issue HTTP HEAD request and check the Last-Modified header.
- (BOOL)cacheValid:(NSString*)path {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
NSDictionary *attrs = [fileManager attributesOfItemAtPath:path error:&error];
if (!error) {
NSDate *modDate = [attrs fileModificationDate];
NSTimeInterval delta = - [modDate timeIntervalSinceNow];
if (delta < kCacheTTL) {
return YES;
}
}
return NO;
}