Read plaintext document into NSArray in iOS? - ios

I'm new to iOS but have plenty of experience with c++ and Python. I'm trying to figure out how to read a plaintext file I have on my computer into an NSArray in xcode. In c++ I would do this:
while(istr>>string) myArray.push_back(string);
However, I need to create a local copy to be stored on the iOS device. Is there a way I can package this data so that a local copy of JUST THE ARRAY will be stored on the device? I was thinking of maybe doing something with a JSON serialization or something.
Should I really just suck it up and do this:
NSArray myArray = [[NSArray alloc] initWithObjects: #"myInfo", nil];
I just want a more elegant way to handle this, I guess.

I think maybe you're thinking a little too C about this. In C and C++, strings are arrays of bytes. In ObjC, there's an object for that. It's called NSString, and it's probably what you should be storing plaintext in.
It even has an easy class method to help you out with this if you already have a byte array:
+(id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc
See the NSString documentation for more details.
As to storing it on the device, there are solutions that range from the simple (NSUserDefaults) to the complex (Core Data), but pretty much anything will expect plain text be in an NSString.
EDIT:
The title of this question talks about reading the string from the filesystem. First step is to get the bytes of the file into an NSData object. The easy way:
+(id)dataWithContentsOfFile:(NSString *)path
Then make a string out of the data with this initializer of NSString:
-(id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding

I don't know if this can help you, anyway if you just need to store an array of data to filesystem and deserialize it back to NSArray, an easy way is to use plists.
It is a convenient way to store a small amount of data, without any kind of relationship (there is Core Data for that). The main advantage is that you can store in it NSArray, NSDictionary, NSNumber, NSString, NSDate and NSData (so any kind of binary information) and they get automatically serialized and deserialized through some simple methods.
You can write an NSArray to a file in this way:
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag
and deserialize it back with this:
+ (id)arrayWithContentsOfFile:(NSString *)aPath
If you just want to provide some initial data to your app, and it is for example an array of strings or something similar, you can manually add a plist to your project by going to File->New->File and choosing Resources->Property list, and fill it by hand.
You can read more at https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/PropertyLists/Introduction/Introduction.html

Related

Saving NSArray data to disk

I have an NSMutableArray that contains NSMutableDictionary's. Each dictionary has an AVAsset, an NSURL, an NSString, and two UIImages. I want to save my array to disk so that each time I close and open my app, I can load the array and convert the URLs's to NSData objects in order to play audio and use the AVAssets for some other actions. I know I can save and load my array using initWithContentsOfFile and writeToFile:atomically and this answer is pretty informative: Saving a NSArray. However, that answer was from 2009. Is there a better way of saving and loading an array these days?.
As for the answer you linked, the answer is still valid. And according to it, you cannot store it the way it mentions. This is because array must be plist format compatible in order to be saved like that. When you parse your array down to lowest element hierarchy, you have UIImage which is just an object pointer and doesn't make sense.
One practical way would be store UIImages as separate files, and store their paths as part of your NSMutableDictionary objects. Same holds true for AVAssets. Off course you need to engineer the solution to fully accomplish this goal.
One more way to store non-plist compatible objects is to use archiving and unarchiving feature. Refer to the documentation. Here, make sure that each object in the tree follows protocol NSCoding (Probably, AVAsset in your question does not conform to it, so you need a way to work around it). For an example, see this answer and search the likes of it.

How to archive ivar/property of object in iOS

I know how to use NSCoding in some object.
What I do not like is that:
[NSKeyedArchiver archiveRootObject:self toFile:path]
will save also other data (some stuff regarding the object) except from which is defined in
- (void)encodeWithCoder:(NSCoder *)aCoder
Is there some way to same just one ivar/property of the object, and then restoring it, without saving all other stuff ?
I hope that you can understand my question.
Also, is there some way to make file encrypted ?
I am planing to keep score of game in this way (if there is some other better way please say so).
But I would not like that somebody can change file and in that way fake his score.
Yes, write it explicitly to a file on its own, as data, text, JSON formatted text, ...
NSKeyedArchiver needs the additional information so that it knows what type of class to create. If you don't want that information to be saved then you can't use NSKeyedArchiver.
A simple option is to save as a plist. NSDictionary makes this easy:
NSDictionary *saveDict = #{ #"MyInt" : #( self.intValue ), #"MyBool" : #( self.boolValue ) };
[saveDict writeToFile:... atomically:YES];
but, this has limitations on the data types you want to store. So does JSON.

NSDictionary Vs. NSArray

I am reading on objective-c (a nerd ranch book), and I can't help thinking about this question: How do I decide which collection type, NSArray or NSDictionary (both with or w/o their mutable subclasses), to use when reading content from URL?
Let's say am reading JSON data from a PHP script (a scenario am dealing with), which to use? I know it is stated in many references that it depends on structure of data (i.e. JSON), but could a clear outline of the two structures be outlined?
Thank you all for helping :)
NSArray is basically just an ordered collection of objects, which can be accessed by index.
NSDictionary provides access to its objects by key(typically NSStrings, but could be any object type like hash table).
To generate an object graph from a JSON string loaded via a URL, you use NSJSONSerialization, which generates an Objective-C object structure. The resulting object depends on the JSON string. If the top-level element in your JSON is an array (starts with "["), you'll get an NSArray. If the top-level element is a JSON object (starts with "{"), you'll get an NSDictionary.
You want to use NSArray when ever you have a collection of the same type of objects, and NSDictionary when you have attributes on an object.
If you have, lets say a person object containing a name, a phone number and an email you would put it in a dictionary.
Doing so allows the order of the values to be random, and gives you a more reliable code.
If you want to have more then one person you can then put the person objects in an array.
Doing so allow you to iterate the user objects.
"withContentOfURL" or "withContentOfFile" requires the data in the URL or the file to be in a specific format as it is required by Cocoa. JSON is not that format. You can only use these methods if you wrote the data to the file or the URL yourself in the first place, with the same data. If you write an NSArray, you can read an NSArray. If you write an NSDictionary, you can read an NSDictionary. Everything else will fail.

How should I store a large number of nested dictionary information in iOS?

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

Using NSKeyedArchiver to generate XML from NSManagedObjects held in an NSDictionary

I have an NSDictionary, which contains a bunch of NSManagedObjects.
I can then use NSKeyedArchiver to write this to an NSData object.
These are generated using this method. Which works fine and allows me to save a section of schema to disc and then read it back as a new set of objects in the core data model.
If I use either archivedDataWithRootObject:
or archiveRootObject:toFile:, as per the documentation
I can see that the format of the archive is NSPropertyListBinaryFormat_v1_0, whereas I want to serialise in NSPropertyListXMLFormat_v1_0, so that I can write my objects to a file and then process them elsewhere as plain old XML. (In fact I want to generate documents from them on a Windows based system.)
1) Is there a way I can do this? If so how?
2) Is there a better approach.
I want to maintain the serialised nature, since I also want to send the file back to the iOS device later and recreate the object model.
Create your own instance of NSKeyedArchiver with initForWritingWithMutableData:.
Set the format with setOutputFormat:NSPropertyListXMLFormat_v1_0.
Encode your root object with encodeObject:forKey:.
Call finishEncoding.
To unarchive the data you encoded in this way, you have to similarly instantiate an NSKeyedUnarchiver.
Thanks Ole! I was heading in that direction, but was not sure if it was the right way. Here is what I did in code in case it helps someone.
NSDictionary *dataAsDictionary=[self toDictionaryBlockingRelationships:blockRelationship];
NSString *savePath = [#"~/Documents/Saved.data" stringByExpandingTildeInPath];
NSMutableData *xmlData=[NSMutableData data];
NSKeyedArchiver *archive=[[NSKeyedArchiver alloc ]initForWritingWithMutableData:xmlData];
[archive setOutputFormat:NSPropertyListXMLFormat_v1_0];
[archive encodeRootObject:dataAsDictionary];
[archive finishEncoding];
if(![xmlData writeToFile:savePath atomically:NO]){
NSLog(#"Failed to write to file to filePath=%#", savePath);
}
[archive release];

Resources