Objective-C NSMutableArray removeObjectAtIndex: crashes - objective-c

I have a view controller with a table view where you can delete the cells. I have another class which handles things called bookmarks, the class is called BookmarkHandler. There are methods where you can upload a bookmark, get the whole bookmark array and delete a bookmark. Here is the class as follows:
+ (NSMutableArray *)bookmarkCollection {
NSMutableArray *bookmarkCollection = [[NSUserDefaults standardUserDefaults] objectForKey: #"bookmarks"];
if (!bookmarkCollection) {
bookmarkCollection = [[NSMutableArray alloc] init];
}
return bookmarkCollection;
}
+ (void)deleteBookmark: (NSIndexPath *)indexPath {
NSMutableArray *bookmarkCollection = [[NSUserDefaults standardUserDefaults] objectForKey: #"bookmarks"];
[bookmarkCollection removeObjectAtIndex: indexPath.row];
[[NSUserDefaults standardUserDefaults] setObject:bookmarkCollection forKey: #"bookmarks"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
+ (void)uploadBookmark:(NSDictionary *)singleBookmark {
NSMutableArray *bookmarkCollection = [[NSUserDefaults standardUserDefaults] objectForKey: #"bookmarks"];
if (!bookmarkCollection) {
bookmarkCollection = [[NSMutableArray alloc] init];
}
NSMutableDictionary *bookmark1 = [[NSMutableDictionary alloc] initWithDictionary: singleBookmark];
NSMutableDictionary *bookmark2 = [[NSMutableDictionary alloc] initWithDictionary: singleBookmark];
NSNumber *number1 = [[NSNumber alloc] initWithInt: 1];
NSNumber *number2 = [[NSNumber alloc] initWithInt: 2];
[bookmark1 setObject:number1 forKey: #"bookmarkTag"];
[bookmark2 setObject:number2 forKey: #"bookmarkTag"];
[bookmarkCollection addObject: bookmark1];
[bookmarkCollection addObject: bookmark2];
[[NSUserDefaults standardUserDefaults] setObject:bookmarkCollection forKey: #"bookmarks"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
The bookmark collection which is a mutable array, is populated by dictionaries which have name and date objects/keys. These name and dates is what populates the table view cell title's in the other view controller. The number of cells in the table view is determined by the [[BookmarkHandler bookmarkCollection] count];
In the other view controller you can delete the table view cells, so what I do is I implement the delegate method:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[BookmarkHandler deleteBookmark: indexPath];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject: indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}
}
So, when I delete a cell, I delete a bookmark from BookmarkHandler by calling the deleteBookmark: and delete the row from the table view. But sometimes there is a crash on this line:
[bookmarkCollection removeObjectAtIndex: indexPath.row];
But there is no crash log, and I have added an All Exceptions breakpoint.
Is there something I am doing wrong? Thanks for help...

The problem is here:
NSMutableArray *bookmarkCollection = [[NSUserDefaults standardUserDefaults] objectForKey: #"bookmarks"];
NSUserDefaults doesn't save a mutable array, it just saves it as NSArray.
So take the mutable copy of it:
NSMutableArray *bookmarkCollection = [[[NSUserDefaults standardUserDefaults] objectForKey: #"bookmarks"] mutableCopy];

You should check the indexPath.row to make sure it is NOT out of boundry. After this maybe you can find the reason.
if (indexPath.row>=0 && indexPath.row<bookmarkCollection.count) {
[bookmarkCollection removeObjectAtIndex: indexPath.row];
} else {
NSLog(#"indexPath.row is out of boundry of bookmarkCellection size: %d", bookmarkCollection.count);
}

Related

Only first object of NSMutableArray is stored in NSUserDefaults

I am trying to store a queue of UILocalNotification to solve the limit problem. I used this approach and it does archive and unarchive my object but only the first one.
How do I archive all objects from my NSMutableArray?
Code
// init/unarchive queue
if (self.queue == nil)
{
// try loading stored array
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"LocalNotificationQueue"];
if (dataRepresentingSavedArray != nil) {
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if (oldSavedArray != nil) {
self.queue = [[NSMutableArray alloc] initWithArray:oldSavedArray];
}
else
{
self.queue = [[NSMutableArray alloc] init];
}
}
else
{
self.queue = [[NSMutableArray alloc] init];
}
}
// add
[self.queue addObject:notif];
// store queue
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:self.queue] forKey:#"LocalNotificationQueue"];
If I add items 1,2,3. Restart and load. I have only 3.
Add 1,2,3. Restart and load. I have 3, 1, 2.
If it matters. This is a Phonegap/Cordova CDVPlugin.
After
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:self.queue] forKey:#"LocalNotificationQueue"];
You need to call
[[NSUserDefaults standardUserDefaults] synchronize]
To save the user defaults.
Try this, without the NSKeyedArchiver:
[[NSUserDefaults standardUserDefaults] setObject: self.queue] forKey:#"LocalNotificationQueue"];

How to Read String from NSUserDefaults

I have an object at the root of plist that stores objects and keys (one of which is input from a user textfield). I have no problem writing and creating structure; it's trying to read values that causing problem. Below code I used to write the data, can someone tell me how to read the string [val] and [myKey]?
thank you!
#define defaultValue #"Your Name"
#define myKey #"PersonName"
- (void)textFieldAction:(id)sender {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *val;
// Get the new value from the textfield
val = [myTextField stringValue];
if ([val isEqualToString:defaultValue]) {
[defaults removeObjectForKey:myKey];
} else {
// [defaults setObject:val forKey:myKey];
NSDictionary *my_dict = [NSDictionary dictionaryWithObjectsAndKeys:
#"Name", #"PersonName",
#"Email", #"PersonEmail",
#"Dept", #"PersonDept",
#"Job", #"PersonJob",
val, myKey,
#"Status", #"PersonStatus",
nil];
[defaults setObject: my_dict forKey: #"Person"];
[[NSUserDefaults standardUserDefaults] registerDefaults:my_dict];
I would like to automatically populate the textfield with the [val], for [myKey], if the user has the info already in the plist. Here's the code I'm trying to use for that:
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
NSUserDefaults *defaults = [[NSUserDefaults standardUserDefaults] objectForKey:myKey];
NSString *val = [defaults stringForKey:myKey];
if (val == nil) val = defaultValue;
[myTextField setStringValue:val];
You can write the value into NSUserDefault like the following:
[[NSUserDefaults standardUserDefaults] setValue:[myTextField stringValue] forKey:#"Person"];
And read it later like the following:
[myTextField setStringValue:[[NSUserDefaults standardUserDefaults] stringForKey:#"Person"];
So you can simplify your code into:
- (void)textFieldAction:(id)sender {
[[NSUserDefaults standardUserDefaults] setValue:[myTextField stringValue] forKey:#"Person"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
You can set a default value with the registerDefaults:. And when you can retrieve that value simply by calling:
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
// ...
[[NSUserDefaults standardUserDefaults] registerDefaults:#{#"Person", defaultValue}]
NSString *value = [[NSUserDefaults standardUserDefaults] stringForKey:#"Person"];
[myTextField setStringValue:value];
// ...
}
If I understand correctly, you should be able to do what you need by simply doing:
NSString * val = [[NSUserDefaults standardUserDefaults] objectForKey: myKey];
[textfield setText: val];
NSUserDefaults Reference Page
myTextField.text = [[NSUSerDefaults standardUserDefaults] objectForKey:myKey];

How to store NSMutableArray to NSUserDefaults and show results in TableView?

I want to save the Notifications which come from server in my application and also make a User Interface to give users the ability of chosing which Notification(message) to read. In a scheduled method my client controls for changes inside the server and the communication is in JSON format. I have parsed it and can see the results in NSLog(#"....",..) too. I also control the status of message from the server, if the status equals to 1 i will save the message and add a node to TableView.. Now, can anyone help me about how to transmit datas in NSMutableArray both to NSUserDefaults and TableView? I can Share code or JSON representation too if you want..
It will be better if you could explain with some code.. Thanks
I decided to share some of my code,
as i have writen under the code too, i want to display NSMutableArray in UITableView
`-(IBAction)Accept:(id)sender
{ userName=[[NSString alloc] initWithString:userNameField.text ];
[userNameField setText:userName];
NSUserDefaults *userNameDef= [NSUserDefaults standardUserDefaults];
[userNameDef setObject:userName forKey:#"userNameKey"];
password =[[NSString alloc] initWithString:passwordField.text];
[passwordField setText:password];
NSUserDefaults *passDef=[NSUserDefaults standardUserDefaults];
[passDef setObject:password forKey:#"passwordKey"];
serverIP=[[NSString alloc] initWithString: serverField.text];
[serverField setText:serverIP];
NSUserDefaults *serverDef=[NSUserDefaults standardUserDefaults];
[serverDef setObject:serverIP forKey:#"serverIPKey"];
[userNameDef synchronize];
[serverDef synchronize];
[passDef synchronize];
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"BNTPRO "
message:#"Your User Informations are going to be sent to server. Do you accept?"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:#"Cancel", nil];
[message show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"OK"])
{
if([userNameField.text isEqualToString:#""]|| [passwordField.text isEqualToString:#""] || [serverField.text length]<10)
{
UIAlertView *message1 = [[UIAlertView alloc] initWithTitle:#"BNTPRO "
message:#"Your User Informations are not defined properly!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[message1 show];
[userNameField resignFirstResponder];
[passwordField resignFirstResponder];
return;
}
//## GET code to here**
NSString *str1=[#"?username=" stringByAppendingString:userNameField.text];
NSString *str2=[#"&password=" stringByAppendingString:passwordField.text];
NSString *str3=[str1 stringByAppendingString:str2];
NSString *str4 =[#"http://" stringByAppendingString:serverField.text];
NSURL *url=[NSURL URLWithString:[str4 stringByAppendingString:[#"/ipad/login.php" stringByAppendingString:str3]]];
NSLog(#"%#\n",url);
//get the url to jsondata
NSData *jSonData=[NSData dataWithContentsOfURL:url];
if (jSonData!=nil) {
NSError *error=nil;
id result=[NSJSONSerialization JSONObjectWithData:jSonData options:
NSJSONReadingMutableContainers error:&error];
if (error==nil) {
NSDictionary *mess=[result objectForKey:#"message"];
NSDictionary *messContent=[mess valueForKeyPath:#"message"];
NSDictionary *messDate=[mess valueForKeyPath:#"date"];
NSDictionary *messID=[mess valueForKeyPath:#"ID"];
NSDictionary *messStatus=[mess valueForKey:#"status"];
NSLog(#"%# *** Message %# \n Message Content: %# \n Mesage ID: %# \n Message Date: %#\n \nilhancetin MessageSatus: %#", result, mess, messContent, messID,messDate,messStatus);
NSString*key1=[ result objectForKey:#"key" ];
NSString *s1=[#"http://" stringByAppendingString:serverField.text];
NSString *s2=[s1 stringByAppendingString:#"/ipad/button.php"];
NSURL *url2=[NSURL URLWithString:[s2 stringByAppendingString:[#"?key=" stringByAppendingString:key1]]];
NSLog(#"\n%#\n",url2 );
NSData *data2=[NSData dataWithContentsOfURL:url2];
id result2=[NSJSONSerialization JSONObjectWithData:data2 options:NSJSONReadingMutableContainers error:nil];
NSMutableArray *mesID = [NSMutableArray array];//saving meesages to NSMutableArray
NSMutableArray *status = [NSMutableArray array];
// i logged here and it saves the data, now i want to display my data in table view
`
save it in NSUserDefaults:
[[NSUserDefaults standardUserDefaults]setObject:yourArray forKey:#"theArray"];
get it from NSUserDefaults:
[[NSUserDefaults standardUserDefaults]objectForKey:#"theArray"];
setting values from an array to UITableViewCell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TheCell";
UITableViewCell *_cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (_cell == nil) {
_cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
_cell.textLabel.text = [yourArray objectAtIndex:indexPath.row];
return _cell;
}
Hope it helps
update
don't have a Mac nearby at the moment, so my answer might be a bit sloppy.
In your header file don't forget to add UITableViewDelegate and UITableViewDataSource, so it will look somewhat like that:
#interface yourController : UIViewController <UITableViewDelegate, UITableViewDataSource, ... some others if you need it ...> {
then in the implementation file(.m) you can just start typing
-tableview
and then use the autocompletion to get the methods that you need. You will most probably need these 3:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (void)tableView: (UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath
depending on the needs of your app you might need more of them, but these 3 should be there.
For more info about UITableView please check that link: http://developer.apple.com/library/ios/#documentation/uikit/reference/UITableView_Class/Reference/Reference.html

cancel button on search bar does not cancel correctly

I have a search bar, i can search now, but when I enter a text to search, and click the cancel button. It does not give me back my first stage, meaning full of the items in the table.
For example: I search the item with word: a, it gives me all the a items, yes, it is right now, but when i hit the cancel button, i want the programme gives me all the items exist, not just a items.
Here is the code: please help me out. Thank you so much.
- (void)searchBarCancelButtonClicked:(UISearchBar *)aSearchBar
{
searchBar.text = #"";
[searchBar resignFirstResponder];
letUserSelectRow = YES;
searching = NO;
self.tableView.scrollEnabled = YES;
NSLog(#"what text after cancel now: %#", searchBar.text);
[self.tableView reloadData];
}
- (NSMutableArray *) searchTableView {
NSString *searchText = searchBar.text;
NSLog(#"search text: %#", searchText);
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
NSMutableArray *tempArr = [[NSMutableArray alloc] init];
for (NSDictionary *dTemp in arrayData)
{
NSString *tempStr = [dTemp objectForKey:#"url"];
NSLog(#"sTemp string: %#",[ NSString stringWithFormat:#"%#", tempStr]);
NSRange titleResultsRange = [tempStr rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
{
NSLog(#"1 count :%d", [resultArray count]);
[resultArray addObject:dTemp];
NSLog(#"2 count :%d", [resultArray count]);
[tempArr addObject:resultArray];
[resultArray release];
resultArray = [NSMutableArray new];
}
}
if (resultArray != nil) {
[resultArray release];
}
return tempArr;
}
- (void)searchBar:(UISearchBar *)aSearchBar textDidChange:(NSString *)searchText
{
NSLog(#"what text after cancel now: %#", searchBar.text);
if([searchText length] > 0) {
[sortedArray removeAllObjects];
searching = YES;
letUserSelectRow = YES;
self.tableView.scrollEnabled = YES;
NSMutableArray *searchArray = [self searchTableView];
sortedArray = [[NSMutableArray alloc] initWithArray:searchArray copyItems:YES];
for (int i = 0; i<[sortedArray count]; i++) {
NSLog(#"this is the search array: %#", [[sortedArray objectAtIndex:i] class]);
}
NSLog(#"sorted array: %d", [sortedArray count]);
}
else {
searching = NO;
letUserSelectRow = NO;
self.tableView.scrollEnabled = NO;
}
[self.tableView reloadData];
}
You don't need to override any of UISearchBar methods to accomplish this. The new way of doing this relies on the UISearchDisplay controller instead (specifically on shouldReloadTableForSearchString).
Declare your view controller to conform to UISearchDisplayDelegate protocol, and keep two instance variables: your model as NSArray (all data) and a filtered array as NSMutableArray (a subset of your data). The code you presently have in "searchTableView" would filter the content of the model and place it into the filtered NSMutableArray. Then you would override the following UITableView methods: -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section and -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. In each, before returning, make a comparison to determine whether your tableView argument is equal to self.searchDisplayController.searchResultsTableView. If it is, the user is looking at the filtered list and your should use the content of the filtered NSMutableArray to create the view, otherwise, the user is looking at the whole data set and you should use the content of the NSArray that holds your model. Take a look at the following Apple code for a simple example of what I described:
http://developer.apple.com/library/ios/#samplecode/TableSearch/Introduction/Intro.html

iPhone:Store & Retrieve NSMutableArray object

appDelegate.categoryData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
categoryStr, #"name",image ,#"image", nil];
[appDelegate.categories addObject:appDelegate.categoryData];
NSLog(#"Category Data:--->%#",appDelegate.categories);
I am successfully added object mutabledictionary into mutuablearray but I want to store that array and retrieve when application is launched so please give me idea to develop this functionality.
Thanks in advance.
//you can save array in NSUserDefaults like below
if ([[NSUserDefaults standardUserDefaults] valueForKey:#"detail"]==nil)
{
NSMutableDictionary *dic_detail = [NSMutableDictionary dictionaryWithObjectsAndKeys:
categoryStr, #"name",image ,#"image", nil];
NSMutableArray *ary_detail = [[NSMutableArray alloc] init];
[ary_detail addObject:dic_detail];
[[NSUserDefaults standardUserDefaults] setObject:ary_detail forKey:#"detail"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
//if you want read that array you can read like below in your app
NSMutableArray *array = [[NSMutableArray alloc] initWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"detail"]];
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"applicationDidEnterBackground = %#",[[NSUserDefaults standardUserDefaults] objectForKey:#"detail"]);
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
NSLog(#"applicationWillEnterForeground = %#",[[NSUserDefaults standardUserDefaults] objectForKey:#"detail"]);
}
in my log its printing like this
applicationDidEnterBackground = (
{
image = img1;
name = cat1;
}
applicationWillEnterForeground = (
{
image = img1;
name = cat1;
}
Perhaps something like this:
NSMutableArray * array = [NSMutableArray array];
appDelegate.categoryData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
categoryStr, #"name",
image ,#"image",
array, #"array", nil];
Something like:
On Application launch:
[[NSUserDefaults standardUserDefaults] registerDefaults:
[NSDictionary dictionaryWithObjectsAndKeys:
[NSArray new], #"StoredArray", nil]];
In your class that "owns" control of the array:
- (void)setArray:(NSMutableArray *)array {
[[NSUserDefaults standardUserDefaults] setObject:array forKey:#"StoredArray"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSArray*)array {
return [[[NSUserDefaults standardUserDefaults] arrayForKey:#"StoredArray"] mutableCopy];
}
Hope this helps!