I have used NSuserDefaults and NSkeyedArchive before but i dont think it will work for my new project..
I get data back from JSON and store it in an array (name,age,country) (all NSString)
i want to make a save button in the detail view so that it saves that person's data.
And show the saved data in another tableview. (for loop on the array and get all objects back)
How should i handle this in a easy way.. i except max 40 stored names so its not so heavy..
So in short i want a function like you see in "home app's" where you can "favorite/store a house"
-- Update
viewDidLoad
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docDir stringByAppendingPathComponent:#"Names.plist"];
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
arrayWithNames = [[NSMutableArray alloc]init];
[arrayWithNames addObjectsFromArray:array];
Savebutton
NSMutableArray *nameInfo = [[NSMutableArray alloc]initWithObjects:self.name,self.age,self.country, nil];
[arrayWithNames addObjectsFromArray:nameInfo];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Names.plist"];
[arrayWithNames writeToFile:path atomically:YES];
This works but i get all data together instead of every array as an independent object
btw i made sure there cant be a NULL :)
As long as all of the data is NSString values, as you say, you can just use writeToFile:atomically: to save an array to a file. However, JSON sometimes contains nulls, which aren't compatible with that method. If you try to use that method when nulls are present, it will throw an exception. If there's any chance of nulls (and there almost always is a chance), you'll need to take some precautions. A couple of possibilities:
Make mutable copies of your data, run through it, and remove nulls or replace them with something else (like an empty string).
Convert the data back to JSON via [NSJSONSerialization dataWithJSONObject:options:error:] and then write the resulting NSData to a file.
I not quite understand your question.
But in your case what I did was create a Model with the structure of information I intended to store (in your case looked Person) and created an array in which i will add the objects Person
Could use several cases to save, but in my opinion, the simplest would be through the NSUserDefaults (the solution depends heavily on your database).
Soo, you will have the model Person
import <Foundation/Foundation.h>
#interface Person : NSObject
#property(nonatomic,strong) NSString *name;
#property(nonatomic,strong) NSString *country;
#property(nonatomic,strong) NSString *age;
...
With the methods for the encryption:
- (void)encodeWithCoder:(NSCoder *)encoder {
//Encode properties, other class variables, etc
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeObject:self.age forKey:#"age"];
[encoder encodeObject:self.country forKey:#"country"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super init])) {
//decode properties, other class vars
self.name = [decoder decodeObjectForKey:#"name "];
self.age = [decoder decodeObjectForKey:#"age"];
self.country = [decoder decodeObjectForKey:#"country"];
}
return self;
}
Then you create a NSMutableArray where you add your objects.
[arrayPeople addObject:person];
When you decide to store in your application data you can do this:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *myEncodedObjectShopping = [NSKeyedArchiver archivedDataWithRootObject:arrayPeople];
[defaults setObject:myEncodedObjectShopping forKey:#"people"];
To retrive the data:
NSData *myDecodedObject = [defaults objectForKey:#"people"];
NSMutableArray *decodedArray =[NSKeyedUnarchiver unarchiveObjectWithData: myDecodedObject];
Related
I am having problem with creating the Plist File that saves data received from web service using JSON. The Plist File cannot be created. The path is empty and data is saved to nowhere. This problem occurred when I cleaned the derived data. Please suggest any solution for this.
JSON data:
eventID = 2356;
eventName = "Testing Event";
This is how I save in Plist:
NSArray *eventsDictionary = [dataDictionary objectForKey:#"eventList"];
NSDictionary *plistDict = [NSDictionary dictionaryWithObject:eventsDictionary
forKey:#"Events"];
if ([eventsDictionary count]==0) {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
path = [path stringByAppendingPathComponent:DATA_DICTIONARY_KEY_USER_DEFAULTS];
[plistDict writeToFile:path atomically:YES];
}
else {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
path = [path stringByAppendingPathComponent:DATA_DICTIONARY_KEY_USER_DEFAULTS];
[plistDict writeToFile:path atomically:YES];
}
Thank you very much.
You mentioned in a comment that the data was obtained from a web service as JSON and then converted to a dictionary.
The problem is almost certainly that your JSON data contains null values, which means that your dictionary contains instances of NSNull. You cannot write NSNull to a file like this because it is not one of the property list types. Writing a file like this only works with instances of NSDictionary, NSArray, NSString, NSData, NSNumber, and NSDate. Also any dictionary keys must be strings.
If there's an NSNull (or an instance of any non-property list class) then writing the file like this will fail.
You need to either go through the data and remove all NSNull instances, or else write your file some other way.
Here is the simplest way:
Add your dict to array:
NSMutableArray * mutArr = [NSMutableArray alloc]init];
[mutArr addObject:plistDict];
[self saveToPlistName:#"yourplist" fromArray:mutArr];
and use this method
-(void)saveToPlistName:(NSString *)plistName fromArray:(NSMutableArray*)array
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [path objectAtIndex:0];
NSString *filePath = [documentFolder stringByAppendingFormat:[NSString stringWithFormat:#"/%#.plist",plistName]];
[array writeToFile:filePath atomically:YES];
NSLog(#"SAVE SUCCESS TO DIRECTORY%#",filePath);
}
vote for me if its helpful for you
I am developing iPhone app in which i have TableView,
What i want to do is On click of UITableViewCell i want to remove this values from plist, MOBILES.Brand.2 and MOBILES.Brand.5 if they exist in .plist if they doesn't exist in .plist then i want to add it to my .plist
Here is the structure of my .plist:
:
Here is my code snippet:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
plistPath = [self getPlistPath];
//PlistDict is mutableDictionary contains the keys and values of .plist (of above image)
if ([PlistDict valueForKeyPath:#"MOBILES.Brand.2"] != nil) { //if key and it's value exists in Filter.plist
//Delete Key from .plist...
NSMutableDictionary *savedStock = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
[savedStock removeObjectForKey:#"MOBILES.Brand.2"];
[savedStock removeObjectForKey:#"MOBILES.Brand.5"];
[savedStock writeToFile:plistPath atomically:YES];
}else{
//Add Key to .plist
NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
[data setValue:#"251" forKeyPath:#"MOBILES.Brand.2"];
[data setValue:#"298" forKeyPath:#"MOBILES.Brand.5"];
[data writeToFile:plistPath atomically:YES];
}
}
-(NSString*)getPlistPath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Filter.plist"];
PlistDict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
return path;
}
after writing above code snippet it doesn't remove the keys from .plist but it adds the key successfully if not existed.
Where i am doing mistake ? please help and thanks in advance.
The problem is you are trying to remove using the NSMutableDictionary keypath directly using removeObjectForKey that requires the exact key not keypath and you are supplying a keypath. Use the following NSMutableDictionary Category taken from here
#interface NSMutableDictionary (Additions)
- (void)removeObjectForKeyPath: (NSString *)keyPath;
#end
#implementation NSMutableDictionary (Additions)
- (void)removeObjectForKeyPath: (NSString *)keyPath
{
// Separate the key path
NSArray * keyPathElements = [keyPath componentsSeparatedByString:#"."];
// Drop the last element and rejoin the path
NSUInteger numElements = [keyPathElements count];
NSString * keyPathHead = [[keyPathElements subarrayWithRange:(NSRange){0, numElements - 1}] componentsJoinedByString:#"."];
// Get the mutable dictionary represented by the path minus that last element
NSMutableDictionary * tailContainer = [self valueForKeyPath:keyPathHead];
// Remove the object represented by the last element
[tailContainer removeObjectForKey:[keyPathElements lastObject]];
}
#end
It should work for you.
Method - 2
If the above doesn't work, you can do an iteration of your dictionary, and specifically delete the object for the key.. Try
NSMutableDictionary *savedStock = [[NSMutableDictionary alloc] initWithContentsOfFile:plistPath];
NSMutableDictionary *brand=[[savedStock objectForKey:#"MOBILES"] objectForKey:#"Brand"];
[brand removeObjectForKey:#"2"];
[brand removeObjectForKey:#"5"];
Cheers.
After much searching of SO I've been able to come up with code to correct previous failures. What I am trying to accomplish is accepting user input from a UITextField and add that input to my TableView, which is populated from a plist. The root value of my plist is dictionary and I would like to keep it as such. My problem is when writeToFile is called, I'm completely over writing the existing values instead of inserting, adding or appending to the existing data. It doesn't appear that my attempt to combine the two dictionaries is working, since only the new value is being stored to the plist. Any insight as to where I'm going wrong? The following code is what I have
//////////********** Add New Cell When OK is Chosen
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
UITextField *newDevice = [alertView textFieldAtIndex:0];
NSLog(#"newDevice - %#",newDevice.text);
if (alertView.tag == 1 && buttonIndex == 1){
NSMutableDictionary *input = [[NSMutableDictionary alloc]init];
[input setObject:[NSArray arrayWithObject:newDevice.text] forKey:#"Room"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Test.plist"];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
// Combine the two Dictionaries to create one
[dictionary addEntriesFromDictionary:input];
// Write Combined Dictionary to plist
[dictionary writeToFile:path atomically:YES];
// Add Newly Created text to Table because reload table doesn't do it
[myTableData addObject:newDevice.text];
// Reload Table Data even though it seems useless
[myTable reloadData];
}
}
I guess you overwrote the values of the key Room.
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Test.plist"];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
// Get exist input from plist
NSMutableArray *inputFromPlist = dictionary[#"Room"];
// add new object to input
[inputFromPlist addObject:newDevice.text];
// set Room with modified object
[dictionary setObject:inputFromPlist forKey:#"Room"];
// Write Combined Dictionary to plist
[dictionary writeToFile:path atomically:YES];
// do something after saving plist ...
I've already made an app that compares the user's location to an array of locations with format...
NSArray *myStationArray;
myStationArray = #[
#{
kStation : #"27200",
kLatitude : #( 41.656467),
kLongitude : #(-81.277963)
},
#{
kStation : #"27650",
kLatitude : #(41.657118),
kLongitude : #(-81.276545)
},
...
Now I want the user to be able to create this array dynamically where they can select an IBAction button that will add their current location (lat long) to a mutable array with the format shown. The kStation value will be inputted by the user. I know conceptual questions are frowned upon so I'll offer these specific questions based on this idea.
1.) Where should I create the mutable array (viewDidLoad, IBAction, didUpdateToLocation, ect.)? It will need to be saved to the app so the user can add and delete from it throughout its life.
2.) What will my IBAction code look like to save the current location and kStation input to create an array to match the example above?
3.) Is this something where you'd want to use a database such as sqlite? I'd prefer to avoid using a database if possible but would like to know the best method of doing this.
Thank you for your time in advance.
You would probably define this NSMutableArray for your list as a class property:
#property (nonatomic, strong) NSMutableArray *stations;
While you're at it, you could define a NSString property for your filename:
#property (nonatomic, strong) NSString *filename;
You could load the previous values or initialize it in viewDidLoad, where kFilename is set to #"stations.plist" or something like that:
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
self.filename = [docsPath stringByAppendingPathComponent:kFilename];
if ([[NSFileManager defaultManager] fileExistsAtPath:self.filename])
self.stations = [NSMutableArray arrayWithContentsOfFile:self.filename];
else
self.stations = [NSMutableArray array];
You could add new values in your IBAction (if you want them to be added when the user clicks on the button) or in didUpdateToLocation (if you want it to be added automatically as the user moves). For example, if you did it in IBAction:
- (IBAction)didTouchUpInsideAddButton:(id)sender
{
NSDictionary *newItem = #{
kStation : ..., // grab your station identifier however you want to do that
kLatitude : #(self.locationManager.location.coordinate.latitude),
kLongitude : #(self.locationManager.location.coordinate.longitude)
};
[self.stations addObject:newItem];
[self.stations writeToFile:self.filename atomically:YES];
}
If your list of stations is likely to get large, then you might want to consider using Core Data or SQLite, but if this list is likely to be reasonably short (i.e. something that you don't mind loading the whole thing into memory at the same time), the above technique of using plists via arrayWithContentsOfFile and writeToFile is probably sufficient.
To answer your questions in order:
1) You would probably want to create and initialize your NSMutableArray in viewDidLoad.
2) You could either create your own class, something similar like MKAnnotation format or create a dictionary and then stick that into an array like so:
-(IBAction)myButtonAction:(id)sender {
NSDictionary *myDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:#"Station", #"27200", #"Latitude", #"41.656467", #"Longitude", #"-81.277963", nil];
[myArray addObject:myDictionary];
}
3) You can load your MutableArray file like this:
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSMutableString *documentDirectory = [documentDirectories objectAtIndex:0];
NSString *myPath = [documentDirectory stringByAppendingPathComponent:#"UserData"];
NSMutableArray *myArray = [[NSMutableArray alloc] initWithContentsOfFile:myPath];
You can save your MutableArray file like this:
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSMutableString *documentDirectory = [documentDirectories objectAtIndex:0];
NSString *filePath = [documentDirectory stringByAppendingPathComponent:#"UserData"];
BOOL fileError = [myArray writeToFile:filePath atomically:YES];
I am trying to save data to the plist in my app per person, but it save it to everyone, so for example if I have a field for eye color and I enter Brown in the textfield for John Doe, it save Brown for everyone else as well, is there a way to save this info per person in the app instead using plist? I have had no luck trying:
-(NSString *)pathofFile{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsfolder = [paths objectAtIndex:0];
return [docsfolder stringByAppendingFormat:#"data.plist"];
}
Here is the code I have in my view did load:
- (void)viewDidLoad {
[self loadPerson];
[super viewDidLoad];
NSString *filepath = [self pathofFile];
if ([[NSFileManager defaultManager]fileExistsAtPath:filepath]) {
NSArray *array = [[NSArray alloc]initWithContentsOfFile:filepath];
Drinfo.text = [array objectAtIndex:0];
[array release];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(applicationWillTerminate:) name: UIApplicationWillTerminateNotification object:app];
and here is what I have as part of my saveperson method:
NSMutableArray *array = [[NSMutableArray alloc]init];
[array addObject:self.Drinfo.text];
[array writeToFile:[self pathofFile] atomically:YES];
[array release];
You could use a plist just create a NSDictionary for each person inside the plist and add the colour string to that. The steps would be something like
Open up the plist from file, set it to be a NSDictionary and make sure its a mutable copy.
Create a new NSMutableDictionary and add any attributes to that e.g. eye colour.
Add that mutable dictionary to your plist dictionary with the key set to the persons name
Save the plist