As promised earlier, I’m going to tell you how to change data in an iOS application and apply changes to the DA Server. As client application we will use the iOS Relativity Client we have previously created.

In the course of this session we will try to implement an editor for changing data from the Orders table. We will also use UIPickerView & UIDatePicker for changing the Order’s fields.
The source code can be obtained from here.
Let’s start!

The first thing we need to do is to add a new editor view and its controller.
After performing File->New…->New File, select the “UIViewController subclass” item. As the base class we will use the UITableViewController.
Now we need to add an edit button to the existing OrdersViewController. Clicking on this button should open an editor for the selected row.

-(void)viewDidLoad {[super viewDidLoad];   // create & set Edit button UIBarButtonItem *actionButton =[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(edit:)]; [[self navigationItem] setRightBarButtonItem:actionButton]; [actionButton release]; ... }   -(IBAction)edit:(id)aSender {   OrderEditorController *editor =[[[OrderEditorController alloc] initWithStyle:UITableViewStyleGrouped] autorelease];   // set OrderViewController as the delegate to be able to notify it about the results of the editing process[editor setDelegate:self];   // find the currently selected rowNSIndexPath*path =[self.tableView indexPathForSelectedRow]; NSInteger idx =[path indexAtPosition:1]; DADataTableRow *row =[orderRows objectAtIndex:idx];   // and set it for our editor [editor setOrderRow:row];   // push the editor view to be a topmost view[[self navigationController] pushViewController:editor animated:YES]; }   // delegate the method that the editor will call for applying changes-(void)endEditWithOk:(DADataTableRow *)row {// applying changes on Relativity server[[DataAccess sharedInstance] applyUpdatesForTable:[row table]]; // reload view for synchronizing client-side data[[self tableView] reloadData]; }   // delegate the method that the editor will call for canceling changes-(void)endEditWithCancel:(DADataTableRow *)row {// cancel the change[row cancel]; // reload table[[self tableView] reloadData]; }
Now it is time to start implementing our *OrderEditorController*. The *Orders* table has several fields we can edit: *OrderDate* and *OrderStatus*, but we can also change the *client* of the order, though I don’t think we need to change the document in such a radical way ;) .

Note: There is one small problem we need to resolve first. Unfortunately, the current version of the PCTrade database doesn’t have an OrderStatus reference, so the Orders table only has its integer values (which by default are 0).
Let’s assume that we need to implement several available document statuses. A new uncompleted order usually has the “New” status. When it is completed but not payed yet, it has a “Pending” status. When we received the money, it changes to “Closed” and finally, there is the additional “Deleted” status. In the future we probably may want to track the document history, so we loose any of the orders, even if they were deleted. So our database will keep those orders, but with the “Deleted” status. In order to keep the correspondence between the available status numbers and their names, we will add a new NSArray into our DataAccess class as shown below.

/////////////////////////////// DataAccess.h@interface DataAccess :NSObject { ... NSArray*orderStatusNames; } ... @property(readonly)NSArray*orderStatusNames; -(NSUInteger)idForOrderStatusName:(NSString*)name; -(NSString*)nameForOrderStatus:(NSUInteger)status; @end   /////////////////////////////// DataAccess.m   -(id) init { self =[super init]; if(self){ ...   orderStatusNames =[[NSArray alloc] initWithObjects:@"New", @"Pending", @"Closed", @"Deleted", nil];   }return self; }   -(void) dealloc { ... [orderStatusNames release], orderStatusNames =nil; [super dealloc]; }     -(NSString*)nameForOrderStatus:(NSUInteger)status {   if(status >=[orderStatusNames count])return@"Unknown state";   return[orderStatusNames objectAtIndex:status]; }   -(NSUInteger)idForOrderStatusName:(NSString*)name {   for(NSUInteger i =0; i < [orderStatusNames count]; i++){NSString*statusName =[orderStatusNames objectAtIndex:i]; if([statusName compare:name]== NSOrderedSame)return i; }return0; }   -(NSArray*)orderStatusNames {   return orderStatusNames; }
Now we are ready to start the editor implementation. Please open the *OrderEditorController.h* file and add the following:
@interface OrderEditorController : UITableViewController {   DADataTableRow *orderRow;   UIBarButtonItem *saveButton; UIBarButtonItem *cancelButton;   id delegate; NSDateFormatter*df; }   @property(retain) DADataTableRow *orderRow; @property(assign)id delegate;   -(IBAction)save:(id)aSender; -(IBAction)cancel:(id)aSender; @end
Our editor should have references to the edited row, the delegate reference, the save & cancel buttons and the appropriate methods. This will implement them:
#pragma mark - View lifecycle   -(void)viewDidLoad {[super viewDidLoad];   // create & add cancel button cancelButton =[[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(cancel:)]; self.navigationItem.leftBarButtonItem = cancelButton;   // create & add save button saveButton =[[UIBarButtonItem alloc] initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(save:)]; self.navigationItem.rightBarButtonItem = saveButton;   // we will also need this formatter for converting NSDate to NSString df =[[NSDateFormatter alloc] init]; [df setDateStyle:NSDateFormatterMediumStyle]; }   -(IBAction)save:(id)aSender {   if([delegate respondsToSelector:@selector(endEditWithOk:)])[delegate endEditWithOk:orderRow];   [self.navigationController popViewControllerAnimated:YES];   }   -(IBAction)cancel:(id)aSender {   if([delegate respondsToSelector:@selector(endEditWithCancel:)])[delegate endEditWithCancel:orderRow];   [self.navigationController popViewControllerAnimated:YES]; }
In the majority of cases, we can represent the editor view as the table view, where each cell represents the field name at the left side and the current value at the right side. We can also group certain fields together based on general meaning or application logic. To do that we have to initialize our OrderEditorController with the *UITableViewStyleGrouped* style. When the user taps on a certain cell, we will need to allow him to edit that value.

