I'm writing an iPhone app that loads Menu data from a database in the cloud. I am using AFNetworking (specifically AFJSONRequestOperation) to download the data in appDelegate.
Everything works fine up until this point, but I also need the app to be able to load the menu when the app is offline. To handle this, I want to save the returned JSON data to disc after it is retrieved by the AFJSONRequestOperation call.
My initial strategy was to save the returned JSON as a string, but I can't find a way to get the string response from an AFJSONRequestOperation. It seems silly to make two calls to the web service (one to return a JSON object and the other to return text) although it would be straightforward. I'd like to know if there is a better or more efficient way to do this. I could skip the AFJSONRequest and go for a plain AFHttpRequest but then I would need to construct the JSON object manually.
Is there a better option than either of the two I've thought up? In my opinion, the right answer would involve a single call that constructs the JSON object and also allows me to have access to the original text response, but I am open to hearing alternatives.
Don't save it as the JSON string level. AFNetworking will return the JSON as either an NSArray or an NSDictionary depending on your JSON structure.
You can just save the array or dictionary as a plist.
see -[NSDictionary writeToURL:atomically:] or -[NSArray writeToURL:atomically:]
I would try NSJSONSerialization to save NSData to disk.
Example:
NSData *dataToSave = [NSJSONSerialization dataWithJSONObject:responseObject options:nil error:nil];
id jsonObject = [NSJSONSerialization JSONObjectWithData:dataToSave options:nil error:nil];
Another option would be to take your responseObject from AFNetworking. Presumably a well-formed NSDictionary and simply place it in NSUserDefaults for semi-persistent storage. You need to make sure you don't have any null values in your dictionary as NSUserDefaults does not play nicely with null values.
Related
I am using one web service in my app. Unfortunately I am not expecting the
response to be auto sorted based on the keys. I need the original response as
it is in web. Can anyone let me know how to do this in iOS app.I am using the
following code to show the response.
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
JSON objects have unordered keys. iOS is not sorting the keys in the data structure. It's just sorting them when you print out the result (for convenience). Internal to the data structure, there is no guaranteed order. It depends on how things hash.
There is no way to use NSJSONSerialization to create an "ordered dictionary" since ordered dictionaries don't exist in JSON. The way you fix this is to use a list of dictionaries, such as:
[ { "First": "A" }, { "Second", "B" } ]
This is promised to stay in order because it's a list.
If you can't change the format, and the format relies on order, then it's not proper JSON and you'll have to parse it some other way. Typically I try to do simple string parsing (splitting on newlines for instance) to find large valid JSON "chunks" that can be handed to the parser.
When writing data to file (e.g. Thumbnails for caching, user data, etc.), how do you deal with the fact that the iDevice could not be able to write your data to file since the disk is full?
Will NSFileManager throw an exception in case of low disk space?
What's the designated way to deal with this and to inform my user that there's very little disk space left for his data? (I'm saving a fair amount of different data at different places in my app and searching for a common way to deal with it.)
As you mentioned in the comments that you want to save NSDictionary. If you only want to know whether the file is saved successfully or not, you can inspect the return value of the
writeToFile:atomically: function.
Return Value
YES if the file is written successfully, otherwise NO.
More information under the NSDictionary's Storing Dictionaries Section.
Alternatively,
If you want to get a more detail error message for the failure (such as out of disk space, folder not exist and etc.), then you can convert the NSDictionary to NSData before saving it.
NSDictionary to NSData:
NSData *myData = [NSKeyedArchiver archivedDataWithRootObject:myDictionary];
NSData to NSDictionary:
NSDictionary *myDictionary = (NSDictionary*) [NSKeyedUnarchiver unarchiveObjectWithData:myData];
The benifits is that you will also have access to this API -writeToFile:options:error:.
If there is an error writing out the data, upon return contains an NSError object that describes the problem.
Also more detail could be found under the Storing Data Section of NSData.
I think that's the best you can do in case there is a low disk space problem on the device.
I've found that when using NSDictionary to create the params for an AFNetworking POST request the behaviour is inconsistent. I have multiple POSTs where the params arrive at the server in the same order I created them in the NSDictionary and now creating a new request they are arriving at the server in a different order.
This is how I send them:
NSDictionary *params = #{#"username": #"testuser", #"count": #"6"};
But this is how it arrives on the server:
{"count":"6","username":"testuser"}
Its important for me that it arrive at the server exactly the same as it leaves my app as I hash together values for integrity and can't verify if it arrives in a different order to how it leaves.
I know NSDictionary is by nature not expected to keep the order, is there anyway using NSDictionary or another way I can guarantee the order?
Thanks in advance
You'd have to use an ordered dictionary class. Unfortunately, there's not one in the Foundation framework, but it's fairly easy to create (or even download) your own. This article describes how to create an ordered dictionary class in Objective-C, and includes source code.
I am using AFNetworking to retrieve JSON data from a web service. Part of the response string I get is:
{"DATA":{"LEASE TYPE":"3 Yrs + 0 renew of 0 Yrs","LANDLORD":"","TENANT":"test comp"...
and so on. The order of the key values in the "DATA" dictionary ("LEASE TYPE","LANDLORD","TENANT"...) is important for presentation purposes. However, when AFNetworking calls NSJSONSerialization's:
+ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;
method, the returned dictionary has the keys in a different order.
I notice that the AFJSONRequestOperation object has the server's response stored as an NSString, with everything in the correct order. However I'm not keen on parsing the JSON by hand if I can avoid it.
Can anyone suggest a way that will let me get at / keep the keys in their original order?
Thanks.
If the order is important use an array not a dictionary, dictionaries are be by their nature unordered. Or add an array of dictionary keys in the order desired.
If you have no control over the response that is sent you will have to parse the JSON yourself at least for the ordering.
When you'r creating an NSDictionary, the order will not be the same. I often recognized that dictionaries get ordered by key-name alphabetically.
But when using dictionaries the order doesn't really matter. And they shouldn't!
As the previous answers mentions dictionaries are by nature without order, but you can find here a nice class of OrderedDictionary:
http://www.cocoawithlove.com/2008/12/ordereddictionary-subclassing-cocoa.html
http://projectswithlove.com/projects/OrderedDictionary.zip
I have a large number of nested dictionaries and the leaf (or most nested) dictionaries store integer data and integer keys. All the information remains constant (but may change in a future release). I am currently allocating the dictionaries from constants in code but I feel I should be reading that information from XML or similar. I have read about Core information, plists, databases and archives but I don't want the user to be able to change it, I never want to be able to write it (except maybe during the release procedure) and I never want to display it. I would like to be able to hand edit it before release.
What is the best method to store this constant data?
Basically you need to ship your data in files with the app -
XML or JSON are both suitable for this. When I have had to do something similar I used JSON
It works something like this :
Define your JSON in text file (UTF8) and then use the
NSString initWithContentsOfFile to load file contents into a NSString
You can then use the NSJSONSerialization JSONObjectWithData to give you the top level dictionary for your JSON
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
From this you can extract your NSStrings / NSArrays using NSDictionary objectForKey for your data. Obviously the exact format will depend on your JSON format