Objective-c: Initialize NSMutableArray with NSString that is plist structure - objective-c

If I have a NSString that came back from a web service in the form of a plist structure, how can I initialize a NSMutableArray with this NSString. I want to know if there is a similar way to initWithContentsOfFile for NSString.
My first thought was to save the NSString to a file and then use initWithContentsOfFile; but I am trying to avoid save to file first. It seems like there should be a simpler way.

Untested, should work like this:
NSData *data = [myString dataUsingEncoding:NSUTF8StringEncoding];
NSMuableArray *array = [NSPropertyListSerialization
propertyListWithData:data
options:NSPropertyListMutableContainers
format:NSPropertyListXMLFormat_v1_0
error:NULL];

See the Property List Programming Guide "Reading and Writing Property-List Data". It covers how to turn NSData into a property list. If you already have NSData from the network, just don't convert it to NSString. If you only have an NSString, use dataUsingEncoding: to convert it to NSData.

Check the documentation for the -propertyList and -mutableCopy methods.

You could use NSXMLParser to parse the XML (which is what a plist is) and turn it into a dictionary (and retrieve the array from there)

Related

how to detect pasteboard item type

I am trying to identify between three types of objects:
if it is a URL of a file
If it is a URL of a directory
if it is a simple string
up till now, I have just this code, which does not work!
NSArray * classes = nil;
classes = [[NSArray alloc] initWithObjects:[NSURL class],
[NSAttributedString class],[NSString class], nil];
NSDictionary *options = [NSDictionary dictionary];
NSArray * copiedItems = nil;
copiedItems = [pb readObjectsForClasses:classes options:options];
Now I try to take the first object of the array copiedItems and try to call "types" property and i get a crash!
Check here and here:
You would need to use these pasteboard types, instead of the ones you're using.
NSString *NSStringPboardType;
NSString *NSFilenamesPboardType;
NSString *NSPostScriptPboardType;
NSString *NSTIFFPboardType;
NSString *NSRTFPboardType;
NSString *NSTabularTextPboardType;
NSString *NSFontPboardType;
NSString *NSRulerPboardType;
NSString *NSFileContentsPboardType;
NSString *NSColorPboardType;
NSString *NSRTFDPboardType;
NSString *NSHTMLPboardType;
NSString *NSPICTPboardType;
NSString *NSURLPboardType;
NSString *NSPDFPboardType;
NSString *NSVCardPboardType;
NSString *NSFilesPromisePboardType;
NSString *NSMultipleTextSelectionPboardType;
There's an pasteboard type for URLs. To distinguish between a file and a folder, you would need to instantiate an NSURL object with the pasteboard data, and find out if it is a directory by querying its attributes.
EDIT:
You also need to consider if the pasteboard data is being put there by your own application or other applications. If it's being put by other applications, I'm not sure the pasteboard types with the classes will work.
I use something like this in one of my projects:
supportedTypes = // array with supported types, maybe from the list
NSString *type = [pasteboard availableTypeFromArray:supportedTypes];
NSData *data = [pasteboard dataForType:type];
types is a method on NSPasteboard used to tell you what is available from the pasteboard. So, you shouldn't call it on the items you get back from the pasteboard.
If you're going to request multiple class types, iterate over the response and check the class type of each item, then decide how to interact with it.
Alternatively, decide which class type of data is most useful and make individual class type requests to the pasteboard. If you get a result back, use it and carry on, if not, try the next most useful class type. Look at using canReadObjectForClasses:options: to make this easier.

Reading in from a plist, but accepting only certain strings

