1

I'm using Siesta with an API that returns a very lightweight list response for root entities. For instance, for /entity this is what the response looks like:

{
  count: 200,
  results: [
  {
    url: "https://example.com/api/entity/1/",
    name: "foo"
  },
  {
    url: "https://example.com/api/entity/2/",
    name: "bar"
  },
  {
    url: "https://example.com/api/entity/3/",
    name: "bat"
  }]
}

The full object found at the url in the results has an avatar property that I'd really like to show in my table view for this list but I can't figure out how to make that happen with the Siesta framework. Is it possible to fetch more details from the underlying /entity/1 endpoint as part of loading the resource for the /entity list?

Brent Schooley
  • 782
  • 1
  • 6
  • 20

1 Answers1

0

In Siesta’s view of the world, one url ⟺ one resource. There is thus a “summary list” resource, /entity, plus a separate “entity detail” resource for each row, /entity/1 etc. It doesn’t matter that they happen to share some of the same data; Siesta itself doesn’t make any effort to merge, synchronize, prepopulate one resource from the other. Separate URLs, separate resources.

The rule of thumb is, “If you need data from a resource, observe that resource.” Since you want to use both summary info from /entities and detail info from /entities/n, you observe both resources.

Here is a sketch of an approach you might use:

  • Get your table view showing just the info from /entities, no avatars. You can use RepositoryListViewController from the example project as a starting point.
  • Make each table cell accept a summary model, and observe its corresponding detail resource:

    class EntityTableViewCell: UITableViewCell, ResourceObserver {
      @IBOutlet weak var nameLabel: UILabel!
      @IBOutlet weak var avatar: RemoteImageView!
    
      private var summary: EntitySummary?
      private var detailResource: Resource?
    
      func showEntity(summary: EntitySummary) {
        self.summary = summary
        detailResource?.removeObservers(ownedBy: self)
        detailResource = MyApi.resource(absoluteURL: summary?.url)
        detailResource.addObserver(self).loadIfNeeded()
      }
    
  • Now populate the cell in resourceChanged(), mixing and matching from the summary and detail as you see fit:

      func resourceChanged(resource: Resource, event: ResourceEvent) {
        let detail: EntityDetail? = detailResource?.typedContent()
        nameLabel.text = detail?.name ?? summary?.name
        avatar.imageURL = detail?.avatar
      }
    
  • You might also want to stop observing when the cell moves out of view:

      override func prepareForReuse() {
        showEntity(nil)
      }
    }
    

(This sketch assumes that you have separate EntitySummary and EntityDetail models. You might also have a single Entity model with the detail-only fields optional, or you might just be using raw JSON dictionaries. The approach is the same regardless.)

Here’s what happens when a cell scrolls into view:

  1. Your cellForRowAtIndexPath calls showEntity(_:), passing an EntitySummary it got from the /entities resource.
  2. The cell starts observing /entities/n.
  3. This immediate triggers resourceChanged(). The detail resource has no data yet, so your cell immediately gets populated with summary info only.
  4. Eventually the detail resource loads. If your cell is still observing it, then resourceChanged() gets called again, and this time it sees the detail info.

Note that in #4, if your cell got scrolled out of view and reused before that detail resource loaded, then your cell will no longer be observing it — and thus the late-arriving response will not clobber the reused cell’s contents.

Paul Cantrell
  • 9,175
  • 2
  • 40
  • 48
  • Thanks for this. I can't seem to get the `configureTransformer` to work for the `absoluteURL` resource. I have `self.configureTransformer("/entity/*")` with a cast to the model but that does not seem to work. – Brent Schooley Aug 04 '16 at 16:13
  • It looks from your original question like the entity resources have trailing slashes, which `*` will not match, so you may need use the pattern `"/entity/*/"` instead of `"/entity/*"`. – Paul Cantrell Aug 05 '16 at 03:17