1

I am very new to creating applications and haven't fully figured out how to use the plist function within XCode.

My problem is that I have 3 different input methods within a view controller to which the user will select values from, those being a stepper, a picker view and a date that logs the current date, which I would like to save to a plist so that the user can view those entries in a table view within another view controller.

I haven't really used a plist before therefore my question may sound very silly but regardless I need some help with this.

So far I have the inputs setup but they don't really do anything, I know this question is very basic but I am struggling to find information on this that doesn't go too technical.

I can post my code if that will be beneficial.

Any help will be greatly appreciated.

@property (weak, nonatomic) IBOutlet UILabel *balesFedLabel;
@property (weak, nonatomic) IBOutlet UIStepper *balesFedStepper;
@property (weak, nonatomic) IBOutlet UIPickerView *fieldPickerView;
@property (weak, nonatomic) IBOutlet UILabel *dateLabel;
@property (weak, nonatomic) IBOutlet UITextField *sheepGroup;
@property (strong, nonatomic) IBOutlet UIBarButtonItem *backButton;

//Actions
- (IBAction)stepperValueChange:(id)sender;
- (IBAction)saveButton:(id)sender;
- (IBAction)textFieldDoneEditing:(id)sender;


@property NSArray *dataSource;
@property NSString *tempFieldSelection;
@property(nonatomic) UIKeyboardAppearance keyboardAppearanceDark;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self setupArray];

NSLocale *gbLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"];
NSString *dateFormatString = [NSDateFormatter dateFormatFromTemplate:@"dd/MM/yyyy" options:0 locale:gbLocale];

NSLog(@"dataFormatString: %@", dateFormatString);
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:dateFormatString];

NSString *stringFromDate = [dateFormatter stringFromDate:[NSDate date]];
self.dateLabel.text = stringFromDate;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {[self.view  endEditing:YES];
}

- (void)setupArray {
_dataSource = [[NSArray alloc] initWithObjects:@"Cow Pasture", @"Top Lot",    @"East Lot", @"West Lot", @"Front Meadow", @"Big Meadow", nil];

}

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return [_dataSource count];
}

