Observers
Code can observe changes to a resource’s state, either by implementing the ResourceObserver
protocol:
resource.addObserver(self)
…or by providing a callback closure:
resource.addObserver(owner: self) {
[weak self] resource, event in
…
}
(Note that you’ll usually need [weak self]
in the closure to prevent a memory leak.)
Observers receive a notification whenever a resource’s state changes: when it starts loading, receives new data, or receives an error. Additionally, each observer is also pinged immediately when it first starts observing, even if the resource has not changed. This lets you put all your update code in one place.
The simplest way to implement your observer is to ignore what kind of event triggered the notification, and take an idempotent “update everything” approach:
func resourceChanged(_ resource: Resource, event: ResourceEvent) {
// The convenience .jsonDict accessor returns empty dict if no
// data, so the same code can both populate and clear fields.
let json = resource.jsonDict
nameLabel.text = json["name"] as? String
favoriteColorLabel.text = json["favoriteColor"] as? String
errorLabel.text = resource.latestError?.userMessage
}
Note the pleasantly reactive flavor this code takes on — without the overhead of adopting full-on Reactive programming with a capital R.
(Aside: It would be the most natural thing in the world to wire a Siesta resource up to a reactive library. Pull requests welcome!)
Resource Events
If updating the whole UI is an expensive operation (but it rarely is; benchmark first!), you can use the event
parameter and the metadata in latestData
and latestError
to fine-tune your UI updates.
For example, if you have an expensive update you want to perform only when latestData
changes:
func resourceChanged(_ resource: Resource, event: ResourceEvent) {
if case .newData = event {
// Do expensive update
}
}
If your API supports the ETag
header, you could also use the Entity.etag
property:
func resourceChanged(_ resource: Resource, event: ResourceEvent) {
if displayedEtag != resource.latestData?.etag {
displayedEtag = resource.latestData?.etag
// Do expensive update
}
}
Use this technique judiciously. Lots of fine-grained logic like this is a bad code smell when using Siesta.
Here’s how the various ResourceEvent
values map to Resource
state changes:
observers |
latestData |
latestError |
isLoading |
timestamp |
|
---|---|---|---|---|---|
observerAdded |
one added | – | – | – | – |
requested |
– | – | – | true |
– |
requestCancelled |
– | – | – | false * |
– |
newData |
– | updated | nil |
false * |
updated |
notModified |
– | – | nil |
false * |
updated |
error |
– | – | updated | false * |
updated |
* If calls to load(...)
forced multiple simultaneous load requests, isLoading
may still be true even after an event that signals the completion of a request.
See the API docs for Resource
, ResourceEvent
, and Entity
for more information.
Next: Requests