Populate UITableView with NSMutableArray saved with NSUserDefault - ios

In my application I have an UITextView, which is populated at runtime when I insert new data via an Alert Controller. To print this use data:
NSArray * textfields = alertController.textFields;
            
UITextField * urlfield = textfields [0];
UITextField * titlefield = textfields [1];
UITextField * categoryfield = textfields [2];
            
FeedInfo * newFeed = [[FeedInfo alloc] init];
NewFeed.feedURL = urlfield.text;
NewFeed.feedTitle = titlefield.text;
NewFeed.feedCategory = categoryfield.text;
            
[Self.feedArray addObject: newFeed];
[Self.tableView reloadData];
Where feedArray is a defined class model:
#interface FeedInfo : NSObject
#property (nonatomic,strong) NSString *feedURL;
#property (nonatomic,strong) NSString *feedTitle;
#property (nonatomic,strong) NSString *feedCategory;
#end
Next, I print the data from feedArray inside the IUTableView with:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
FeedInfo *feedToShow = [self.feedArray objectAtIndex:indexPath.row];
cell.textLabel.text = feedToShow.feedTitle;
cell.detailTextLabel.text = feedToShow.feedCategory;
return cell;
OK. Up to here everything works fine.
Now the problem is that before I quit the application, I want to save all the cells in the UITableView, because when the re-opening must still have all the inserted data.
I was thinking of saving all the feedArra array (1st class of this topic) by inserting this piece of code after [self.feedArray addObject: newFeed]; and before [self.tableView reloadData];
NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults
[UserDefaults setObject: self.feedArray forKey: #"feedArray"];
[userDefaults synchronize];
Within the viewDidLoad method instead:
NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
Array = [userDefaults objectForKey: # "feedArray"];
And in cellForRowAtIndexPath:
FeedInfo *feedToShow = [array objectAtIndex:indexPath.row];
cell.textLabel.text = feedToShow.feedTitle;
cell.detailTextLabel.text = feedToShow.feedCategory;
But it throws an Exception:
RSSReader[47834:2470261] [User Defaults] Attempt to set a
non-property-list object ("<FeedInfo: 0x600000235200>" ) as an NSUserDefaults/CFPreferences value for key feedArray 2017-07-18 11:00:13.652
RSSReader[47834:2470261] *** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: 'Attempt to insert non-property
list object (
"<FeedInfo: 0x600000235200>" ) for key feedArray'

You model (Feed) should confirm to NSCoding protocol to Archive and UnArchive the data.
FeedInfo.h
#interface FeedInfo : NSObject <NSCoding>
#property (nonatomic,strong) NSString *feedURL;
#property (nonatomic,strong) NSString *feedTitle;
#property (nonatomic,strong) NSString *feedCategory;
#end
FeedInfo.m
#implementation FeedInfo
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (!self) {
return nil;
}
self.feedURL = [aDecoder decodeObjectForKey:#"feed_url"];
self.feedTitle = [aDecoder decodeObjectForKey:#"feed_title"];
self.feedCategory = [aDecoder decodeObjectForKey:#"feed_category"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:_feedURL forKey:#"feed_url"];
[coder encodeObject:_feedTitle forKey:#"feed_title"];
[coder encodeObject:_feedCategory forKey:#"feed_category"];
}
#end
Store to Userdefault
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.feedArray];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"feedArray"];
Retrive from UserDefault
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"feedArray"];
NSArray *feedArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];

NSUserDefault support these classes :NSNumber(Integer、Float、Double),NSString,NSDate,NSArray,NSDictionary,BOOL;
FeedInfo is a custom class , and you cannot put it in the NSUserDefault;
You should turn the FeedInfo object to NSData , then add it;
NSData * newData = [NSKeyedArchiver archivedDataWithRootObject:newFeed];
[Self.feedArray addObject: newData];

You can you this code. my code is help you
Save NSMutableArray in NSUserDefaults
- (void)saveList:(NSMutableArray *)arr
{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:ZIPCODE_LIST];
[[NSUserDefaults standardUserDefaults]synchronize];
}
Get NSMutableArray from NSUserDefaults
- (NSMutableArray *)getList
{
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:ZIPCODE_LIST];
NSMutableArray *arr = (NSMutableArray *)[NSKeyedUnarchiver unarchiveObjectWithData: data];
return arr;
}

Related

save and load data in UItableview

