i try to download data from a webserver with json into my ios app.
That´s the json output
{
"responseHeader":{
"status":0,
"QTime":37,
"params":{
"wt":"json",
"q":"title:ios"
}
},
"response":{
"numFound":348,
"start":0,
"docs":[
{
"edition":"2. ed.",
"illustrated":"Not Illustrated",
"id":"BSZ117259543",
"author":"Raw, Charles",
"title":"IOS /",
"spelling":"Raw, Charles (DE-576)180904566 Do you sincerely want to be rich? span. IOS / Charles Raw; Bruce Page; Godfrey Hodgson 2. ed. São Paulo : Ed. Expressão e Cultura, 1972 XV, 496 S. Page, Bruce (DE-576)162468539 aut Hodgson, Godfrey (DE-576)161444822 aut",
"last_indexed":"2012-09-02T01:11:38Z",
"recordtype":"marc",
"title_auth":"IOS /",
"title_sort":"ios",
"title_short":"IOS /",
"fullrecord":"00985nam a22003372"
}
]
}
}
The Code of my project:
- (void)viewDidLoad
{
[super viewDidLoad];
// 1 Schritt
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
//code executed in the background
// 2
//NSData* kivaData = [NSData dataWithContentsOfURL:[NSURLURLWithString:#"http://api.kivaws.org/v1/loans/search.json?status=fundraising"]];
NSData* kivaData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://bib.flux-services.net/solr/biblio/select?q=title:ios&wt=json"]];
//3
NSDictionary* json = nil;
if (kivaData) {
json = [NSJSONSerialization JSONObjectWithData:kivaData options:kNilOptions error:nil];
}
//4
dispatch_async(dispatch_get_main_queue(), ^{
[self updateUIWithDictionary:json];
});
});
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)updateUIWithDictionary:(NSDictionary*)json {
#try {
label.text = [NSString stringWithFormat:#"%# from %#",
json[#"docs"][0][#"title"],
json[#"docs"][0][#"edition"],
//json[#"loans"][0][#"loan_amount"],
//json[#"loans"][0][#"use"],
nil
];
}
#catch (NSException *exception) {
[[[UIAlertView alloc]initWithTitle:#"Error"
message:#"Could not parse the json feed."
delegate:nil cancelButtonTitle:#"Close" otherButtonTitles:nil]show];
NSLog(#"Exception: %#", exception);
}
When I run the programm then the IPhone simulator could not display the data.
I haven´t any idea where´s the problem!?!
Have anybody a idea or a solution???
First, exceptions are for programming errors. You don't catch exceptions. If you get exceptions, your program crashes, you find the reason, you fix the code. That's how it's done in Objective-C. People might thing you are a Java programmer.
Second, an NSLog of the dictionary you received would be useful. What you printed doesn't match your code.
Third, when you get JSON data, you just check everything. You check that you've received a dictionary. You check that there is a property named "docs" and that it is an array. You check that it has at least one element. You get the first element and check that it is a dictionary. You check that the dictionary has a property "title" and a property "edition" and that they are strings and so on.
Fourth, to help keeping your sanity, define an object representing what's in the JSON data, taking a dictionary as an initialiser, and extract everything into that object. So from then on you are in safe NSObject territory.
Fifth, what on earth do you think happens when your users read "Could not parse the JSON feed"? Their poor brains will explode. Error messages are for users, not for programmers.
Related
In my iOS app, I am using the forecast.io API to get a weather forecast for 3 specific days. Once I get the array from all 3, I want to create an NSMutableArray and add all of those objects to it. The problem I am getting is that it is trying to create the NSMutableArray before the forecast data is retrieved. Here is what I have so far:
typedef void(^myCompletion)(BOOL);
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:YES];
[self myMethod:^(BOOL finished) {
if(finished){
NSMutableArray *allOfIt = [[NSMutableArray alloc] initWithObjects:self.weatherSaturday, self.weatherSunday, self.weatherMonday, nil];
NSLog(#"%#", allOfIt);
}
}];
}
-(void) myMethod:(myCompletion) compblock{
//do stuff
ForecastKit *forecast = [[ForecastKit alloc] initWithAPIKey:#"MY-API-KEY"];
// Request the forecast for a location at a specified time
[forecast getDailyForcastForLatitude:37.438905 longitude:-106.886051 time:1467475200 success:^(NSArray *saturday) {
// NSLog(#"%#", saturday);
self.weatherSaturday = saturday;
} failure:^(NSError *error){
NSLog(#"Daily w/ time %#", error.description);
}];
[forecast getDailyForcastForLatitude:37.438905 longitude:-106.886051 time:1467561600 success:^(NSArray *sunday) {
// NSLog(#"%#", sunday);
self.weatherSunday = sunday;
} failure:^(NSError *error){
NSLog(#"Daily w/ time %#", error.description);
}];
[forecast getDailyForcastForLatitude:37.438905 longitude:-106.886051 time:1467648000 success:^(NSArray *monday) {
// NSLog(#"%#", monday);
self.weatherMonday = monday;
} failure:^(NSError *error){
NSLog(#"Daily w/ time %#", error.description);
}];
compblock(YES);
}
When the code is ran, it fires the NSLog for allOfIt, which shows as null, before it gets any of the forecast data. What am I missing?
The problem I am getting is that it is trying to create the NSMutableArray before the forecast data is retrieved
Yup, exactly. The problem is simply that you don't understand what "asynchronous" means. Networking takes time, and it all happens in the background. Meanwhile, your main code does not pause; it is all executed instantly.
Things, therefore, do not happen in the order in which your code is written. All three getDailyForcastForLatitude calls fire off immediately and the whole method ends. Then, slowly, one by one, in no particular order, the server calls back and the three completion handlers (the stuff in curly braces) are called.
If you want the completion handlers to be called in order, you need each getDailyForcastForLatitude call to be made in the completion handler of the getDailyForcastForLatitude call that precedes it. Or, write your code in such a way that it doesn't matter when and in what order the completion handlers come back to you.
So I am using the pokedex API as a learning curve for IOS and web services,
Here is my didrecivedata when the connection completes
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
//If the resposne recieved is good the call this function
// NSLog(#"data is %#", data);
//NSString *myString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//NSLog(#"string is %#", myString);
//Put data into a string
NSError *e = nil;
pokeDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&e];
NSLog(#"dictionary is %#", pokeDictionary);
}
This outputs Json to the console, I can log it into the console like this
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// do something with the data
// receivedData is declared as a method instance elsewhere
NSLog(#"Succeeded!");
NSLog(#"The Pokemon's name is %#", pokeDictionary[#"name"]);
NSLog(#"The Pokemon's attack is %#", pokeDictionary[#"attack"]);
NSLog(#"The Pokemon's speed is %#", pokeDictionary[#"speed"]);
}
However tried to extract Json into text fields like this
{
self.pokemonAttack.text = (#"The Pokemon's speed is %#", pokeDictionary[#"name"]);
self.pokemonAttack.text = (#"The Pokemon's speed is %#", pokeDictionary[#"attack"]);
self.pokemonSpeed.text = (#"The Pokemon's speed is %#", pokeDictionary[#"speed"]);
}
Error is "expression result unused", I guess my main issue is I am not comfortable with objective-c and just hacking around in IOS. For this I apologise and understand the comments of saying do objective-c courses
If you can point me in the right direction I can continue my trial by fire, I guess I should also be moving to swift soon
First of all you should know that the delegate method connection:didReceiveData: may be called multiple times as the connection loads the data incrementally. It may be called once if your returned data is very short, but it will most likely break at some point as you'll end up trying to parse incomplete data.
Regarding the warning you're getting - you just can't format strings like that. You're trying to use string formatting like you're calling NSLog, but the NSLog method does the formatting for you. You need to do something like this:
self.pokemonAttack.text = [NSString stringWithFormat:#"The Pokemon's speed is %#", pokeDictionary[#"speed"]];
I have a function where i use NSJSONSerialization. I want to do a good release / memory management because it can be 200+ objects.
for (NSDictionary *dict in visitsAndQuestionnaires) {
NSInputStream *is = [[NSInputStream alloc] initWithFileAtPath:filePath];
[is open];
if (is) {
JSON = [NSJSONSerialization JSONObjectWithStream:is options:0 error:nil];
if (![JSON respondsToSelector:#selector(objectForKey:)]) {
JSON = nil;
}
[is close];
}
[is release];
if (JSON) {
// HERE MY FUNCTION TO CREATE A LAYOUT BASED ON JSON
}
// WHEN FUNCTION WITH JSON IS DONE:
if (JSON) {
//JSON = NIL;
[(id)JSON release];
}
}
The function than does it function for 219 objects only on object 219 the application crashes with this console log:
-[CFDictionary release]: message sent to deallocated instance 0x26a13ca0
Even though im only releasing JSON when it still exists and this works (i can see it in profiler) only it looks like when it tries to release last object its already gone.
How can i fix this? So that this releas functions works?
The NSJSONSerialization class returns an autoreleased object. Just don't release it.
Creating an Autoreleasepool on the function loop was the solution. My thanks go out to you guys! Learned something today..
I'm downloading a .plist like this:
dispatch_async(kBgQueue, ^{
NSError* error = nil;
NSData* data = [NSData dataWithContentsOfURL:kFeedURL options:NSDataReadingUncached error:&error];
if (error) {
//NSLog(#"Feed error: %#", [error localizedDescription]);
} else {
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:YES];
}
});
But how do I convert the data back to a plist and save it to the app?
- (void)fetchedData:(NSData *)responseData {
//???
}
It's hard to tell you what to do without more information. You say you're downloading a plist, but then ask how to convert the data back to a plist. If it's a serialized plist, then you probably want to use the NSPropertyListSerialization class method, propertyListWithData:options:format:error: to convert it to a plist object. That method returns an object typed id, so you probably should log the class of the object, to see what you actually got, and then use one of the save methods from that class to save the data.
I'm new to ios development.My app gets slower when i'm parsing image using json parser in ios 5.
Please could anybody help to solve this problem.
-(NSDictionary *)Getdata
{
NSString *urlString = [NSString stringWithFormat:#"url link"];
urlString = [urlString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSURL *url = [NSURL URLWithString:urlString];
NSData* data = [NSData dataWithContentsOfURL:url];
NSError* error;
NSDictionary* json;
if (data) {
json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSLog(#"json...%#",json);
}
if (error) {
NSLog(#"error is %#", [error localizedDescription]);
// Handle Error and return
// return;
}
return json;
}
Your description of the problem isn't exactly helpful. It's unclear to me if everything in your app is slow, or just certain operations; if you exprience a slow action and then it becomes fast again or if it continues to perform slowly.
Whatever, the general rule is to performan all network communication including the parsing of the answer on a separate thread, i.e. not on the main thread that is responsible for managing the user interface. That way the app remains responsive and appears to be fast.
If you can download the images separately, you can already display the result and put a placeholder where the image will appear. Later, when you have received the image you remove the placeholder and put the image there.
This line is probably the culprit.
NSData* data = [NSData dataWithContentsOfURL:url];
If you're calling this on the main thread (and because you haven't mentioned threads at all I suspect that you are) it will block everything and wait until the server has responded.
This is a spectacularly bad experience for the user :)
You need to do all of this on a background thread and notify the main thread when you're done. There's a couple of ways of doing this (NSOperation etc) but the simplest is just this :
// Instead of calling 'GetData', do this instead
[self performSelectorOnBackgroundThread:#selector(GetData) withObject:nil];
// You can't return anything from this because it's going to be run in the background
-(void)GetData {
...
...
// Instead of 'return json', you need to pass it back to the main thread
[self performSelectorOnMainThread:#selector(GotData:) withObject:json waitUntilDone:NO];
}
// This gets run on the main thread with the JSON that's been got and parsed in the background
- (void)GotData:(NSDictionary *)json {
// I don't know what you were doing with your JSON but you should do it here :)
}