قالب وردپرس درنا توس
Home / IOS Development / Upgrade TableViews with Loading State in Swift

Upgrade TableViews with Loading State in Swift



@Javi Briefly mentioned the drug approach to handling table views at the Swift user group meeting the other night and chose a enum representing the state of the table as loading, failed or loaded with the corresponding type (data for the table view). Here is a simple example:

  enum    TableState    [
      case    Loading 
      case    Failed 
      case    Items ([String]) 
 } 

If you only use a matrix (or optional matrix) for table data, there is only so much you can say about the state of the operation to collect and insert data for the table view. I would admit tracking this kind of thing like a Bool property on display control ̵

1; hasLoadedData or something – but it's cluttered and it's not immediately clear what data loading you're tracking.

It would be nice to be able to derive a state of the table solely from the data structure. Previously, we could have written table display code that retrieved data from an optional system, and let . Some state indicate that the data has not yet loaded and some . Some state (even with an empty order) means that the data is loaded.

But it's more than just a loading and loading mode on most asynchronously loaded table views. Generally, we will track if the data has not been downloaded for some reason (no network connection, server error codes, etc.) And show some useful message in that case, so the user is not waiting for something to happen. Now we have added a third state and maybe a loadedDataError optional to our display controller and it starts to make your viewer sad. 😢

Simplify with Enums and Protocols

The enumet above goes a long way towards making view control more readable and represents the state of table overview data. But we end up with many switches in our code that are cluttered. It is a prerequisite for the idea that enum switches should never exist outside the enumeration definition (I'm not 100% aboard this idea, but at least this makes our code more readable). So, let's expand our enum slightly:

  enum    TableStateString    [
      case    Loading 
      case    Failed 
      case    Items ([String]) [19659026]          Int       {
          Breaks ~~ POS = HEADCOMP    Self    {
          case    la    .Items 
              return 
          items.count 
          standard : 
              return    1 
        } 
    } 

      FUNC    value  (row:    ] [19659000]]          {
          {
          Breaks ~~ POS = HEADCOMP    Self    {
          Case    .Loading: 
              Return 
              "Loading ..." 
          case    .Failed: 
              return    "Failed" 
          case    la    .Items (elements): 
              la    item    =    items [row] 
              return    item 
        } 
    } 
} 

We have added a calculated property to get the number of rows to display and a method to return th e value for a particular row.

Now we can use this in our controls as follows. Note that we add table view when the data changes by responding to the new value in didSet ! class TableStateViewController : UIViewController {
var tableState = TableStateString.Loading {
didSet {
self .tableView.reloadData ()
}
}

find loadItems () {
tableState = . Failed
// or
tableState = .Items ([ ] "One" "Two" "Three" "Four" "Five" ]) [19659043]}

FUNC Tableview (Tableview: UITableView, numberOfRowsInSection section: Int) -> Int {[19659030] return tableState.count
}

FUNC Tableview (Tableview: ] UITableView, cellForRowAtIndexPath inde xPath: NSIndexPath) -> UITableViewCell {
la cell = Tableview. dequeueReusableCellWithIdentifier (196591004) cell forIndexPath: indexPath

la value = tableState.value (indexPath.row)
cell. text = value

return cell
}
}

In this simple example, we only show the value in a single table display cell if the result is Failed ] or Loading but there are several visually appealing options like DZNEmptyDataSet that you can display when you receive one of these states.

Of course, we only use a single String to write table summary data here, we insert a default table cell and put the text label from the list of strings. This is okay to write for String type alone if you want to generate a StringTableState for a specific section of your app that only needs strings. But many table views get their data from structures or classes, and we usually have many different tables with many different data types in a single app.

Fortunately, this is Swift and there's a lot we can do with generics to give a TableState that works for all types of types. Here is a more general implementation of TableState which works for all types, provided your type complies with the simple TableValuable protocol protocol TableValuable {
associated type TableItem
static FUNC loadingValue () -> TableItem
static FUNC failedValue () ] -> TableItem
FUNC value () -> TableItem
}

enum TableState <T: Table Case Case
Case Case Could
Case Items [T]]

Count: Int Int ] [
]
9004] items.count [19659030] standard :
return [19659041] 1
}
}

FUNC value (row: Int) ] -> T.TableItem [
[19659000] {
case .Loading:
return T.loadingValue () [19659000] case .Failed:
return T.failedValue ()
case la .Items (elements):
la topic = ]

Return item.value () [19659042]]
] ]

// and implement TableValuable on String

extension String : TableValuable {
typealias TableItem [19659004] = String

static FUNC failedValue () -> TableItem {
return "failed …" [19659041]}

static FUNC loadingValue () -> TableItem {
return "Loading …" [19659043]}

func value () -> TableItem {
return self
}
}

It's an interesting workout in using associated types in protocols and enums if you have not got your feet wet with them yet. A line with the note is the call to get the associated type from a one-case case: case light .Items (items): which is incredibly addictive when you start using associated types. I have never seen this type of item attachment on enums in another language, and once again the idea comes into your mind, you realize the countless usage of it.

Perhaps the best part about this code is that it's just over 30 lines of understandable swift. If you have a case where there are more states than Loading Failed and Loaded in a specific section of your app, it's okay to change a few places to be more appropriate for your use. It's definitely more of a micro frame than an actual frame, I'm even tough to provide a CocoaPods / Carthage-compatible project for it instead of just the kernel.

My favorite part of this mechanism is how it encourages you to break what is usually a large table view data source in smaller components. Instead of configuring a table cell in cellForRowAtIndexPath you now have an extension to your list object ( String or otherwise) that is ripe for inserting the cell configuration or at least returning the data which is relevant to that cell.

You may already have a protocol that all table view data types match – TableCellConfigurable Maybe – and it's trivial to claim that your data source is both TableValuable ] and TableCellConfigurable TableCellConfigurable ]. Again, prototype programming in Swift really shines.


We have only one associated type of .Items case in the enummen, but if you have a ErrorType for anything that goes wrong in your application, you can also specify an associated type on .Failed enum case to the wrong type and further track the cause of errors, perhaps to customize the error message in your app. Customize the value (row: Int) -> T.TableItem the method to return either a specially crafted row data type or you can implement a method error () -> ErrorType? if you want to return your red errors as a separate type from your data type.


Questions or comments? Find us at twitter or send a problem on github




Source link