So now we need to configure our table view as shown below:

  #pragma mark - Table view data source   -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {// we want to have two groups, // one for OrderDate & OrderStatus, and one for the Client field return2; }   -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {switch(section){case0:// the first section will have two cells (showing OrderDate & OrderStatus)return2; case1:// the second section will have a single cell (showing the Client field)return1;   default:return0; }}   staticNSString*TABLE_CELL_ID =@"OrderEditorCell";   -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {   UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:TABLE_CELL_ID]; if(cell ==nil){ cell =[[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:TABLE_CELL_ID] autorelease]; }   // Configure the section =[indexPath section]; int index =[indexPath indexAtPosition:1]; switch(section){case0:{// for the first section ...switch(index){case0:{// configure the cell to show the OrderDate field[[cell textLabel] setText:@"Order Date:"]; NSDate*value =[orderRow valueForKey:@"OrderDate"]; NSString*valueAsString = value ? [df stringFromDate:value]:@"Click to select"; [[cell detailTextLabel] setText:valueAsString]; break; }case1:{// configure the cell to show the OrderStatus field[[cell textLabel] setText:@"Order Status:"]; NSNumber*value =[orderRow valueForKey:@"OrderStatus"]; NSString*valueAsString = value ? [[DataAccess sharedInstance] nameForOrderStatus:[value intValue]]:@"Click to select"; [[cell detailTextLabel] setText:valueAsString]; break; }}break; }case1:{// for the second sectionswitch(index){case0:{// configure the cell to show the Client field[[cell textLabel] setText:@"Client:"]; NSString*value =[orderRow valueForKey:@"CustomerName"]; NSString*valueAsString = value ? value :@"Click to select"; [[cell detailTextLabel] setText:valueAsString]; break; }}break; }default:break; }[cell setNeedsDisplay]; return cell; }
