NSArray to NSData for Core Data - iphone

I need to save array in core data, so i read that I can use NSData for it. So I think that I have problem with archiving.
NSMutableArray *newArray = [[NSMutableArray alloc]init];
NSData *newData = [NSKeyedArchiver archivedDataWithRootObject:newArray];
[myEntity setValue:newData forKey:#"nameOfMyData"];
And then I try to pick my array in another VIewController for filling
NSData *newdata = [NSData dataWithData:self.myEntity.nameOfMyData];
NSMutableArray *photoArray = [[NSMutableArray alloc]init];
photoArray = [NSKeyedUnarchiver unarchiveObjectWithData:newdata];
I have no crash, but in command line appear next:
[NSKeyedUnarchiver initForReadingWithData:]: data is empty;
did you forget to send - finishEncoding to the NSKeyedArchiver?
And when i try to add object to my array, it does not add
[photoArray addObject:myImage];
So myImage is creating and with it I have no trouble, but in debugger always write for photoArray:
photoArray = (NSMutableArray*) 0x00000000 0 objects

It should work. But when unarchiving the array rather use:
NSData *newdata = [NSData dataWithData:self.myEntity.nameOfMyData];
NSMutableArray *photoArray = [NSMutableArray arrayWithArray: [NSKeyedUnarchiver unarchiveObjectWithData:newdata]];
You either have objects in your array that dont conform to NSCoding protocol or you do not save your core data context (or saving is unsuccessful).
After archiving with
NSData *newData = [NSKeyedArchiver archivedDataWithRootObject:newArray];
what do you see when you check newData for nil? I suppose it is nil.

Related

Objective-C - how to save and load an array containing images

Good evening everyone, I was hoping you could help with an Objective-C question I have.
In my app, I have a mutable array that contains 16 objects; the objects being images.
I would like to save and load the array so that the images are retained when the user quits the program.
I am new to data persistence and I can see there are several ways of saving and loading data and I am familiar with the NSUserDefaults method of saving and loading data. I am aware though that you cannot save arrays with images in this way.
Could someone please explain, perhaps with an example of the best and simplest way of saving and loading an array with images? Any help would be great as I'm unsure the best way to go with this.
Thanks everyone in advance!
Consider using NSKeyedArchiver.
// Archive
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:theArray];
NSString *path = #"/Users/Anne/Desktop/archive.dat";
[data writeToFile:path options:NSDataWritingAtomic error:nil];
// Unarchive
NSString *path = #"/Users/Anne/Desktop/archive.dat";
NSMutableArray *theArray = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
This way you can be sure the unarchived array is identical to the original.
All classes conforming to the NSCoding protocol can be used by NSKeyedArchiver.
Note: You can use any extension.
Response to comment:
The following should work on iOS:
// The Array
NSMutableArray * array = [[NSMutableArray alloc] init];
// Determine Path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [ [paths objectAtIndex:0] stringByAppendingPathComponent:#"archive.dat"];
// Archive Array
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
[data writeToFile:path options:NSDataWritingAtomic error:nil];
// Unarchive Array
NSMutableArray *theArray = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

Why NSMutableDictionary don't want write to file?

