I got some static data, not too much, maybe 50-60 counts, which is necessary for view's content, so I have to preload them into my app. The data is not plain, not appropriate for property list,and it is too small so I'm not sure if core data is fit. Any suggestion?
I think you can convert them into NSData then save into a file.
For example:
//Write
NSArray * array = #[#"1",#"2",#"3",#"4"];
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:array];
[data writeToFile:yourpath atomically:YES];
//Read
NSData * data = [NSData dataWithContentsOfFile:writeToFile:yourpath];
NSArray * array = [NSKeyedUnarchiver unarchiveObjectWithData:data];
Related
I want to store in a NSMutableDictionary some null values using NSNull and then serialize this data using a property list. Problem is NSNull is not allowed and I get "Property list is in invalid format" error. Is there a workaround for this? Seems very surprising they didn't put NSNull on the list of valid plist objects.
I convert NSArray or NSDictionary to NSData before serializing. Following is a category on nsarray for serializing and deserializing. This comfortableby handles some data being nsnull
#implementation NSArray(Plist)
-(BOOL)writeToPlistFile:(NSString*)filename{
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:self];
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
NSString * path = [documentsDirectory stringByAppendingPathComponent:filename];
BOOL didWriteSuccessfull = [data writeToFile:path atomically:YES];
return didWriteSuccessfull;
}
+(NSArray*)readFromPlistFile:(NSString*)filename{
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
NSString * path = [documentsDirectory stringByAppendingPathComponent:filename];
NSData * data = [NSData dataWithContentsOfFile:path];
return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
Instead of using a property list, consider using an NSKeyedArchiver and NSKeyedUnarchiver. Properties lists have a fixed set of types, and that doesn't include NSNull, and it isn't meant to be extended.
Here are links to the relevant documentation: NSCoding, NSKeyedArchiver and NSKeyedUnarchiver.
No, there is no way to put NSNull in a property list. (Trivia: the binary property list format actually supports nulls, but the writer doesn’t implement support.)
For a dictionary, the easiest workaround is to simply filter out the null values, so { foo = "bar"; baz = null; } becomes { foo = "bar"; }.
From the documentation of NSPropertyListSerializationClass:
A property list object. plist must be
a kind of NSData, NSString, NSNumber,
NSDate, NSArray, or NSDictionary
object. Container objects must also
contain only these kinds of objects.
So you need to have one of those. Depending on the type of data, you could put in a placeholder instead of NSNull and then do a process before/after loading the .plist (like, for example, using a zero-length NSData object to represent your NSNull in the plist). Exactly what kind of placeholder would be dependent on what kind of data you are storing, and choosing something to avoid. Then, after loading, translate the empty NSData back to NSNull.
I need to save unsigned char array to NSPasteBoard and then read it. How I can do it? I tried to save it to NSString, but this working only with ASCII codes.
Try this:
char myChars[] = "This is a test";
NSData *charsData = [NSData dataWithBytes:myChars length:strlen(myChars)];
[[NSPasteboard generalPasteboard] clearContents];
[[NSPasteboard generalPasteboard] setData:charsData forType:NSPasteboardTypeString];
NSData *data = [[NSPasteboard generalPasteboard] dataForType:NSPasteboardTypeString];
char myChars2[data.length];
[data getBytes:myChars2 length:data.length];
NSLog(#"%s", myChars2);
The question is a bit unclear. Do your application use pasteboard to communicate with other apps or just to store data between launches?
1. Your application needs to store binary data for itself between launches.
I suggest you to use NSUserDefaults for that purpose. As it said in docs, it's a database so you may achieve data even in next application launch.
static const NSString *kCharsDefaultsKey = #"kCharsDefaultsKey";
- (void)saveChars:(unsigned char *)chars length:(size_t)length
{
NSData *data = [NSData dataWithBytes:chars length:length];
NSUserDefaults *defaults = [NSUserDefaults standartDefaults];
[defaults setValue:data forKey:kCharsDefaultsKey];
[defaults synchronize];
}
- (void)getChars:(unsigned char *)chars length:(size_t)length
{
NSUserDefaults *defaults = [NSUserDefaults standartDefaults];
NSData *data = [default valueForKey:kCharsDefaultsKey];
if(data)
{
[data getBytes:chars];
}
}
2. Your application needs to push data between some apps.
The pasteboard-mechanism can be a way to establish this kind of communication. But I strongly recommend you to choose another way. You can't be sure there isn't another data inside the pasteboard.
- (void)someMethod
{
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
NSArray *contents = [pasteboard readObjectsForClasses:#[[NSData class]] options: nil];
// how should you distinguish which object contains your chars and which doesn't?
}
I would rather recommend you to use NSConnection.
Also please take a look at this great article.
How can I save NSData into sqlite, I am using FMDB wrapper for saving data.
Below is the code which I have tried so far
For saving
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:model.expertArray];;
NSString *query = [NSString stringWithFormat:#"insert into save_article values ('%#','%#','%#','%#','%#','%#')",
model.Id, model.title, model.earliestKnownDate, data, model.excerpt,model.image_url];
For Retriving
while([results next]) {
NSData *data = [results dataForColumn:#"experts"];
NSMutableArray *photoArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
I have tried both datatype blob & text but no luck so far, can anybody guide me how to do it?
Below is the Snippet for all who may face the same issue while inserting NSData to Sqlite using FMDB.
In my Case I wanted to store NSArray in Sqlite So i first convert the NSArray into NSData & then store it in Sqlite.
Converting NSArray into NSData
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:YourNSarray];;
Saving NSData into Sqlite
[database executeQuery:#"insert into save_article values (?,?,?,?,?,?)", model.Id, model.title, model.earliestKnownDate, data, model.excerpt,model.image_url];
Note:- Don't build a query using stringWithFormat[below is the code which you should not use]:. Thanks to #rmaddy & #hotlicks for pointing me to this :)
NSString *query = [NSString stringWithFormat:#"insert into user values ('%#', %d)",
#"brandontreb", 25];
[database executeUpdate:query];
and now the last step i.e retrieving NSData back to NSArray
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:[database dataForColumn:#"yourcololumname"]];
Hope this will help the needy & beginner :)
Don't build a query using stringWithFormat:. This is a bad idea for several reasons.
Use the executeQuery method where you put a ? for each value to be bound to the query.
Something like this:
[database executeQuery:#"insert into save_article values (?,?,?,?,?,?)", model.Id, model.title, model.earliestKnownDate, data, model.excerpt,model.image_url];
I have a NSMutableArray and a NSString . These two are archived to NSData and add to a NSMutableData Object.
How can I access each data from NSMutableData Object.
NSData *dataArray= [NSKeyedArchiver archivedDataWithRootObject:mutableArray];
NSData *dataTouchedNumer=[NSKeyedArchiver archivedDataWithRootObject:stringValue];
NSMutableData *mutableData=[[NSMutableData alloc]init];
[mutableData appendData:dataArray];
[mutableData appendData:dataTouchedNumer];
You can't do this the way you are showing. If you append two NSData objects together into a single mutable data object, there is no way to separate them later. Try this instead:
To archive the two objects:
NSMutableArray *mutableArray = ... // your mutable array
NSString *stringValue = ... // your string
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:mutableArray forKey:#"array"];
[archiver encodeObject:stringValue forKey:#"string"];
At this point, data contains the two objects. Do what you need with the data (save it for example).
To get your objects back:
NSData *data = ... // the archived data
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
NSMutableArray *mutableArray = [unarchiver decodeObjectForKey:#"array"];
NSString *stringValue = [unarchiver decodeObjectForKey:#"string"];
According to the docs, "archivedDataWithRootObject: returns an NSData object containing the encoded form of the object graph whose root object is given." So your mutableData object contains 2 such encoded object graphs. The question is what kind of data you want to read out of mutableData. It probably does not make much sense, to read simply all bytes with [mutableData bytes], or part of it with getBytes:length: or getBytes:range:.
I have been working on this problem for a while, I have an app running on the mac, it has co-ordinate data stored in a struct like this:
struct xyz {
float x;
float y;
float z;
};
struct xy {
float x;
float y;
};
struct object {
struct xyz *myXYZ;
struct xy *myXY;
};
This all works as expected, then I add the struct into NSData like so:
struct object anInitialTestStruct;
NSMutableData *myTestDataOut = [NSMutableData dataWithBytes:&anInitialTestStruct length:64 freeWhenDone:NO];
BOOL good = [myTestDataOut writeToFile:[NSString stringWithFormat:#"%#/filename.dat", docsDirectory] atomically:YES];
This works as expected, I get a file and looks like there is data in it (for reference I have used pointers and malloc for the anInitialTestStruct but still don't get the desired result)
Now on the iphone, I copy the file into the project, and do this:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"dat"];
NSData *myVecNSData = [[NSData alloc] initWithContentsOfFile:filePath options:NSDataReadingUncached error:&error];
if ( error ) {
NSLog(#"%#", error);
}
I don't get the correct data back. Interestingly if I run the initWithContents method on the mac and read the file in there it appears to be ok.
So I'm thinking there is something different on the iphone / mac way it deals with the filesystem.... I've tried encoding the data using NSKeyedArchiver, but I get an exception stating "incomprehensible archive....."
For case of your "object" structure you have to store "xy" and "xyz" structures separately, for example in a dictionary:
struct object anInitialTestStruct;
NSDictionary *structureDataAsDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[NSMutableData dataWithBytes:anInitialTestStruct.myXY length:sizeof(xy)], #"xy key",
[NSMutableData dataWithBytes:anInitialTestStruct.myXYZ length:sizeof(xyz)], #"xyz key",
nil];
NSData *myTestDataOut = [NSKeyedArchiver archivedDataWithRootObject:structureDataAsDictionary];
BOOL good = [myTestDataOut writeToFile:[NSString stringWithFormat:#"%#/filename.dat", docsDirectory] atomically:YES];
and decoding is something like this:
struct object anInitialTestStruct;
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"dat"];
NSData *myVecNSData = [[NSData alloc] initWithContentsOfFile:filePath options:NSDataReadingUncached error:&error];
if ( error ) {
NSLog(#"%#", error);
}
// retrieving dictionary from NSData
NSDictionary *structureDataAsDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:myVecNSData];
// allocating memory for myXY and myXYZ fields
anInitialTestStruct.myXY = (xy*)malloc(sizeof(xy));
if (anInitialTestStruct.myXY == NULL) {
// error handling
}
anInitialTestStruct.myXYZ = (xyz*)malloc(sizeof(xyz));
if (anInitialTestStruct.myXYZ == NULL) {
// error handling
}
// filling myXY and myXYZ fields with read data
[[structureDataAsDictionary objectForKey:#"xy key"] getBytes:anInitialTestStruct.myXY];
[[structureDataAsDictionary objectForKey:#"xyz key"] getBytes:anInitialTestStruct.myXYZ];
You might have truble encoding your pointers see here
"Pointers
You can’t encode a pointer and get back something useful at decode time. You have to encode the information to which the pointer is pointing. This is true in non-keyed coding as well. ..."