Editing an Entry

The edit feature in the data listing screen has two major steps: editing an entry and accessing Kii Cloud.

  • Use APIs in the iOS SDK to switch screens and edit an entry. You can skip the Editing an entry section below as this step does not involve any Kii Cloud features.

  • Update the entry data by Calling the Kii Cloud features. In this step, call Kii Cloud APIs to update the data on Kii Cloud according to the data obtained from the screen.

Check the source code below.

Swift

Objective-C

Editing an entry

The user edits income and expense data in the data listing screen. To add an entry, the user taps the "Add" button in the navigation bar. To update or delete an entry, the user taps it in the list.

The screen elements for editing an entry are configured in the storyboard as below.

The "addItem" segue and the "editItem" segue are set for switching from the data listing screen to the edit screen. The "addItem" segue is set to the "Add" button in the navigation bar and the"editItem" segue is programmatically used.

The "doneEditItem" segue is set as an unwind segue from the edit screen.

Initial entry values and edited data need to be passed between the data listing screen (BalanceListViewController) and the edit screen (EditItemViewController). These processes are implemented as below by following common methods for screen transitions in iOS apps.

  • The prepare(for:sender:) method is used to prepare for performing a segue. This method passes the data from the listing screen to the edit screen. The edit screen holds the passed data in an instance variable.

  • The DoneEditDelegate protocol implemented in the edit screen passes the data to the data listing screen. This way, the connection between the classes is kept simple because the protocol returns the edited data without any direct dependency from the child screen to the parent screen.

The following tasks are performed in Step :num1: to :num4: in the figure.

  1. The "addItem" segue is performed when the "Add" button is tapped in the BalanceListViewController class. When an entry in the list is tapped, the tableView(_:didSelectRowAt:) method is called to perform the "editItem" segue. Performing a segue involves calling the prepare(for:sender:) method.

  2. The prepare(for:sender:) method sets initial entry values for the edit screen. The method sets empty values for the "addItem" segue and the values in the KiiObject instance specified as a parameter for the "editItem" segue. The table below lists the variables set by the method.

    Variable Description
    mode The mode of the action. Tapping the "Add" button sets .Add for adding an entry. Tapping an entry in the list sets .Edit for editing an entry.
    objectId The ID of a KiiObject to edit. It is nil if an entry is added.
    name The description of the income or expense entry.
    type The type of the entry that is 1 for income and 2 for expense.
    amount The amount of the entry in cents. It is 100 times the amount that is displayed on the screen.
    delegate The DoneEditDelegate protocol of the BalanceListViewController class.
  3. An action is assigned to each of the "Save" button and the "Delete" button in the edit screen. A user action causes a corresponding action handler to be executed.

  4. Each action handler updates the data on Kii Cloud. Then it calls a method of the DoneEditDelegate protocol to update the array of KiiObjects in the BalanceListViewController class with the edited data.

Calling the Kii Cloud features

A KiiObject is added, updated, or deleted in the edit screen with the features of the Kii Cloud SDK. Tapping the "Save" button adds or updates a KiiObject and tapping the "Delete" button deletes a KiiObject.

Adding or updating a KiiObject

A KiiObject is added to a bucket in the current user's scope. The method to create a KiiObject is the same as that for Hello Kii.

A KiiObject is updated similarly. The difference is that the ID of an existing KiiObject ID is specified when the KiiObject is saved.

Kii Balance creates the following key-value pairs for a KiiObject.

  • name: The description of the income or expense entry.
  • type: The type of the entry that is 1 for income and 2 for expense.
  • amount: The amount of the entry in cents. It is 100 times the amount that is displayed on the screen.

Aside from the above pairs, predefined fields such as the creation time _created are automatically created.

See the code below for creating a KiiObject. Note that the excerpt is simplified.

  • @IBAction func saveClicked(_ sender: Any) {
      // Read input values.
      let amount = Int(Double(self.amountText.text!)! * 100)
      var name = self.nameText.text
      let type = self.type
    
      // Create a KiiObject instance.
      let bucket = KiiUser.current()?.bucket(withName: BalanceItem.Bucket)
      var object: KiiObject
      if self.mode == .Add {
        object = bucket!.createObject()
      } else {
        object = bucket!.createObject(withID: self.objectId!)
      }
      object.setObject(NSNumber(value: amount), forKey: BalanceItem.FieldAmount)
      object.setObject(name, forKey: BalanceItem.FieldName)
      object.setObject(NSNumber(value: type!.rawValue), forKey: BalanceItem.FieldType)
    
      // Call the Kii Cloud API for saving the KiiObject on Kii Cloud.
      object.save { (object, error) in
        if error != nil {
          let alert = KiiAlert.create(title: "Error", message: description)
          self.present(alert, animated: true, completion: nil)
          return
        }
    
        // Return to the data listing screen.
        self.closeKeyboard()
        if self.mode == EditItemViewMode.Add {
          self.doneEditDelegate.addItem(object: object);
        } else {
          self.doneEditDelegate.updateItem(object: object);
        }
        self.performSegue(withIdentifier: "doneEditItem", sender: nil)
      }
    }
  • - (IBAction)saveClicked:(id)sender {
      // Read input values.
      double amount = (int)([self.amountText.text doubleValue] * 100);
      NSString *name = self.nameText.text;
      int type = self.type;
    
      // Create a KiiObject instance.
      KiiBucket *bucket = [[KiiUser currentUser] bucketWithName:BalanceItemBucket];
      KiiObject *object;
      if (self.mode == EditItemViewModeAdd) {
        object = [bucket createObject];
      } else {
        object = [bucket createObjectWithID:self.objectId];
      }
      [object setObject:[NSNumber numberWithInt:amount] forKey:BalanceItemFieldAmount];
      [object setObject:name forKey:BalanceItemFieldName];
      [object setObject:[NSNumber numberWithInt:type] forKey:BalanceItemFieldType];
    
      // Call the Kii Cloud API for saving the KiiObject on Kii Cloud.
      [object saveWithBlock:^(KiiObject *object, NSError *error) {
        if (error != nil) {
          UIAlertController *alert = [KiiAlert createWithTitle:@"Error" andMessage:error.description];
          [self presentViewController:alert animated:YES completion:nil];
          return;
        }
    
        // Return to the data listing screen.
        [self closeKeyboard];
        if (self.mode == EditItemViewModeAdd) {
          [self.doneEditDelegate addItem:object];
        } else {
          [self.doneEditDelegate updateItem:object];
        }
        [self performSegueWithIdentifier:@"doneEditItem" sender:nil];
      }];
    }

