How to save (and retrieve) NSAttributedString in a NSDictionary - objective-c

I am trying to save and retrieve XML data to a file.
For this purpose I am using NSDictionary's writeToFile:
The question is, how to write and retrieve an attributed string to and from a file on disk using NSDictionary's writeToFile:?

If you need something more portable than archived object consider using RTF representation of NSAttributedString:
NSAttributedString* attrString = [[NSAttributedString alloc] initWithString:#"Attributed String"
attributes:#{ NSForegroundColorAttributeName: [NSColor redColor]}];
NSData* rtfData = [attrString RTFFromRange:NSMakeRange(0, attrString.length) documentAttributes:nil];
[rtfData writeToFile:#"string.rtf" atomically:YES];
You can read it back:
NSData* rtfData = [NSData dataWithContentsOfFile:#"string.rtf"];
NSAttributedString* attrString = [[NSAttributedString alloc] initWithRTF:rtfData documentAttributes:nil];
Also since RTF is a simple text format you can convert RTF data to NSString and store it as plain text in a dictionary.

NSAttributedString is not a property list object, so writeToFile: won't work.
It does, however, conform to NSCoding, so you can write it to an archive with your dictionary as the root object (assuming that all the other objects in the dictionary also conform).

Related

Can't write to custom .plist

Please consider the following code:
- (IBAction)testButton:(id)sender{
//create BFF with random id
BFF * testBff = [[BFF alloc]init];
testBff.relationType=#"BFF";
testBff.id= [NSNumber numberWithInt:(arc4random() % 100)];
testBff.handshake=[NSDate date];
//Encode the object
//Since a .plist doesn't take custom objects, I convert my object into type NSData*
NSData *encodedBFF = [NSKeyedArchiver archivedDataWithRootObject:testBff];
//Insert the data into the plist and save
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"myBFFS.plist"];
[data setObject: encodedBFF forKey:testBff.id];
[data writeToFile:path atomically:YES];
}
I would expect that every time I hit the button connected to this method, a new object would be added to myBFFs.plist residing in my documents folder.
-The Path seems to be fine
-The Variables Inspector indicates that the 'data' NSDictionary contains a key with a value of type NSMutableData
Somehow, however, nothing is written to a file.
If I change
[data setObject: encodedBFF forKey:testBff.id];
to
[data setObject: #"foo" forKey:#"bar"];
a file IS written. So apparently, my encoding attempts failed?
What am I doing wrong here?
I solved it as follows:
Apparently, the culprit was setting the key in this line:
[data setObject: encodedBFF forKey:testBff.id];
which I changed to
[myPlist setObject:encodedBFF forKey:[testBff.id stringValue]];

NSDictionary to NSDictionary formatted string and back

I am trying to convert two formats of NSDictionary formatted strings to and from each other.
Both strings:
<key>info</key><dict><key>title</key><string>The Title</string></dict>
and
info = { title = "The Title";}
convert to an NSDictionary if I use initWithData or initWithContentsofFile. When I use [NSDictionary description] to get a string from a dictionary, I get the second string.
So it is a one way conversion if I input the first string. Is there an easy way to convert the second string back to the first and also use them together in a file like below and convert that to NSDictionary? Thanks.
<dict>
dict1 contents...
</dict>
info ={dict2 contents...}
Solved my problem, many thanks to the comments.
To save in "human readable plist form"/"property list representation" (XML, etc) either use
NSDictionary's writeToFile: method. Doc link
or
NSPropertyListSerialization dataWithPropertyList: method. Doc link
NSError *error;
dataToBeStored = [NSPropertyListSerialization dataWithPropertyList:dictionaryToBeStored format:NSPropertyListXMLFormat_v1_0 options:0 error:&error];
if (data) {
NSFileManager* fileManager = [[NSFileManager alloc] init];
[fileManager createFileAtPath:fullPath contents:dataToBeStored attributes:nil];
[fileManager release];
}
Now if you open the saved file up and read the data and convert to string you get a nice human readable plist. Or you can also init an NSDictionary with it.

Write/Read NSMutableAttributedString to Plist

OK, here we are :
I've got an NSTextView
I'm getting it's NSMutableAttributedString content
I'm unable to read/write it to plist
By using Rob's code ( Saving custom attributes in NSAttributedString ), I've made some progress (I'm managing to write the data to disk), but I cannot recover it (= NSKeyedUnarchiver returns nil).
Encoding :
// where MAS --> NSMutableAttributedString
NSData* stringData = [NSKeyedArchiver archivedDataWithRootObject:MAS];
Decoding :
NSMutableAttributedString* mas = (NSMutableAttributedString*)[NSKeyedUnarchiver unarchiveObjectWithData:dat];
Any ideas? Any possible workaround (even if not with NSCoder, which I doubt it works with RTFs...) would be welcome!
And here's how it was solved.
NSAttributedString -> NSData :
NSData* j = [(NSMutableAttributedString*)MAS RTFDFromRange:NSMakeRange(0,[[MAS string] length])
documentAttributes:nil];
NSData -> NSAttributedString
NSMutableAttributedString* mas = [[NSMutableAttributedString alloc] initWithRTFD:dat
documentAttributes:nil];
Simple as that.

How to create JSONP on MacOS?

I use the following code to create a JSON file.
// Some data in keys and vals.
NSDictionary* dictionary = [NSDictionary dictionaryWithObjects:vals forKeys:keys];
NSError* writeError = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
options:NSJSONWritingPrettyPrinted error:&writeError];
NSString* path = #"json.txt";
[jsonData writeToFile:path atomically:YES];
How can I output a JSONP file? Is there a Cocoa framework I can use?
Update: In the meantime, I used a quick-and-dirty solution: I read in the JSON file just written before to the disc and add the missing JSONP-function to the string. Then, I write the file a second time. I think that's not worth being the answer to my question. So I will leave this question open to a smarter solution.
You could convert the JSON data to a string, wrap it in your function call and then write it to a file. Example:
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dictionary
options:NSJSONWritingPrettyPrinted
error:NULL];
NSMutableString *jsonString = [[[NSMutableString alloc] initWithData:jsonData
encoding:NSUTF8StringEncoding] autorelease];
[jsonString insertString:#"functionCall(" atIndex:0];
[jsonString appendString:#");"];
[jsonString writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
(I'm using a mutable string here for better memory efficiency.)
I don't know objective-c or Cocoa. (I use python on MacOS to create JSNOP responses), but it's a simple thing to do.
The basic idea is to wrap the JSON data in a javascript function call:
functionCall({"Name": "Foo", "Id" : 1234, "Rank": 7});
The tricky part is that the function name, "functionCall", is set by the browser and AFAIK the name of that query parameter is not standardized. jQuery uses jsonCallback. Other's use json or callback. So the request url must be checked for that callback name and that function name must be used to wrap the json data.

Attach version number to NSKeyedArchive

I'm using NSKeyedArchiver to write out an NSDictionary of my app's data to the filesystem.
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:myAppsDictionary forKey:kDataKey];
[archiver finishEncoding];
BOOL success = [data writeToFile:[self dataFilePath] atomically:YES];
Is there a simple way to attach a version number to this datafile without adding an object to the NSDictionary itself? Could I just insert a line before finishEncoding that would hold a key/value of the version number? Would encodeInt32:forKey: work? Or, is there a more Cocoa friendly way?
I believe your proposal is fine.
You might find this Apple document helpful: http://bit.ly/2T2cSg
Excerpt:
"You may just encode a “version” integer or string with some key or in some rare cases you may want a dictionary object full of goodies."