神刀安全网

Remote Data State as an Enum

09 Jun 2016

Last night I came across How Elm Slays a UI Antipattern article written by Kris Jenkins . In it the author notices that a common list-based UI can be in one of four separate states: NotAsked , Loading , Failure and Success and proceeds to model those states explicitly as a sum type.

According to Kris, this approach works well in Elm giving compile-type safety to this common source of UI confusion. Let’s see if this approach will fit the stateful world of UIKit by using it for a view based on UITableView .

Before starting I’d also strongly consider using the representation introduced by Scott Hurff in How to fix a bad user interface :

  • Empty State
  • Error State
  • Partial State
  • Loading State
  • Ideal State

For now, though, let’s stick with the one presented by Kris. In Swift we’ll have the following enumeration:

enum RemoteData<Data, Error: ErrorType> {     case NotAsked     case Loading     case Failure(Error)     case Success(Data) } 

On the UI side we can have two subviews on the same level in the view hierarchy:

  • UITableView
  • PlaceholderView

PlaceholderView is a simple view with only title property for a demonstration purposes – but we can easily add more information, like an image in the future:

class PlaceholderView: UIView {      struct Configuration {         let title: String     }      @IBOutlet weak var titleLabel: UILabel?     var configuration: Configuration?      ... } 

As you can see it has Configuration struct encapsulating its configuration. Since working with a raw enum isn’t pleasant we can create a helper struct called UserInterfaceState :

struct UserInterfaceState<Data, Error: ErrorType> {     let remoteData: RemoteData<Data, Error>      var viewWithDataShouldBeHidden: Bool {         return placeholderConfiguration() != nil     }      var placeholderViewShouldBeHidden: Bool {         return !viewWithDataShouldBeHidden     }      var data: Data? {         if case .Success(let data) = self.remoteData {             return data         } else {             return nil         }     }      init(remoteData: RemoteData<Data, Error>) {         self.remoteData = remoteData     }      func placeholderConfiguration() -> PlaceholderView.Configuration? {         switch self.remoteData {         case .NotAsked:             return PlaceholderView.Configuration(title: NSLocalizedString("View is waiting for an update", comment: "NotAsked state"))         case .Loading:             return PlaceholderView.Configuration(title: NSLocalizedString("Loading", comment: "Loading state"))         case .Failure(let error):             return PlaceholderView.Configuration(title: NSLocalizedString("Error happened: /(error)", comment: "Failure state"))         case .Success(_):             return nil         }     } } 

Each time we get a new instance of RemoteData we create a new instance of UserInterfaceState and update our UI accordingly, e.g.:

tableView?.hidden = state.viewWithDataShouldBeHidden dataSource.data = state.data  placeholderView?.configuration = state.placeholderConfiguration() placeholderView?.hidden = state.placeholderViewShouldBeHidden 

Even though we omitted many details here, I think it’s clearly visible that with this technique we can have achieve a better UX with not that much additional work on our part.

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Remote Data State as an Enum

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址