is it possible to save NSMutableArray or NSDictionary data as file in iOS?
To write in file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:FILE_NAME];
[myArray writeToFile:filePath atomically:YES];
To read from file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:FILE_NAME];
myArray = [NSArray arrayWithContentsOfFile:filePath];
myArray will be nil if file is not present. And NSDictionary has similar methods. Please check the reference for the details of these methods.
Is there any restrictions on save NSDictionary to file
In order for NSDictionary
to be successfully saved all the objects it holds have to conform to NSCoding protocol.
In short that means that you have to implement
- (void)encodeWithCoder:(NSCoder *)encoder
and
- (id)initWithCoder:(NSCoder *)decoder
for all your custom classes.
A nice tutorial is here: http://www.raywenderlich.com/1914/nscoding-tutorial-for-ios-how-to-save-your-app-data
EDIT:
Once you have written you NSCoder
methods you can save data like this:
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:self];
[archiver finishEncoding];
[data writeToFile:file atomically:YES];
And init the object from file like this:
NSData *data = [[NSData alloc] initWithContentsOfFile:file];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
self = [unarchiver decodeObject];
[unarchiver finishDecoding];
You did mention having problems with json object: the thing about json object is that first you have to check its type: it can be a NSDictionary
or a NSArray
- depending on the input and format.
Common problem occours when you get an array with only one element - dictionary you are looking for. This happens if your dictionary {...}
json representation is embedded within []
.
How to store Value of NSDictionary to NSMutableArray in loop?
You need to create a new NSMutableDictionary *dRow on every iteration:
- (NSDictionary *) getPreparedAllRow
{
//NSMutableDictionary *dRow=[[NSMutableDictionary alloc]init];
NSMutableArray *data=[[NSMutableArray alloc]init];
int counter=0;
while(sqlite3_step(statment) == SQLITE_ROW)
{
int Column_Count = sqlite3_column_count(statment);
NSMutableDictionary *dRow=[[NSMutableDictionary alloc]init];
if(Column_Count >= 1)
{
for(int count =0 ; count < Column_Count ; count++)
{
[dRow setObject:[self columnValue:count] forKey:@(sqlite3_column_name(statment, count))];
//dRow [ @(sqlite3_column_name(statment, count))] = [self columnValue:count];
}
}
NSLog(@"%@",dRow);
[data insertObject:dRow atIndex:counter];
counter++;
NSLog(@"%@",data);
}
NSLog(@"%@",data);
return dRow;
}
And I think that you want to return the NSMutableArray*?? maybe you have an error there too...
Save NSMutableArray in iOS
If your array contains non-plist objects, then you cannot use NSUserDefaults
without first encoding the array.
The value parameter can be only property list objects: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and NSDictionary objects, their contents must be property list objects.
You'll want to encode it using NSKeyedArchiver
. This will give you an NSData
object that you can then store in NSUserDefaults
, or write it to file through NSKeyedArchiver
itself.
All you have to do is conform to NSCoding
in your custom object, and override initWithCoder:
to initialise your object when it's loaded and encodeWithCoder:
to encode your variables when it gets encoded. For example, your custom object will look something like this:
@interface customArrayObject : NSObject <NSCoding>
@property (nonatomic) NSString* foo;
@property (nonatomic) NSInteger bar;
@end
@implementation customArrayObject
-(instancetype) initWithCoder:(NSCoder *)aDecoder { // decode variables
if (self = [super init]) {
_foo = [aDecoder decodeObjectForKey:@"foo"];
_bar = [aDecoder decodeIntegerForKey:@"bar"];
}
return self;
}
-(void) encodeWithCoder:(NSCoder *)aCoder { // encode variables
[aCoder encodeObject:_foo forKey:@"foo"];
[aCoder encodeInteger:_bar forKey:@"bar"];
}
@end
It's also worth noting that NSUserDefaults
is used to store user preferences, and therefore if your array contains data that isn't in any way to do with a user preference, you shouldn't be using NSUserDefaults
- you should be writing it to disk yourself.
Writing your array to disk is actually a lot more trivial than it sounds, you can use the archiveRootObject:toFile:
method on NSKeyedArchiver
. For example, this will write your custom array to the documents directory:
// Gets the documents directory path
NSString* documentsPath = NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES)[0];
// Archive and save the file to foo.dat in the documents directory. Returns whether the operation was successful.
BOOL success = [NSKeyedArchiver archiveRootObject:customArray toFile:[NSString stringWithFormat:@"%@/%@", documentsPath, @"foo.dat"]]
However, it is also worth noting that this flat (yes/no) as to whether the operation was successful isn't that great when it comes to error handling. If you want to implement custom error handling, then you'll want to first encode the object using NSKeyedArchiver
's archivedDataWithRootObject:
method, and then NSData
's writeToFile:options:error:
method.
Saving NSMutableArray to the iPhone Documents Directory
You can not save your array of objects because objects are not NSString, NSData, NSArray, or NSDictionary.You could rather use NSKeyArchiver and NSKeyUnArchiver
For example:#import "Foundation/Foundation.h"
@interface Dog : NSObject {**NSCoding**}//your class must conform to NSCoding Protocol
@property (retain) NSString *Name;
@end
The implementation needs some additional code. We need to implement the NSCoding protocol, which means
two additional methods. (initWithCoder: and encodeWithCoder:)#import "Dog.h"
@implementation Dog
@synthesize Name;
-(id)initWithCoder:(NSCoder*)decoder{
if ((self = [super init])) {
Name = [decoder decodeObjectForKey:@"Name"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder{
[encoder encodeObject:Name forKey:@"Name"];
}
Once we implement the protocol, saving will look like this:
// Save method
// We initialise our object and set the values
Dog *dog1 = [[Dog alloc] init];
dog1.Name= @"Dog1";
Dog *dog2 = [[Dog alloc] init];
dog2.Name= @"Dog2";
Dog *dog3 = [[Dog alloc] init];
dog3.Name= @"Dog3";
NSMutableArray *arrayDogs = [NSMutableArray array];
[arrayDogs addObject: dog1];
[arrayDogs addObject: dog2];
[arrayDogs addObject: dog3];
//Sorts the array in alphabetical order according to name – compareDogNames: is defined in the Dog class
arrayDogs = (NSMutableArray *)[arrayDogs sortedArrayUsingSelector:@selector(compareDogNames:)];
// Store the array
[NSKeyedArchiver archiveRootObject:arrayDogs toFile:dogFilePath];
//load the array*
NSMutableArray* retreivedADogObjs = [NSKeyedUnarchiver unarchiveObjectWithFile:dogFilePath];
@end
Hope it will help you
Happy to help.*
Store Empty NSMutableArray in NSDictionary
There's nothing wrong with storing an empty array in a dictionary.
For example:
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
NSMutableArray *array = [NSMutableArray array];
dict[@"array"] = array;
Storing a nil pointer that's declared as a pointer to an array would be a problem, however.
How to save NSMutablearray in NSUserDefaults
Note: NSUserDefaults will always return an immutable version of the object you pass in.
To store the information:
// Get the standardUserDefaults object, store your UITableView data array against a key, synchronize the defaults
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:arrayOfImage forKey:@"tableViewDataImage"];
[userDefaults setObject:arrayOfText forKey:@"tableViewDataText"];
[userDefaults synchronize];
To retrieve the information:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSArray *arrayOfImages = [userDefaults objectForKey:@"tableViewDataImage"];
NSArray *arrayOfText = [userDefaults objectForKey:@"tableViewDataText"];
// Use 'yourArray' to repopulate your UITableView
On first load, check whether the result that comes back from NSUserDefaults
is nil
, if it is, you need to create your data, otherwise load the data from NSUserDefaults
and your UITableView
will maintain state.
Update
In Swift-3, the following approach can be used:
let userDefaults = UserDefaults.standard
userDefaults.set(arrayOfImage, forKey:"tableViewDataImage")
userDefaults.set(arrayOfText, forKey:"tableViewDataText")
userDefaults.synchronize()
var arrayOfImages = userDefaults.object(forKey: "tableViewDataImage")
var arrayOfText = userDefaults.object(forKey: "tableViewDataText")
Related Topics
How to Store Push Notification Alert Message in Userdefault
Programmatically Get a Storyboard Id
Uitableview with Two Custom Cells (Multiple Identifiers)
Fade Out Scrolling Uitextview Over Image
Ios5 Images Disappear When Scrolling with Webkit-Overflow-Scrolling: Touch
Xcode 5 Crashes -- Xcode Quit Unexpectedly
Convert Swiftui View to PDF on iOS
Repeating Local Notification Daily at a Set Time with Swift
Swift - Downloading Video with Downloadtaskwithurl
"Cannot Connect to Itunes Store" In-App Purchases
How to Detect If iOS App Is Running in UI Testing Mode
How to Verify That I am Running on a Given Gcd Queue Without Using Dispatch_Get_Current_Queue()
Proper Use of Loadview and Viewdidload with Uiviewcontroller Without Nibs/Xibs
How to Programmatically Increase Uitableview Cell's Height in Iphone
Rotate a Uiview Around Its Center But Several Times