When self.mode is .Add, an KiiObject is added. When self.mode is .Edit, an KiiObject is updated or deleted.

When Kii Balance adds a KiiObject, Kii Cloud automatically assigns a KiiObject ID. Therefore, the createObject() method of the KiiBucket class is called without a KiiObject ID specified. When a KiiObject is updated, the ID of the target KiiObject (self.objectId) is specified as the method argument.

When the save() method successfully completes, a method of the DoneEditDelegate protocol is called to update the array of KiiObjects under the control of the BalanceListViewController class.

Updating a KiiObject

The KiiObject is correctly updated even though the save() method and the BalanceListViewController instance handle different instances of the same KiiObject. The KiiObject is correctly updated on Kii Cloud as far as the KiiObject ID and the bucket name are correct in the URL for the REST API.

If the update on Kii Cloud is successful, the updateItem() method of the DoneEditDelegate protocol replaces a corresponding KiiObject instance in the BalanceListViewController instance.

Alternatively, you can get a KiiObject instance from the BalanceListViewController instance, update it, and call the save() method. However, you need to add a rollback step to this implementation. When an error occurs, the updated values in the KiiObject on the client need to be reset to the previous values.

Choosing the update method

Kii Cloud provides the following three methods for updating a KiiObject. Kii Balance uses the partial update method without overwrite check.

  • Full update without overwrite check

    This method completely replaces the key-value pairs in a KiiObject on the server with the key-value pairs sent from the client. Any existing pairs on the server will be deleted.

    This method does not check if another client has updated the KiiObject.

  • Partial update without overwrite check

    This method merges the key-value pairs in a KiiObject on the client to the key-value pairs on the server. As a result, any existing pairs on the server will remain if they are not updated by the set of pairs sent from the client.

    This method does not check if another client has updated the KiiObject.

  • Full update with overwrite check

    This method completely replaces the key-value pairs in a KiiObject on the server with the key-value pairs sent from the client. Any existing pairs on the server will be deleted.

    f another client has updated the KiiObject since it was downloaded to the client, an error will occur.

For more information about the behaviors of these methods, see Updating a KiiObject.

Kii Balance would correctly work with the partial update method because the keys in the key-value pairs sent from the client are always the same as those on the server.

Kii Balance does not perform overwrite check because there should be no overwrite error for data in the user scope. If your mobile app will write in a group scope or the application scope, it is safer to apply the update method with overwrite check because multiple users might update the same KiiObject at the same time.

When you design your mobile app, consider usage conditions before deciding the update method. Such conditions include if the update should be incremental and if concurrent access can occur.

Deleting a KiiObject

A KiiObject is deleted by its ID.

See the code below for deleting a KiiObject. Note that the excerpt is simplified.

  • func deleteItem() {
      // Create a KiiObject instance with its ID.
      let bucket = KiiUser.current()?.bucket(withName: BalanceItem.Bucket)
      let object = bucket!.createObject(withID: self.objectId!)
    
      // Call the Kii Cloud API for deleting the KiiObject on Kii Cloud.
      object.delete { (object, error) in
        if error != nil {
          let description = (error! as NSError).userInfo["description"] as! String
          let alert = KiiAlert.create(title: "Error", message: description)
          self.present(alert, animated: true, completion: nil)
          return
        }
    
        // Return to the data listing screen.
        self.doneEditDelegate.deleteItem(object: object);
        self.performSegue(withIdentifier: "doneEditItem", sender: nil)
      }
    }
  • - (void) deleteItem {
      // Create a KiiObject instance with its ID.
      KiiBucket *bucket = [[KiiUser currentUser] bucketWithName:BalanceItemBucket];
      KiiObject *object = [bucket createObjectWithID:self.objectId];
    
      // Call the Kii Cloud API for deleting the KiiObject on Kii Cloud.
      [object deleteWithBlock:^(KiiObject *object, NSError *error) {
        if (error != nil) {
          UIAlertController *alert = [KiiAlert createWithTitle:@"Error" andMessage:error.description];
          [self presentViewController:alert animated:YES completion:nil];
          return;
        }
    
        // Return to the data listing screen.
        [self.doneEditDelegate deleteItem:object];
        [self performSegueWithIdentifier:@"doneEditItem" sender:nil];
      }];
    }

The deletion process is similar to the update process. The target KiiObject is instantiated by its ID and then the delete(_:) method is called for deleting the KiiObject on Kii Cloud.

After the deletion is completed, the DoneEditDelegate protocol is used to delete a corresponding KiiObject instance in the array of KiiObjects of the BalanceListViewController class.


This is the end of the tutorial for iOS. For further analysis, go through the source code by using Class Structure as a reference.


What's Next?

Finally, we discuss tips for data design and considerations for implementation. Those would help you with designing a practical mobile app.

Go to Optimizing Application Design.