The goal is to have an array where all strings are of length n.
So at the moment what I have my code doing is reading in a plist (which is just 250,000 strings) into an array, and then iterating over the array in order to find which ones are/aren't of length n. Of course, for the sake of efficiency, I'd prefer being able to read in from the plist STRING BY STRING so, as I'm reading in, I may the length then before inserting into the array. I'm just starting to learn objective-c, but I was struggling to Google around for a solution =P
EDIT: Well I just found out I can find much more documentation typing property list rather than plist into google :) so I may be able to figure this out myself
You can parse plist into tree (NSMutableDictionary). Dict will have keys with name of string length.
for example
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (NSString *str in [plistDict allObjects]) {
NSString *key = [NSString stringWithFormat:#"%d", [str length]];
NSMutableArray *array = [result objectForKey:key];
if (!array) {
array = [NSMutableArray array];
}
[array addObject:str];
[result setObject:array forKey:key];
}
than you can access array with needed strings length
NSArray *string4Lenght = [result objectForKey:#"4"];
Apple doesn't provide an API for incrementally parsing a plist.
If you store your plist in XML format, you could use NSXMLParser to parse it. The schema is pretty simple and somewhat described in the Property List Programming Guide.
If you want to incrementally parse the binary format, you're going to have to do more work. There's no official documentation for the format. Apple's source code for reading and writing the format is open source (CFBinaryPList.c) and there are some useful comments along with the actual code.
If you really need to do it incrementally, I suggest going the XML route. If you do, you might want to subclass NSInputStream to be able to read from a gzip or bzip2 file and decompress on the fly.

Restore object from archived file

I'm using
[NSKeyedArchiver archiveRootObject:data toFile:file];
to save NSMutableArray to file. That works fine.
The problem comes when I try to get the array back.
NSMutableArray *s = [NSKeyedUnarchiver unarchiveObjectWithData:file];
Here I get
Incompatible pointer types sending NSString *__strong to parameter of type NSData
What is wrong here ?
You are trying to use unarchiveObjectWithData:, which expects you to pass it the contents of the file. Try using unarchiveObjectWithFile: instead, which expects you to pass it the filename.
NSData* arrayData = [NSData dataWithContentsOfFile:file];
NSMutableArray* s = [NSKeyedUnarchiver unarchiveObjectWithData:arrayData]
For production code, you should do some type/null checking before assuming the data is valid.

Is there a method for adding one NSString for NSData?

I'm working on writing to a file one user input on a textField.
So far I have one NSFileManager which writes data to a file. Still, I have no way of putting the textField input inside a file.
Is there a way do add a string value to NSData so I can write it?
you can get NSData from NSString,
NSData *newData = [yourString dataUsingEncoding:NSUTF16StringEncoding];
use encoding that fits your case.
append the obtained data to existing NSData,
[existingData appendData:newData]
Note: "existingData" should be an instance of NSMutableData.
Use below code as your reference.
NSString* myString = #"Is there a method for adding one NSString for NSData";
NSData* myData= [myString dataUsingEncoding:NSUTF8StringEncoding];

Is it possible to serialize custom objects to a plist file in Objective-C?

For the sake of simplicity, let's assume that we have the following simple class interface:
#interface Person : NSObject <NSCoding> {
NSString *firstname;
NSString *lastname;
}
#property (copy) NSString *firstname;
#property (copy) NSString *lastname;
#end
Is it possible to serialize this object into a plist (assuming that the NSCoding protocol is implemented correctly)?
Update December, 31st 2010 14:40
I have some follow up questions on this. Is it possible to have NSKeyedArchiver export the plist as XML? Furthermore, is it possible to dump the XML into variable instead of a file?
As for setting the output format to XML, I suggest:
[myKeyedArchiver setOutputFormat: NSPropertyListXMLFormat_v1_0];
As for getting it into a variable and not a file, I would suggest creating your NSKeyedArchiver with the initForWritingWithMutableData: initializer. Once finished encoding,
make sure to call finishEncoding on it, and then the XML will be in the NSMutableData that you passed in at init time.
If you then need to get an NSString from that, you can get that from your NSMutableData like so:
NSString* xmlString = [[NSString alloc] initWithData: myMutableData encoding: NSUTF8StringEncoding];
That should do the trick. Good luck!
Yes, it is. However, its not really convenient as you have to encode the object first and then save the NSData object into the array/dictionary which you want to save into the plist.
NSKeyedArchiver will automatically save the object to a plist when you give it a filename.plist file path.
This appears to be a defect in iOS. I am wondering if it is specific to the handling of the user defaults plist though.
As a possible workaround, perhaps you could try managing an NSMutableDictionary for your settings and read/write the dictionary as a plist file directly.
To write the data:
NSString* error;
NSData* pListData = [NSPropertyListSerialization dataFromPropertyList:settingsDictionary format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
[data writeToFile:settingsFilePath atomically:YES];
Then to read the data back:
NSString* error;
NSData* pListData = [NSData dataWithContentsOfFile:settingsFilePath];
settingsDictionary = [NSPropertyListSerialization propertyListFromData:pListData mutabilityOption:NSPropertyListImmutable format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
Try this magic:
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:person];
plist[#"personData"] = data;
To get it back out just use:
Person* p = [NSKeyedUnarchiver unarchiveObjectWithData:plist[#"personData"]];