Now, when you compile and run the project, you should see something like the following:
[![]( "OrderEditorView")](
Next we can start to implement the editing for the *OrderDate* and *OrderStatus* fields. For editing dates we can use the really good *NSDatePicker* control with the appropriate configuration. For editing the *OrderStatus* we can use our custom picker populated with available statuses. I also want the picker view to appear at the bottom of the current editor using animation, like the standard *Contacts* application does. Having spent some time with Google, I’ve found an interesting [Data Cell sample]( "Data Cell sample") with the required functionality. I’ve just slightly adjusted it for our purposes, namely to support animation for custom pickers.
-(void)viewDidLoad {   ... // create & configure our DatePicker datePickerView =[[UIDatePicker alloc] init]; [datePickerView setDatePickerMode:UIDatePickerModeDate]; [datePickerView addTarget:self action:@selector(pickerDidChange) forControlEvents:UIControlEventValueChanged];   pickerDoneButton =[[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(pickerDone)]; pickerCancelButton =[[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStyleDone target:self action:@selector(pickerCancel)]; }   #pragma mark - Common picker routines-(void)showPicker:(UIPickerView *)picker {   if(!picker)return;   if(activePicker)[self hidePicker:activePicker];   activePicker = picker;   if(picker.superview ==nil){[self.view.window addSubview:picker];   // size up the picker view to our screen and compute the start/end // frame the origin for our slide-up animation// compute the start frame CGRect screenRect =[[UIScreen mainScreen] applicationFrame]; CGSize pickerSize =[picker sizeThatFits:CGSizeZero]; CGRect startRect = CGRectMake(0.0, screenRect.origin.y + screenRect.size.height, pickerSize.width, pickerSize.height); picker.frame = startRect;   // compute the end frame CGRect pickerRect = CGRectMake(0.0, screenRect.origin.y + screenRect.size.height - pickerSize.height, pickerSize.width, pickerSize.height); // start the slide-up animation[UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.3];   // we need to perform some post operations after the animation is complete[UIView setAnimationDelegate:self];   picker.frame = pickerRect;   // shrink the table's vertical size to make room for the date picker CGRect newFrame = self.tableView.frame; newFrame.size.height -= picker.frame.size.height; self.tableView.frame = newFrame; [UIView commitAnimations];   // add the "Done" & "Cancel" buttons to the nav bar self.navigationItem.rightBarButtonItem = pickerDoneButton; self.navigationItem.leftBarButtonItem = pickerCancelButton; }}   -(void)removePicker:(UIPickerView *)picker {   if(!picker)return;   if(picker.superview !=nil){[picker removeFromSuperview]; }}   -(void)hidePicker:(UIPickerView *)picker {   if(!picker)return;   activePicker =nil;   CGRect screenRect =[[UIScreen mainScreen] applicationFrame]; CGRect endFrame = picker.frame; endFrame.origin.y = screenRect.origin.y + screenRect.size.height;   // start the slide-down animation[UIView beginAnimations:nil context:picker]; [UIView setAnimationDuration:0.3];   // we need to perform some post operations after the animation is complete[UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];   picker.frame = endFrame; [UIView commitAnimations];   // grow the table back again in vertical size to make room for the date picker CGRect newFrame = self.tableView.frame; newFrame.size.height += picker.frame.size.height; self.tableView.frame = newFrame;   // remove the "Done" button and restore the nav bar self.navigationItem.rightBarButtonItem = saveButton; self.navigationItem.leftBarButtonItem = cancelButton;   // deselect the current table row//NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];//[self.tableView deselectRowAtIndexPath:indexPath animated:YES];   }   -(void)animationDidStop:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context {[self removePicker:context]; }   // called when we click the Done button-(void)pickerDone {   // change the dataif(activePicker ==(UIPickerView *)datePickerView)[orderRow forKey:@"OrderDate"];   // hide picker[self hidePicker:activePicker]; // reload table view to show changes[[self tableView] reloadData]; }   // called when we click on the Done button-(void)pickerCancel {// just hide the picker[self hidePicker: activePicker]; [[self tableView] reloadData]; }   // the following method will sync the current picker value with the value in the table view-(void)pickerDidChange {   NSIndexPath*indexPath =[self.tableView indexPathForSelectedRow]; UITableViewCell *cell =[self.tableView cellForRowAtIndexPath:indexPath];   if(activePicker ==(UIPickerView *)datePickerView) cell.detailTextLabel.text =[df]; }
Now we need to launch the *UIDatePicker* when a user taps on the *OrderDate* field:
-(void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {   [self hidePicker:activePicker]; int section =[indexPath section]; int index =[indexPath indexAtPosition:1];   switch(section){case0:{switch(index){case0:{ =[orderRow valueForKey:@"OrderDate"]; [self showPicker:(UIPickerView *)datePickerView]; break; }case1:{   break; }}}}}
When you compile and run the application, you should get something like the following:
[![]( "DatePicker in action")](
Finally, we need to implement our custom *UIPickerView*, which is quite simple. At the beginning we need to create a custom UIPickerView and set its data source and delegate properties:
-(void)viewDidLoad { ... statePickerView =[[UIPickerView alloc] init]; [statePickerView setDataSource:self]; [statePickerView setDelegate:self]; [statePickerView setShowsSelectionIndicator:YES]; }
Then we need to add a support *UIPickerViewDataSource* protocol to our editor class and implement several methods, as shown below:
  #pragma mark - Custom Picker routines   // callback method for filling content for our custom statuses picker-(NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {   if(pickerView == statePickerView)return[[DataAccess sharedInstance] nameForOrderStatus:row]; return@""; }   -(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{   // it should be a simple picker with a single componentif(pickerView == statePickerView)return1; return0; }   -(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {   if(pickerView == statePickerView)return[[[DataAccess sharedInstance] orderStatusNames] count]; return0; }
Change the tableView:didSelectRowAtIndexPath: method to show our state picker:
#pragma mark - Table view delegate   -(void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath { ... switch(section){case0:{switch(index){case0:{ ... }case1:{[self showPicker:statePickerView]; break; }}}}}
That’s all. Now when you tap on the status cell of the editor, you should get something like this:
[![]( "StatusPicker in action")](
You can choose a new status, then click the done button and finally save the current order.

I’m slightly worried that you may find this blog post a little confusing and not easy to reproduce. We wrote quite a lot of code here, and I deliberately skipped some minor details, trying to make it easier, but you can download the whole source code from here and play with the project yourself.

Thank you for your attention ;)