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];
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
The development is in Xcode for iOS. I used 2 separate NSmutabledictionary.
I DON´T want to add them (when searching I found that often, but that is not what I try to do)
The app has to store them on the disk and when the app launches it can read the dictionary.
It is not a custom class so initWithCoder and the other one is not necessary (is this right?)
NSArray *data = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [data objectAtIndex:0];
path = [path stringByAppendingPathComponent:#"location"];
[NSKeyedArchiver archiveRootObject:"Dictionary" toFile:path];
This works but when I want to save another dictionary it won't work. My first thought would be to change the objectAtIndex to 1. But Xcode gives me an error when I do that. When I only give another name it simply won't save, but Xcode don't give an error.
What am I doing wrong?
// Find out where the documents folder is
NSString *documentsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
// Create the file path for a file named 'location' inside the documents folder
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"location"];
// Write an array containing both of your arrays to a file at that path
[NSKeyedArchiver archiveRootObject:#[myFirstArray, mySecondArray] toFile:path];
Then, to read them back:
// Read back the array containing the two arrays
NSArray *arrays = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
// Get the individual arrays from the array
NSMutableArray *myFirstArray = arrays[0];
NSMutableArray *mySecondArray = arrays[1];
How can I append values to an existing key in NSDictionary from a plist?
What I have is basically a plist saved in disc with a few keys and values, the keys are the names of some students with one initial value. What I’m trying to do that doesn't work is to append more items to existing keys/Students by reading the plist, adding it to a temporary NSDictionary, appending a temporary array to an existing key in the plist but when I save the plist back it doesn’t work, it only saves the last two items and basically deletes the initial value for that key.
Here is the code that I’m using…
- (IBAction)testing:(id)sender
{
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [dirPaths objectAtIndex:0];
NSString *fullPath = [documentsPath stringByAppendingPathComponent:#"studentsRecords.plist"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fullPath];
if(fileExists)
{
NSMutableDictionary *dictionaryRecords = [[NSMutableDictionary alloc]initWithContentsOfFile:fullPath];
NSLog(#"Items for Nathan: %#",[dictionaryRecords objectForKey:#"Nathan"]);// here the output is, Items for Nathan: Ruler
// which make sense since I only had one initial record for Nathan in my plist,
// Here, I want to add two more items to the key Nathan by appending an arry to
// NSMutableDictionary (dictionaryRecords) but it doesnt work
NSArray *tempArray = #[#"NoteBook", #"Pencil"];
[dictionaryRecords setObject:tempArray forKey:#"Nathan"];
[dictionaryRecords writeToFile:fullPath atomically:YES];
// when I check the plist after saving this, I only see the last two values, NoteBook and Pencil
// instead of the three that I'm expecting, Ruler, NoteBook and Pencil
}
}
What am I missing here?
Thanks a lot in advance.
[dictionaryRecords setObject:tempArray forKey:#"Nathan"] replaces whatever the previous value was with tempArray.
If you want to add to the existing array, you have to retrieve it, make a mutable copy, and append to it.
Here is how I made it work. I hope I didn't over complicated things here but for some reason I had to use two arrays, an NSArray and an NSMutableArray, I would think that the NSMutableArray would be enough but it didn't work.
- (IBAction)testing:(id)sender
{
// get directory path
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [dirPaths objectAtIndex:0];
// create plist
NSString *fullPath = [documentsPath stringByAppendingPathComponent:#"studentsRecords.plist"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fullPath];
// if file exists create dictionary
if(fileExists)
{
// add items from plist to dictionary
NSDictionary *dictionaryExistingRecords = [[NSDictionary alloc]initWithContentsOfFile:fullPath];
// make a copy if dictinary with existing items
NSMutableDictionary *dictionaryNewRecords = [dictionaryExistingRecords mutableCopy];
// array to hold existing values from a spcecified key
NSArray *arrayWithExistingKey = [dictionaryExistingRecords objectForKey:#"Nathan"];
// mutable array to add existing and be able to insert new items
NSMutableArray *arrayOldAndNewItems = [NSMutableArray arrayWithArray:arrayWithExistingKey];
// insert new items to array
[arrayOldAndNewItems addObject:#"Snow Pans"];
// add old and new items to specified key
[dictionaryNewRecords setObject:arrayOldAndNewItems forKey:#"Nathan"];
// save new records to plist
[dictionaryNewRecords writeToFile:fullPath atomically:YES];
}
}
#end
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];
I have a plist in the following form:
Root (array)---> item 1 (dictionary) ----> Sch (string)
---> Name (string)
----> price (number)
----> item 2 (dictionary)----> .....same as item 1
How can I access each row (item1 to ...) and the its child (Sch, Name etc.)? One at a time?
I use:
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"Data.plist"];
NSDictionary *plistData = [[NSDictionary dictionaryWithContentsOfFile:finalPath] retain];
to load the file. How should I go about accessing each child?
What I am trying to do is, I have a NSString *message, what I want to do is to search the whole plist for matching string and display the whole item 1. Any suggestion?
When you initialize a collection from a plist, the type is the root level object. Therefore you would not initialize a dictionary but an array like so:
NSArray *plistData = [[NSArray arrayWithContentsOfFile:finalPath] retain];
Then you would access it like this:
NSString *sch;
NSString *name;
NSString *price;
for (NSDictionary *aDict in plistData) {
sch = [aDict objectAtKey:"Sch"];
name = [aDict objectAtKey:"Name"];
price = [aDict objectAtKey:"price"];
//.. do whatever
}
Here is a link to a discussion that provided a very good example of how to access your data and which type of storage schemes would be more beneficial under certain circumstances. #TechZen's solution is on target, I just thought this question would add an additional resource. Hope this helps!