Getting the Data List
The user can list the entered income and expense entries and edit entries in the data listing screen. This topic explains the data listing process and the next topic explains the data editing process.
Class structure
The data listing screen consists of the classes shown in the class diagram below. The central class is the BalanceListViewController
class that inherits the UIViewController
class. It also implements the UITableViewDelegate
protocol and the UITableViewDataSource
protocol for handling the table view.
The BalanceListViewController
class accesses Kii Cloud and gets income and expense data as an array of KiiObjects
. The obtained KiiObjects
are stored in a variable of a BalanceListViewController
instance.
The TotalAmountTableViewCell
class and the BalanceItemTableViewCell
class are used for handling cells in the table view. These classes simply store fields in a cell as outlets.
Check the source code below.
Swift
Objective-C
- BalanceListViewController.h / BalanceListViewController.m
- BalanceItemTableViewCell.h / BalanceItemTableViewCell.m
- TotalAmountTableViewCell.h / TotalAmountTableViewCell.m
Getting the data list
In the data listing screen, KiiObjects that store income and expense data are obtained from Kii Cloud.
The following two methods are mainly used for getting data from Kii Cloud. Kii Balance uses the first method.
-
Querying KiiObjects in a specific bucket
You get KiiObjects that match certain conditions in a batch. This method is suitable for the data listing screen.
-
Specifying the ID of a target KiiObject
You save the ID of a certain KiiObject and get the KiiObject with the saved ID. This method is not suitable for the data listing screen because ID management is required.
Data is obtained within the viewDidLoad()
method. This method is called after the view is loaded.
The getItems()
method actually gets data. See how the query is started in the code block below.
The query gets all KiiObjects in the bucket in the ascending order of the creation time. The query is performed in the same way as that in Hello Kii. For more information, see the Hello Kii tutorial.
In order to pagenate the query result, the closure or block of the execute(_:with:)
method is not directly written as an argument but specified in a variable callback
. The types of arguments passed to the method are substantially the same as that in Hello Kii. For pagination, see the next section.
Pagination
Kii Balance provides a complete data listing feature by paginating the query result.
Pagination is the process of dividing the query result into pages and getting one page at a time, when the query result contains numerous items.
For example, if a query gets 420 KiiObjects, the result can be obtained in multiple pages as below.
You can get up to 200 entries in one page because the default value of the bestEffortLimit option is 200. However, as shown in the second result, various conditions such as the transfer size can decrease the number of returned entries even if there is another or more pages to get. Additionally, the last page might contain no entry.
The APIs in the above figure return values as follows.
-
For the first page
The
execute(_:with:)
method of theKiiBucket
class gets the first page (). The query result is obtained in theresults
argument in the callback as an array ofKiiObjects
.nextQuery
contains a query to get the next page because there are more pages to get. -
For the second page
The
execute(_:with:)
method gets the second page by usingnextQuery
that was returned when the first page was obtained (). As with the first query, the query result is obtained in theresults
argument.nextQuery
contains a query to get the next page because there is another page to get. -
For the third page
The
execute(_:with:)
method gets the third page by usingnextQuery
that was returned when the second page was obtained ().nextQuery
contains a value ofnil
. It means there is no more page and all the entries are obtained.
See how pagination is performed in the code block below. This block shows how the query that was started in the previous sample code is executed.
Note the following key points of the above processing.
- Pagination is implemented by recursively calling
KiiQueryResultBlock
.- The Swift version recursively calls the closure. Swift does not allow you to reference a variable that holds a closure when you define the variable. In order to implement the recursion, the code above declares the variable
callback
in thevar
statement, then in separate lines, sets the closure to it and references the variable. - The Objective-C version recursively calls the block. In order to implement the recursion, the code above declares the variable
callback
with the__block
keyword so that the variable can be referenced when the block is defined.
- The Swift version recursively calls the closure. Swift does not allow you to reference a variable that holds a closure when you define the variable. In order to implement the recursion, the code above declares the variable
- The
KiiProgress
class displays the progress. The progress indicator disappears when an error occurs and the mobile app stops processing the subsequent pages or when all the entries are obtained. - In order to recover from a possible error, obtained
KiiObjects
are not directly added toself.items
. The query result is saved inobjectList
, then added toself.items
in a batch when all the entries are obtained. SupposeKiiObjects
were directly added toself.items
. If an error occurred while getting the second page, only the entries obtained in the first page would remain inself.items
.
In order to ensure the quality of your mobile app, pay attention to the recovery processing mentioned in the last key point. Instabilities such as signal quality on the smartphone cause API errors because network access is required for executing the Kii Cloud APIs.
When designing your mobile app, you need to not only create error messages but also consider how the user can recover from errors. If your scenarios are insufficient, the user might encounter data inconsistency and/or find no way to operate the mobile app. You can test the error case mentioned in the last key point by decreasing the page size in the limit
property of query
and using a device in airplane mode in combination with breakpoints of the debugger.
If the list is obtained successfully, the obtained KiiObjecst
are added to self.items
under the control of the BalanceListViewController
instance. Then the "Refresh" button is replaced with the "Add" button in the navigation bar. If the list cannot be obtained, the user is supposed to tap the "Refresh" button to get the list.
In Kii Balance, pagination is implemented with the non-blocking API. In some cases, your implementation can be simpler by using Grand Central Dispatch (GCD) and the blocking API.
Preventing a memory leak in a callback
It is necessary to take measures against a memory leak in the recursion processing for pagination because a retain cycle occurs in the KiiQueryResultBlock
closure.
A query is executed with references to various objects. In this section, let us focus on the BalanceListViewController
instance, the getItems()
method, and the KiiQueryResultBlock
closure. As shown in the figure below, all of them are strongly referenced.
Reference causes a retain cycle. Without any countermeasure, a memory leak would occur.
The closure implementation uses the variable callback
for recursive calls. The closure captures a strong reference to this variable and creates a retain cycle to itself.
Kii Balance resolves the retain cycle by breaking the reference. To do so, a value of nil
is set to callback
after an error occurs or all entries are obtained.
The other references in the figure do not cause any retain cycle nor memory leak.
-
Reference
The local variable
callback
in thegetItems()
method has a strong reference to the closure. This reference does not cause any memory leak becausecallback
is deallocated when thegetItems()
method completes. -
Reference
The closure has a strong reference to the
BalanceListViewController
instance because the closure referencesself
.If you search information about referencing
self
from a closure on the Internet, you might find articles about retain cycles. Note that the implementation of Kii Balance does not cause any retain cycle. TheBalanceListViewController
instance does not have any instance variable that holds a reference to the closure and there is only the one-way reference from the closure to theBalanceListViewController
instance. In this case, no memory leak is caused.If the closure were managed in an instance variable of the
BalanceListViewController
instance, it would cause a retain cycle. To avoid a retain cycle,self
in the closure should be weakly referenced.
Displaying data in the table view
The tableView(_:cellForRowAt:)
method creates and returns a cell to display an entry.
As with Hello Kii, this method processes data stored in a KiiObject
instance for display and returns a cell for the entry according to the specification of the table view.
See how cells are created in Kii Balance in the code block below.
This code associates each field of a KiiObject
instance with a display item.
The TotalAmountTableViewCell
class is used to create a cell for the balance that is displayed at section 0 of the table view. The BalanceItemTableViewCell
class is used to cells for the list of income and expense items displayed at the subsequent sections.
What's Next?
Let us review how to edit entries in the data listing screen.
Go to Editing an Entry.