- (void)viewDidLoad
{
[super viewDidLoad];
if ([[NSFileManager defaultManager] fileExistsAtPath:pathString])
{
infoDict = [[NSMutableDictionary alloc] initWithContentsOfFile:pathString];
}
else
{
infoDict = [[NSMutableDictionary alloc]initWithObjects:[NSArray arrayWithObjects:#"BeginFrame",#"EndFrame", nil] forKeys:[NSArray arrayWithObjects:[NSNumber numberWithBool:YES],[NSNumber numberWithBool:YES], nil]];
if ([infoDict writeToFile:pathString atomically:YES])
{
NSLog(#"Created");
}
else
{
NSLog(#"Is not created");
NSLog(#"Path %#",pathString);
}
}
This is my code. I check if file is created, if not - I create a NSMutableDictionary and I write it to file at path, but writeToFile method returns NO. Where is problem? If I create this file with NSFileManager it works, but doesn't when I want to write a dictionary.
writeToFile:atomically only works if the dictionary you call it on is a valid property list object (see docs).
For a NSDictionary to be a valid property list object, among other things, its keys must be strings, but in your example the keys are NSNumber instances.
You can not control the content you are going to write sometimes. For example, you can't avoid a null value when you are going to write a JSON object that is gotten from a server.
NSData is compatible with these "invalid" values, so converting NSArray or NSDictionary to NSData is an ideal way in these cases.
write:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:jsonObject];
[data writeToFile:path atomically:YES];
read:
NSData *data = [NSData dataWithContentsOfFile:path];
NSDictionary *jsonObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];

iPhone:Convert NSMutableArray to NSData with stored & retrieved issue

I am facing a problem, that is to convert NSMutableArray into NSData and stored & retrieved that array into rootviewcontroller so please give me idea. Here is the code I'm working with:
appDelegate = (FindNearMeAppDelegate *)[[UIApplication sharedApplication]delegate];
appDelegate.isCategoryCell = TRUE;
appDelegate.isButton = NO;
NSString *categoryStr = categoryName.text;
NSLog(#"Category Name:--->%#",categoryStr);
appDelegate.categoryData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
categoryStr, #"name",image ,#"image", nil];
[appDelegate.categories addObject:appDelegate.categoryData];
NSLog(#"Category Data:--->%#",appDelegate.categories);
I am initialize nsmutablearray (appDelegate.categories) and nsmutabledictionary in appdelegate.
Thanks in advance.
From what I understand, you want to save your images and retrieve them. You can convert your image to NSData using one of the 2 methods, UIImagePNGRepresentation or UIImageJPEGRepresentation depending on the type of the image. You can then save this data-
NSData* imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:imagePath atomically:YES]; //imagePath is where you want to save it. Most likely it contains the value of the name key in your dictionary.

UITableView crashes on scroll when using XML plist

i've been reading for hours, searched apple's doc, stackoverflow, can't understand what i'm doing wrong....
when i use this data from a XML plist on my UITableViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"arrayofstrings"
ofType:#"plist"];
NSData *myData = [NSData dataWithContentsOfFile:path];
NSString *error;
NSPropertyListFormat format;
myArray = [NSPropertyListSerialization propertyListFromData:myData
mutabilityOption:NSPropertyListImmutable
format:&format
errorDescription:&error];
}
my tableview shows the first visible rows just fine but crashes when trying to scroll.
it doesn't happen when instead of the XML data i use something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
myArray = [[NSArray alloc] initWithObjects:#"thing1", #"thing2", #"thing3", #"thing4", #"thing5",#"thing6", #"thing7", #"thing8", #"thing9", #"thing10",
#"thing11",#"thing12", #"thing13", #"thing14", nil];
}
this way the tableview scrolls just fine. what's my problem?! Is the plist conversion to array supposed to be in any other way?
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"arrayofstrings"
ofType:#"plist"];
NSData *myData = [NSData dataWithContentsOfFile:path];
NSString *error;
NSPropertyListFormat format;
myArray = [[NSPropertyListSerialization propertyListFromData:myData
mutabilityOption:NSPropertyListImmutable
format:&format
errorDescription:&error] retain];
}
The return value from propertyListFromData:mutabilityOption:format:errorDescription is autoreleased. Make sure you call retain so it doesn't get released out from under you at the end of the current run loop.
The second method works because creating the NSArray with alloc/init leaves the array with a retain count of 1.
The problem is that in the first case your call to [NSPropertyListSerialization propertyListFromData: returns an NSArray with no retain count on it (note the method doesn't have alloc, new, or copy in the name) - and then you don't retain this NSArray. Hence, the array is getting deallocated shortly after, and your code crashes trying to access garbage memory.
In the second case, you are making an NSArray using alloc - this returns an NSArray with a retain count of 1, which means it isn't deallocated (until a release gets called at some point).
To fix this, in your first case you want to assign the array as follows:
self.myArray = ...
The self. is the crucial part here (assuming you have declared the myArray property as retain).
There's plenty of resources and blog posts available concerning memory management.

How to convert NSArray to NSData?

Can anyone tell me how to convert an NSArray to an NSData? I have an NSArray. I need to send it to an NSInputStream. In order to do that I need to convert the NSArray to an NSData.
Please help me, I'm stuck here.
Use NSKeyedArchiver (which is the last sentence of the post Garrett links):
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
Note that all the objects in array must conform to the NSCoding protocol. If these are custom objects, then that means you need to read up on Encoding and Decoding Objects.
Note that this will create a fairly hard-to-read property list format, but can handle a very wide range of objects. If you have a very simple array (strings for instance), you may want to use NSPropertyListSerialization, which creates a bit simpler property list:
NSString *error;
NSData *data = [NSPropertyListSerialization dataFromPropertyList:array format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error];
There's also an XML format constant you can pass if you'd rather it be readable on the wire.
On a somewhat related note, here's how you would convert the NSData back to an NSArray:
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:data]
I used this code.
NSError *error;
NSMutableData *jsonData = [[NSJSONSerialization dataWithJSONObject:yourDemoArray
options:0 // Pass 0 if you don't care about the readability of the generated string
error:&error] copy];
Swift :
let data = NSKeyedArchiver.archivedData(withRootObject: jsonArray)
print(data)
You can do this-
NSArray *array= [NSArray array];
NSData *dataArray = [NSKeyedArchiver archivedDataWithRootObject:array];
In iOS 9+ use this please:
NSArray *array = [[NSArray alloc] init];
NSData *data = [NSPropertyListSerialization dataWithPropertyList:array format:NSPropertyListBinaryFormat_v1_0 options:0 error:nil];
The older version of this was deprecated in iOS 8.
Swift 5
let data = try! NSKeyedArchiver.archivedData(withRootObject: array, requiringSecureCoding: true)