I have some problem with saving and loading data in UITableView. After I press button2, I can't find my data that I saved(maybe I didn't save it).
My.h file:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITableViewDelegate,UITableViewDataSource>
{
NSMutableArray * arrayIdea;
NSMutableArray * arrayEdit;
IBOutlet UITableViewCell * cell;
}
#property(nonatomic,strong)IBOutlet UITextField * txtField;
#property(nonatomic,strong)IBOutlet UITableView * tabel1;
-(IBAction)button2:(id)sender;
-(IBAction)button1:(id)sender;
#end
My .m file:
#import "ViewController.h"
#interface ViewController ()<UITextFieldDelegate>
#end
#implementation ViewController
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
[textField resignFirstResponder];
return YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
arrayIdea = [[NSMutableArray alloc] init];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (arrayIdea.count > 0) {
return arrayIdea.count;
}
return 0;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell = [tableView dequeueReusableCellWithIdentifier:#"cell1"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell1"];
}
cell.textLabel.text =[NSString stringWithFormat:#"%#", arrayIdea[indexPath.row]];
return cell;
}
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
if(editingStyle == UITableViewCellEditingStyleDelete) {
[arrayIdea removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath,nil] withRowAnimation: UITableViewRowAnimationFade];
}
}
-(IBAction)button1:(id)sender;{
[arrayIdea addObject:self.txtField.text];
[self.tabel1 reloadData];
self.txtField.text = #"";
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:arrayIdea forKey:#"savedstring"];
[defaults synchronize];
}
-(IBAction)button2:(id)sender;{
NSUserDefaults*defaults = [NSUserDefaults standardUserDefaults];
cell.textLabel.text =[defaults objectForKey:#"savedstring"];
}
#end
You need to encode/decode your array to be able to save it/ retrieve it in NSUserDefaults.
Save
NSData *dataSave = [NSKeyedArchiver archivedDataWithRootObject: arrayIdea];
[[NSUserDefaults standardUserDefaults] setObject:dataSave forKey:#"savedstring"];
[[NSUserDefaults standardUserDefaults] synchronize];
Read
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"savedstring"];
NSArray *savedArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
You can save and retrieve like this ..
Save
[[NSUserDefaults standardUserDefaults]setObject:arrayIdea forKey:#"savedstring"];
And retrieve
[[NSUserDefaults standardUserDefaults] objectForKey:#"savedstring"];
In cellForRowAtIndexpath, If you write code like below.
cell.textlabel.text = [[defaults objectForKey:#"saved string"] objectAtIndex:indexPath.row];
instead of passing array.
In numberOfRowsInSection you can pass
[[defalts objectForKey:#"saved string"] count];
You already called table reload method, so there is no need of button 2.
Please try.

How to save data from ViewController and Load it to TableViewController when app rerun?

So I have a class "WishListItem", a TableViewController and a ViewController. I'm having difficulties in saving chunk of data and retrieving it from TableViewController? How to do this effectively?
Here's my ViewController which has a prepareForSegue that stores data to my WishListItem class.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSDate *myDate = self.targetDatePicker.date;
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"MMM d, YYYY"];
NSString *prettyVersion = [dateFormat stringFromDate:myDate];
if (sender != self.addWishListButton) return;
if (self.wishTextField.text.length > 0) {
self.wishItem = [[WishlistItem alloc] init];
self.wishItem.wishlistItem = self.wishTextField.text;
self.wishItem.descWishItem = self.descTextField.text;
self.wishItem.targetDate = prettyVersion;
}
}
WishListItem.h :
#interface WishlistItem : NSObject
#property NSString *wishlistItem;
#property NSString *descWishItem;
#property NSString *targetDate;
#end
ViewController.h :
#interface JLSViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIBarButtonItem *addWishListButton;
#property (strong, nonatomic) IBOutlet UITextField *wishTextField;
#property (strong, nonatomic) IBOutlet UITextField *descTextField;
#property (strong, nonatomic) IBOutlet UIDatePicker *targetDatePicker;
#property WishlistItem *wishItem;
#end
From here. I want to save it every time I add a wishItem. How would I store MULTIPLE ENTRIES? and at the same time retrieve those entries in my TableViewController?
Please let me know if I need to provide more info. TIA.
The easiest and the rude way of doing it is storing the data in NSUserDefaults
+(void)userDefaultsSetObject:(id)userObject forKey:(NSString *)userKey
{
NSUserDefaults *userDefaults=[NSUserDefaults standardUserDefaults];
[userDefaults setObject:userObject forKey:userKey];
[userDefaults synchronize];
}
/**
* This method helps to get values from NSUserDefaults
*/
+(id)userDefaultsGetObjectForKey:(NSString *)userKey
{
NSUserDefaults *userDefaults=[NSUserDefaults standardUserDefaults];
return [userDefaults objectForKey:userKey];
}
Use the above two functions to save and retrieve data from NSUserDefaults. So coming to your problem here, add the wishlistitem objects to an array and set it in NSUserDefaults. Add this code to your tableviewcontroller class, and have a datasource array as a global variable.
NSMutableArray *dataSourceArray;
In ViewDidLoad of the tableviewcontroller class add this,
NSMutableArray *wishlistItems;
if([self userDefaultsGetObjectForKey:#"WishListItems"]==nil)
{
wishlistItems = [NSMutableArray array];
}
else
{
wishlistItems = [self userDefaultsGetObjectForKey:#"WishListItems"];
}
dataSourceArray = [[NSMutableArray alloc] initWithArray:wishlistItems];
And in the tableViewDelegateMethod, try this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [dataSourceArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
WishlistItem *wishListObject = [dataSourceArray objectAtIndex:indexPath.row];
[cell.textLabel setText:wishListObject.wishlistItem];
[cell.detailTextLabel setText:wishListObject.descWishItem];
return cell;
}
So every time you add something do this,
- (NSMutableArray*)wishListAdded:(WishlistItem*)wishList
{
NSMutableArray *wishlistItems;
if([self userDefaultsGetObjectForKey:#"WishListItems"]==nil)
{
wishlistItems = [NSMutableArray array];
[wishlistItems addObject: wishItem];
[self userDefaultsSetObject:wishlistItems forKey:#"WishListItems"];
}
else
{
wishlistItems = [self userDefaultsGetObjectForKey:#"WishListItems"];
[wishlistItems addObject:yourWishListObject];
[self userDefaultsSetObject:wishlistItems forKey:#"WishListItems"];
}
return wishlistItems;
}
To save your array state use this ie in view controller
NSString *valueToSave = #"someValue";
[[NSUserDefaults standardUserDefaults] setObject:valueToSave forKey:#"preferenceName"];
[[NSUserDefaults standardUserDefaults] synchronize];
use this where you want to retrieve the variable ie. intableViewController
NSString *savedValue = [[NSUserDefaults standardUserDefaults]
stringForKey:#"preferenceName"];
You can use mutablearrays for saving multiple objects of your wishlist class and then store that array in the userdefaults and retrieve it as per your needs.
Any changes made to the a particular object in the index of the array needs to be updated in the user default as well, else you will end up getting the same result set which you have stored in the first place.
I think you need to create the #property WishlistItem *wishItem; object in TableViewController .h file and save all the data while in segue method. So this is how your implementation will look like
in TableViewController.h
#import "WishlistItem.h"
#interface SomeTableViewController : UITableViewController
#property WishlistItem *wishItem
in JLSViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSDate *myDate = self.targetDatePicker.date;
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"MMM d, YYYY"];
NSString *prettyVersion = [dateFormat stringFromDate:myDate];
if (sender != self.addWishListButton) return;
if (self.wishTextField.text.length > 0) {
SomeTableViewController *tableViewController = [segue destinationViewController];
tableViewController.wishItem = [[WishlistItem alloc] init];
tableViewController.wishItem.wishlistItem = self.wishTextField.text;
tableViewController.wishItem.descWishItem = self.descTextField.text;
tableViewController.wishItem.targetDate = prettyVersion;
}
}
in TableViewController.m
you can populate the data in tableview using self.wishItem.wishlistItem etc.
if you want to save large data then consider using Core Data, also NSDateFormatter is expensive operation so you can refer this link for some improving that.
There are severals ways of saving data and retrieving them:
1. Saving data to NSUserDefaults (how Anonymous suggested):
With NSUserDefaults you can save objects from the following class types:
NSData, NSString, NSNumber, NSDate, NSArray, NSDictionary
try to take a look at the documentation, UserDefaults is simple to understand and use.
NSUserDefaults Doc
2. Saving data to Property List file (plist)
It is kind of the same like UserDefaults (it can store the same data type) difference is you write your data in a document.
But: Don't keep it in the Document folder unless it is appropriate for storage in iCloud, says Apple; Reference
3. Save data to a data base
this will be another way to store your data...
At the end it all depends how complex will be the data you want to save, are we talking about strings and numbers or images NSUserNotification etc
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSDate *myDate = self.targetDatePicker.date;
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"MMM d, YYYY"];
NSString *prettyVersion = [dateFormat stringFromDate:myDate];
if (sender != self.addWishListButton) return;
if (self.wishTextField.text.length > 0)
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
[dict setObject:self.wishTextField.text forKey:#"wishTextField"];
[dict setObject:self.descTextField.text forKey:#"descTextField"];
[dict setObject:prettyVersion forKey:#"prettyVersion"];
NSMutableArray *array = (NSMutableArray*)[userDefaults objectForKey:#"myWishList"];
//check if array is not null i`m not sure if it will give you error if it is null
// if null NSMutableArray *array= [NSMutableArray alloc]init]; else
[array addObject:dict];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:array forKey:#"myWishList"];
}
}
i think this should work for saving. For loading data in your:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath // methode
//add this
NSMutableArray *array = (NSMutableArray*)[userDefaults objectForKey:#"myWishList"];
// this will return all your wishlist items
NSMutableDictionary *dict = [array objectAtIndex:indexPath.row];
// this will give you the data you saved in the particular place
cell.textLabel.text = [dict objectForKey:#"wishTextField"];//this will be the name of the whishlist
I think this should do

How do I save files for an iPhone Application with my current set up?

I cannot for the life of me figure out how to save my data with my current setup! I've tried to use NSUserDefaults, but because i'm using a custom object it doesn't work. I've been trying to figure out how to use NSKeyedArchiver but I can't figure out where to implement it.
AG_AddItemViewController.h
#import <UIKit/UIKit.h>
#import "AG_Storage.h"
#interface AG_AddItemViewController : UIViewController
#property AG_Storage *store;
#end
AG_AddItemViewController.m
#import "AG_AddItemViewController.h"
#interface AG_AddItemViewController()
#property(weak,nonatomic)IBOutlet UITextField *textField;
#property(weak,nonatomic)IBOutlet UIButton *doneButton;
#property IBOutlet UIDatePicker *datePicker;
#property NSMutableArray *defaultsArray;
#end
#implementation AG_AddItemViewController
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (sender != self.doneButton) return;
if (self.textField.text.length > 0) {
self.store =[[AG_Storage alloc] init];
self.store.itemName = self.textField.text;
self.store.completed = NO;
NSDate *dateChosen = self.datePicker.date;
self.store.creationDate = dateChosen;
NSLog(#"%#", self.store);
[self.defaultsArray addObject:self.textField.text];
}
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.defaultsArray = [[NSMutableArray alloc] init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
AG_Storage
#import <Foundation/Foundation.h>
#interface AG_Storage : NSObject
#property NSString *itemName;
#property BOOL *completed;
#property NSDate *creationDate;
#property NSDate *todaysDate;
#end
AG_ViewController.h
#import <UIKit/UIKit.h>
#interface AG_ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>{
IBOutlet UITableView *tableView;
IBOutlet UILabel *todaysDate;
IBOutlet UILabel *subtractDate;
IBOutlet UILabel *addDate;
}
#end
AG_ViewController.m
#import "AG_ViewController.h"
#import "AG_Storage.h"
#import "AG_AddItemViewController.h"
#interface AG_ViewController ()
#property NSMutableArray *mainArray;
#property NSMutableArray *yesterdayArray;
#property NSMutableArray *tomorrowArray;
#property NSDate *todayDate;
#property NSDate *tomorrowsDate;
#property NSDate *yesterdaysDate;
#end
#implementation AG_ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.mainArray = [[NSMutableArray alloc] init];
self.yesterdayArray = [[NSMutableArray alloc]init];
self.tomorrowArray = [[NSMutableArray alloc]init];
[self loadInitialData];
}
- (void)loadInitialData
{
// Do any additional setup after loading the view, typically from a nib.
//NSDate Info
NSTimeInterval secondsPerDay = 24 * 60 * 60;
NSDate *today = [[NSDate alloc]init];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"MMMM dd, yyyy"];
self.todayDate = today;
self.tomorrowsDate = [today dateByAddingTimeInterval: secondsPerDay];
self.yesterdaysDate = [today dateByAddingTimeInterval: -secondsPerDay];
NSString *todayString = [dateFormat stringFromDate:self.todayDate];
NSString *tomorrowString = [dateFormat stringFromDate:self.tomorrowsDate];
NSString *yesterdayString = [dateFormat stringFromDate:self.yesterdaysDate];
todaysDate.text = todayString;
addDate.text = tomorrowString;
subtractDate.text = yesterdayString;
AG_Storage *theDateToday = [[AG_Storage alloc]init];
theDateToday.todaysDate = self.todayDate;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults arrayForKey:todayString];
NSLog(#"Tried to load...");
[defaults synchronize];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.mainArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableViewer cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableViewer dequeueReusableCellWithIdentifier:#"thisCell"];
AG_Storage *toDoItem = [self.mainArray objectAtIndex:indexPath.row];
cell.textLabel.text = toDoItem.itemName;
if (toDoItem.completed) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (IBAction)unwindToList:(UIStoryboardSegue *)segue
{
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"MMMM dd, yyyy"];
AG_AddItemViewController *source = [segue sourceViewController];
AG_Storage *item = source.store;
NSDate *dateCreated = item.creationDate;
NSString *todayString = [dateFormat stringFromDate:self.todayDate];
NSString *dateCreatedString = [dateFormat stringFromDate:dateCreated];
NSString *tomorrowString = [dateFormat stringFromDate:self.tomorrowsDate];
NSString *yesterdayString = [dateFormat stringFromDate:self.yesterdaysDate];
//Set up file storage!
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (item.itemName != nil) {
if ([dateCreatedString isEqualToString:todayString]) {
[self.mainArray addObject:item];
[tableView reloadData];
[defaults setObject:self.mainArray forKey:todayString];
[defaults synchronize];
NSLog(#"Saved");
}
else if ([dateCreatedString isEqualToString:tomorrowString]){
[self.tomorrowArray addObject:item];
[tableView reloadData];
NSLog(#"THIS WORKED TOO :D");
}
else if ([dateCreatedString isEqualToString:yesterdayString]){
[self.yesterdayArray addObject:item];
[tableView reloadData];
NSLog(#"THIS WORKED");
}
else{
}
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableViewer didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableViewer deselectRowAtIndexPath:indexPath animated:NO];
AG_Storage *tappedItem = [self.mainArray objectAtIndex:indexPath.row];
tappedItem.completed = !tappedItem.completed;
[tableViewer reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return YES if you want the specified item to be editable.
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableViews commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.mainArray removeObjectAtIndex:indexPath.row];
[tableViews deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
#end
I'd like to use NSUserDefaults because it seems simple, but if I can't thats okay. I just cannot wrap my head around this for some reason.
EDIT: This is how I tried to implement Hyperbole's ideas...
if (item.itemName != nil) {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:item];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"storageObjectKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
if ([dateCreatedString isEqualToString:todayString]) {
[self.mainArray addObject:item];
[tableView reloadData];
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"storageObjectKey"];
AG_Storage *someStorageObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"%#", someStorageObject);
NSLog(#"Saved");
}
My recommendation is to have AG_Storage declare conformity to the NSSecureCoding protocol and implement the coding methods in there. After that's done, putting those AG_Storage objects into NSUserDefaults is a trivial task.
#interface AG_Storage : NSObject <NSSecureCoding>
#end
#implementation AG_Storage
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.itemName forKey:#"itemNameKey"];
[encoder encodeObject:#(self.completed) forKey:#"completedKey"];
[encoder encodeObject:self.creationDate forKey:#"creationDateKey"];
[encoder encodeObject:self.todaysDate forKey:#"todaysDateKey"]; // This one doesn't seem necessary to save, though I'm sure you would know better than I would.
}
- (id)initWithCoder:(NSCoder *)decoder {
if ([decoder respondsToSelector:#selector(decodeObjectOfClass:forKey:)]) // Secure coding is only available in iOS 6
{
self.itemName = [decoder decodeObjectOfClass:[NSString class] #"itemNameKey"];
self.completed = [[decoder decodeObjectOfClass:[NSNumber class] forKey:#"completedKey"] boolValue];
self.creationDate = [decoder decodeObjectOfClass:[NSDate class] forKey:#"creationDateKey"];
self.todaysDate = [decoder decodeObjectOfClass:[NSDate class] forKey:#"todaysDateKey"];
}
else
{
self.itemName = [decoder decodeObjectForKey:#"itemNameKey"];
self.completed = [[decoder decodeObjectForKey:#"completedKey"] boolValue];
self.creationDate = [decoder decodeObjectForKey:#"creationDateKey"];
self.todaysDate = [decoder decodeObjectForKey:#"todaysDateKey"];
}
#end
The next step is to break this down into an instance of NSData for storage in NSUserDefaults:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:someAG_StorageObject];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"storageObjectKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
And to retrieve it later:
NSData *data = [[NSUserDefaults standardDefaults] objectForKey:#"storageObjectKey"];
AG_Storage *someStorageObject = [NSKeyedUnarchiver unarchiveObjectWithData:savedData];

My view does not update

My ParseXML method reads the value of NSNumber, which can be incremented by a click of a button.
My ParseXML method has 240 objects, each 8 have an ID from 1 to 30.
The idea is that if i increment the NSNumber from 1 to 2, it refreshes my view and grabs the 8 objects that match the ID and displays it in my view.
That is exactly what is not doing.
.h
#interface FixturesController : UITableViewController
{
NSMutableData *_responseDataFixtures;
int goUp;
NSNumber *test;
}
#property (nonatomic, retain) NSArray *tableDataFixtures;
#property (nonatomic, strong) NSMutableArray *roundParser;
#property (nonatomic, strong) NSString *seasonRoundString;
#property (nonatomic, strong) NSNumber *seasonRoundNumber;
- (IBAction)goUpByOne:(UIButton *)sender;
-(void) parseXMLFixtures:(NSNumber *) giveME;
#end
.m
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self parseXMLFixtures:#2];
}
- (void)viewDidLoad
{
[super viewDidLoad];
goUp = 1;
test = [NSNumber numberWithInt:goUp];
}
// this allows me to increment the count of NSNumber.
- (IBAction)goUpByOne:(UIButton *)sender {
goUp++;
test = [NSNumber numberWithInt:goUp];
goUp = [test intValue];
}
-(void) parseXMLFixtures:(NSNumber *) giveME
{
giveME = test;
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"There's no going back"]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *xmlString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSDictionary *xml = [NSDictionary dictionaryWithXMLString:xmlString];
NSMutableArray *items = [xml objectForKey:#"Match"];
NSMutableArray *newFixtureObjectArray = [[NSMutableArray alloc] init];
NSNull *nullValue = [NSNull null];
[newFixtureObjectArray insertObject:nullValue atIndex:0];
[newFixtureObjectArray insertObject:nullValue atIndex:1];
for (NSDictionary *dict in items) {
FixturesObject *myFixtures = [FixturesObject fixtureFromXMLDictionary:dict];
[newFixtureObjectArray addObject:myFixtures];
}
///////
_seasonRoundString = [NSString stringWithFormat:#"%d", [giveME intValue]];
_roundParser = [[NSMutableArray alloc]init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"round == %#", _seasonRoundString];
NSArray *filteredArray = [newFixtureObjectArray filteredArrayUsingPredicate:predicate];
_roundParser = [NSMutableArray arrayWithArray:filteredArray];
[_roundParser insertObject:nullValue atIndex:0];
NSLog(#" Objects of Fixtures in my array %#", _roundParser);
/////
[self setTableDataFixtures:_roundParser];
}
Any suggestions? Thank you. I really need this to work so i can go sleep ˆˆ
Have you impleted the UITableViewDelegate, UITableViewDataSource methods yet?
The methods are:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{ }
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { }
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{}
You can follow this tutorial

Retrieving and re-saving an nsmutablearray to nsuserdefaults from a uitableview

I have a uitableview with a list of items populated from a JSON file located locally within the app. Everything works as far as getting the list to the table and the multiple selection of items which when selected (or deselected) are then saved to a nsmutablearray.
The problem is when the user leaves the view and returns and selects another item (or deselects a currently selected item). At this point the mutable array is then empty.
I'm not sure if the nsuserdefaults saving of the mutable array is the problem. it saves it fine but then when the view reappears (the mutable array's value is fine at this point) and the user touches a table row the array is null once more.
my .h file:
#interface CategoriesViewController : UITableViewController {
NSMutableArray *_selectedItems;
NSString *filePath;
NSString *string;
}
// arForTable array will hold the JSON results from the api
#property (nonatomic, retain) NSArray *arForTable;
#property (nonatomic, retain) NSMutableArray *categorySelected;
#property (nonatomic, retain) NSString *jsonStringCategory;
#property(nonatomic, retain) UIView *accessoryView;
#end
my .m file:
#implementation CategoriesViewController
#synthesize arForTable = _arForTable;
- (void)viewDidLoad
{
[super viewDidLoad];
self.categorySelected = [[NSMutableArray alloc] init];
[self reloadMain];
// assignment reference so don't release
_selectedItems = [(AppDelegate *)[[UIApplication sharedApplication] delegate] selectedCategories];
self.tableView.hidden = NO;
}
-(void) reloadMain {
// countrySaved value from NSUserDefaults
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
NSString *countryString = [defaults stringForKey:#"selectedCountryTableString"];
NSString *cityString = [defaults stringForKey:#"selectedCityTableString"];
NSLog(#"countrystring from category is %#", countryString);
NSLog(#"citystring from category is %#", cityString);
// getting path to the file
if ([defaults stringForKey:#"selectedCountryTableString"] == NULL) {
filePath = [[NSBundle mainBundle] pathForResource:#"categoriesit" ofType:#"json"];
} else if ([countryString isEqualToString:#"UK"]) {
filePath = [[NSBundle mainBundle] pathForResource:#"categoriesuk" ofType:#"json"];
} else if ([countryString isEqualToString:#"Italy"]) {
filePath = [[NSBundle mainBundle] pathForResource:#"categoriesit" ofType:#"json"];
} else if ([countryString isEqualToString:#"Spain"]) {
filePath = [[NSBundle mainBundle] pathForResource:#"categorieses" ofType:#"json"];
} else if ([countryString isEqualToString:#"Brazil"]) {
filePath = [[NSBundle mainBundle] pathForResource:#"categoriesbr" ofType:#"json"];
}
NSString *fileContent = [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
//NSLog(#"File content: %#", fileContent);
// creating new parser
SBJSON *parser = [[SBJSON alloc] init];
// parsing the first level
NSDictionary *data = (NSDictionary *) [parser objectWithString:fileContent error:nil];
NSDictionary *menu = (NSDictionary *) [data objectForKey:#"menu"];
#ifdef DEBUG
NSLog(#"menu is %#",menu);
#endif
NSMutableArray *itemsTMP = [[NSMutableArray alloc] init];
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:nil error:nil];
// NSLog(#"results File test %#",dict);
itemsTMP = [dict objectForKey:#"results"];
// NSLog(#"itemsTMPitemsTMP File test %#",itemsTMP);
self.arForTable = [itemsTMP copy];
[self.tableView reloadData];
}
#pragma mark - Table view data source
- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.arForTable count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[cell.textLabel setFont:[UIFont fontWithName: #"Asap-Bold" size: 14.0f]];
[cell.detailTextLabel setFont:[UIFont fontWithName: #"Asap-Bold" size: 14.0f]];
cell.selectedBackgroundView = [[UIView alloc] initWithFrame:CGRectZero];
cell.selectedBackgroundView.backgroundColor = [UIColor colorWithRed:204.0/255.0 green:56.0/255.0 blue:55.0/255.0 alpha:1];
}
UIImageView *cellAccessoryImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon-tick.png"]] ;
UIImageView *cellAccessoryNoneImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#""]] ;
if([_selectedItems containsObject:indexPath]){
cell.accessoryView = cellAccessoryImageView;
} else {
cell.accessoryView = cellAccessoryNoneImageView;
}
// Get item from tableData
NSDictionary *item = (NSDictionary *)[_arForTable objectAtIndex:indexPath.row];
// encoding fix
NSString *correctStringTitle = [NSString stringWithCString:[[item objectForKey:#"key"] cStringUsingEncoding:NSISOLatin1StringEncoding] encoding:NSUTF8StringEncoding];
cell.textLabel.text = [correctStringTitle capitalizedString];
NSNumber *num = [item objectForKey:#"id"];
cell.detailTextLabel.text = [num stringValue];
cell.detailTextLabel.hidden = YES;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
if([_selectedItems containsObject:indexPath]){
[_selectedItems removeObject:indexPath];
[self.categorySelected removeObject:[[self.arForTable objectAtIndex:indexPath.row] objectForKey:#"id"]];
string = [self.categorySelected componentsJoinedByString:#","];
[defaults setObject:string forKey:#"selectedCategoryTableString"];
NSLog(#"%# defaults from did select remove categorySelected",[defaults stringForKey:#"selectedCategoryTableString"]);
NSLog(#"%# STRING FROM contains / removeObj",string);
} else {
[_selectedItems addObject:indexPath];
[self.categorySelected addObject:[[self.arForTable objectAtIndex:indexPath.row] objectForKey:#"id"]];
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
string = [self.categorySelected componentsJoinedByString:#","];
[defaults setObject:string forKey:#"selectedCategoryTableString"];
NSLog(#"%# providerSelected from did select add ",self.categorySelected);
NSLog(#"%# STRING FROM contains / addObj",string);
}
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
// [tableView reloadData];
}
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:NO];
[self.navigationController setNavigationBarHidden:YES animated:NO];
self.navigationController.toolbarHidden = YES;
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
// NSLog(#"ALL DEFAULTS %#", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);
NSLog(#"%# defaults from view appear categorySelected",[defaults stringForKey:#"selectedCategoryTableString"]);
string = [defaults stringForKey:#"selectedCategoryTableString"];
NSLog(#"%# STRING from will appear",string);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
also in the app delegate I have in the .h:
#property (strong, nonatomic) NSMutableArray *selectedCategories;
and in the .m:
`_selectedCategories = [NSMutableArray new];
in the didFinishLaunchingWithOptions: method
just to be clear:
when the view appears again (if I nslog the output) the mutablearray has been saved and is retrieved correctly. the mutable array only clears itself when a tablerow is touched again.
thanks if anyone can help with this. I've been stuck on it for some time...
edit
// DONT EVER EVER EVER EVER EVER EVER DO THIS!!!
// We don't use types as variable names, that is implicit...
// I get it, this is a string, BUT WHAT IS IT A STRING OF, the name
// 'string' does you, and anyone else, no good. Think about all your
// code like you are writing it for someone else, because when you come
// back to it in 6 months, you will be someone else, and you won't know
// what this means
NSString *string;
end edit
I would not be using NSUserDefaults this way. You have already parsed JSON into an archiveable object (NSMutableArray). In viewDidLoad, you should probably try doing something like:
-(void)viewDidLoad
{
// Load the array from a plist file
self.dataYouNeed = [NSMutableArray arrayWithContentsOfFile:#"someFileName.plist"];
// If we got back nil, that file didn't exist, so call 'reloadMain',
// do your parsing there THEN SAVE to a plist using:
//
// [myArray writeToFile:#"someFileName.plist"]
//
if(self.dataYouNeed == nil) [self reloadMain];
// Then do the exact same thing when you try to persist your selection...
// aka do not store a CSV string, just store an Array, and call writeToFile:
// when you want to save, and arrayWithContentsOfFile when you want to read
// it back in
}
On top of that, depending on where your data is coming from, I would move all of your data out of the JSON files and set it up in a plist, then you can ditch all of your parsing code.... :). Basically I am saying this is all a little too complicated for such a simple task, make your own life easier.
edit
You may have an issue with not using 'self.string', simply referring to 'string' is dangerous, you are creating a new reference everytime. This is most likely creating a memory leak. (ARC is not magic, it can not handle ALL memory management for you)
edit
Ok, so re-reading your code, I noticed a few things.
1. Why do you store your CSV string in the 'string' instance var?
This is somewhat redundant. At no point do you ever read from this variable without having set it in the few lines of code before. It should just be an NSString declared with in the scope of the method.
2. Are you expecting '_selectedItems' to have retained your reference to the 'selectedCategories' array on your AppDelegate?
You can not make this assumption, especially without having made a #property declaration. ARC does not know how to handle it and will probably be releasing the reference when you leave the view. The more likely possibility is that you are creating a memory leak every time you set that variable. You can also not guarantee that viewDidLoad will be called again to reset the reference. You should probably be setting this in viewWillAppear.
3. Which NSMutableArray are you experiencing a nil reference to?
If it is '_selectedItems', consider #2. If it is 'categorySelected', this is also probably being released when this view disappears. If this is really what you are trying to persist, then why are you not populating it from the viewDidAppear method. The only thing you do in viewDidAppear is set the 'string' variable (which is never actually read from, like #1 says). Did you mean to set 'categorySelected' here? I believe you meant to get your list from NSUserDefaults, then populate 'categorySelected' using that string's componentsSeparatedByString: method, which returns an array
Every time a user goes to some other view and comes back then
self.categorySelected = [[NSMutableArray alloc] init]; gets executed resulting it to an empty array.
First serialize the array when leaving the view:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:#[#"1",#"2",#"3"]];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"myarray"];
[[NSUserDefaults standardUserDefaults] synchronize];
Then deserialize it when you are back to that view:
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"myarray"];
NSArray *myarray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"MYARRAY %#", myarray);
Note: if key is not found then init a new array.
Can you try to change 'retain' to 'strong' here?
#property (nonatomic, retain) NSMutableArray *categorySelected;
I think the issue is, you are setting the selectedItems array in viewDidLoad method. Probably the viewDidLoad is working once.
Just add the following line in your viewWillAppear method:
_selectedItems = [(AppDelegate *)[[UIApplication sharedApplication] delegate] selectedCategories];

Resources