- (UIView *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row    forComponent:(NSInteger)component {
return [_dataSource objectAtIndex:row];
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
self.tempFieldSelection = [_dataSource objectAtIndex:[pickerView selectedRowInComponent:0]];
}


- (IBAction)stepperValueChange:(id)sender {double baleStepperValue =     self.balesFedStepper.value;
self.balesFedLabel.text = [NSString stringWithFormat:@"%.f", baleStepperValue];
}

- (IBAction)textFieldDoneEditing:(id)sender {[sender resignFirstResponder];
}

- (IBAction)saveButton:(id)sender {
NSLog(@"Bales Fed: %@", self.balesFedLabel.text);
NSLog(@"Sheep Group: %@", self.sheepGroup.text);
NSLog(@"Current Field: %@", self.tempFieldSelection);
NSLog(@"Last Date Fed: %@", self.dateLabel.text);
}
  • I didn't understand the 3rd paragraph, you want data from all three sources stored in a plist, or just the last, or what the user selects stored? If the reason for using a plist is to share between view controllers thats not necessarily the right choice, but need a bit more info. Also will the data stored ever change or is it read only? – Gruntcakes May 27 '15 at 14:45
  • I want the data that the user has inputted all saved within a single plist, sorry for being unclear. – Sam Worfell May 27 '15 at 14:46
  • Basically the user will use the stepper to select an integer then use the picker view to choose a location and the date is automatic, the user will then press the save button and all three bits of data will go to a plist. – Sam Worfell May 27 '15 at 14:50
  • plists are really intended for read only settings (though it is possible to write to them (http://stackoverflow.com/questions/905542/how-to-write-data-in-plist). If the user input needs to persist between app launches consider using NSUserDefaults instead. If there's no need to persist the user input then there's no need to store it anywhere, you just need to get it from one view controller to the other. – Gruntcakes May 27 '15 at 14:51
  • you could store the data items in an NSMutableDictionary and then write that to file or NSUserDefaults (for example) – YvesLeBorg May 27 '15 at 14:51
  • I think a plist is needed because I would like the user to view the data within another view controller with the date being the title of the cell and then when selected the picker view and the steeper information will appear. I am not sure if NSUserDefaults would work for my problem, sorry. – Sam Worfell May 27 '15 at 14:55
  • 1
    Do not use a plist to store your data. Use `CoreData` (in case you already use it inside your project) or `NSUserDefaults` – Jasper May 27 '15 at 14:56
  • Do not use `NSUserDefaults` to store your data but it is not necessary to use CoreData for the data model either. – zaph May 27 '15 at 15:16
  • 2
    I'm not sure where some of these comments are coming from; I see no reason to avoid storing data in a plist in one of the appropriate directories provided to your app's sandbox. @SamWorfell a useful first step would be to create an `NSDictionary` containing the data you want to save, have you done so, where are you stuck? (It is also not necessary to persist data to disk just to share it between controllers if that is your only goal.) – Jonah May 27 '15 at 15:23
  • @Jonah Thanks for the advice, the whole stuck part is the fact I am very inexperienced with this kind of thing so therefore do not know which code is best for my issue. So far what I have is a picker view that is connected to a list within the ViewController.m, a stepper that links to a label that in turn increases the value by 1 each click and finally a date which pull the date from the computer. The user would save the entered data and be able to add more as well without editing the previous data so that's why I thought a plist would be best. This data would be shown in some sort of log. – Sam Worfell May 27 '15 at 20:25
  • Core Data is just an overkill to solve OP problem. And it is difficult to learn for somebody new to iOS development. – Juan Catalan May 27 '15 at 23:41

3 Answers3

3

Use an NSMutableDictionary and pass it to the destination UIViewController in a property.

For example in your source view controller:

NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[@"Bales Fed"] = self.balesFedLabel.text;
dict[@"Sheep Group"] = self.sheepGroup.text;
dict[@"Current Field"] = self.tempFieldSelection;
dict[@"Last Date Fed"] = self.dateLabel.text;

Just pass dict to the destination view controller.

If you want to use a plist these are the two methods available in a NSDictionary class:

- writeToFile:atomically: to write the dictionary to a file (in plist format) and the class method dictionaryWithContentsOfFile: or the instance method initWithContentsOfFile: to retrieve the dictionary from disk.

In your case, to write the dictionary to a file in plist format:

NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"myData.plist"];
[dict writeToFile:filePath atomically:NO];

And in the destination view controller use this code to retrieve the plist from disk:

NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"myData.plist"];
NSDictionary *myData = [NSDictionary dictionaryWithContentsOfFile:filePath];

From Apple's documentation for the first method:

This method recursively validates that all the contained objects are property list objects (instances of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary) before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.

If the dictionary’s contents are all property list objects, the file written by this method can be used to initialize a new dictionary with the class method dictionaryWithContentsOfFile: or the instance method initWithContentsOfFile:.

I also recommend you to read the guide Property List Programming Guide

Community
  • 1
  • 1
Juan Catalan
  • 2,299
  • 1
  • 17
  • 23
  • 1
    This information has been very useful so far, I have used it to enhance other parts of my app but it hasn't quite sorted my issue, I will try again tomorrow when I am less frustrated with this, thank you for your contribution. – Sam Worfell May 28 '15 at 00:26
  • @SamWorfell Let me know if you need more help. – Juan Catalan May 28 '15 at 00:44
  • So what I decided to do was just aim for a lower grade what requires me to just print the data into an NSLog that appears in the console of XCode, I would still like to write it into an NSDictionary or MutableDictionary but I am not really sure how to do that, I am now wondering whether or not I can just print the NSLog into a file but what I have currently will still get me a 2:2 and I am okay with that since I am only first year. Thanks for the advice. – Sam Worfell May 28 '15 at 19:11
  • @SamWorfell Show the NSLog output so I can help you out. Also you have 4 lines of code in the answer that explain how to store the information in a NSMutableDictionary. Now it is just a matter to save the dictionary to a file with - writeToFile:atomically: – Juan Catalan May 28 '15 at 19:24
  • 2015-05-28 21:24:13.132 FeedDasSheep[1876:38563] Bales Fed: 5 2015-05-28 21:24:13.132 FeedDasSheep[1876:38563] Sheep Group: Mules Group 1 2015-05-28 21:24:13.132 FeedDasSheep[1876:38563] Current Field: West Lot 2015-05-28 21:24:13.132 FeedDasSheep[1876:38563] Last Date Fed: 28/05/2015 This is the console output and that comes from NSLog(@"Bales Fed: %@", self.balesFedLabel.text); NSLog(@"Sheep Group: %@", self.sheepGroup.text); NSLog(@"Current Field: %@", self.tempFieldSelection); NSLog(@"Last Date Fed: %@", self.dateLabel.text); – Sam Worfell May 28 '15 at 20:24
  • NSLog(@"Bales Fed: %@", self.balesFedLabel.text); NSLog(@"Sheep Group: %@", self.sheepGroup.text); NSLog(@"Current Field: %@", self.tempFieldSelection); NSLog(@"Last Date Fed: %@", self.dateLabel.text); – Sam Worfell May 28 '15 at 20:26
  • @SamWorfell We need to know how the properties are declared. I think at this point you should update your post and write the code so we can better help you. – Juan Catalan May 28 '15 at 20:32
  • Is that code okay, i think it has updated correctly. – Sam Worfell May 28 '15 at 21:38
  • @SamWorfell Yes, this is the correct way to post code. – Juan Catalan May 28 '15 at 21:40
  • This is excellent, thank you very much, quick question, do the NSStrings *documentsPath and *filePath go into the h file or in the m file of the source controller? – Sam Worfell May 28 '15 at 23:13
  • These are local variables so they go in the .m file and get created and destroyed in a code block {} – Juan Catalan May 28 '15 at 23:47
  • Ahh thank you, I believe I have put them in the right place as they are saving correctly, thank you for everything, you have been amazing help. – Sam Worfell May 29 '15 at 00:00
0

Even when i wouldn`t recommend to use a .plist file here is the code to use it:

NSString* plistPath = [[NSBundle mainBundle] pathForResource:@"YOUR_FILE_NAME" ofType:@"plist"];
NSDictionary *plistDic = [NSDictionary dictionaryWithContentsOfFile:plistPath];
LoVo
  • 1,856
  • 19
  • 21
0

As you are just passing small amounts of user entered data from one view controller to another you do not need to use PLists nor CoreData nor NSUserDefaults. These are all suitable for persistent data but from your description it sounds like it is just transient stuff.

Just store the user data in parameters and then pass them forward to the destination ViewController using the prepareForSegue method. See this SO answer for full details.

Community
  • 1
  • 1
Ali Beadle
  • 4,486
  • 3
  • 30
  • 55