UICollection Compositional Layout Part 4 DataSource
As shown in Figure 1 data sources today, in UITableView and CollectionViews? So here we see an example implementation of the UICollectionView data source.
Now, if you’ve worked with UITableView or CollectionViews, you’ve seen this before. Here, we’re providing three of the required two methods in this protocol. And it’s pretty straightforward, right? We have asked about the number of sections and the number of items in each of those sections. And as content renders, we’re going to ask for cells. Pretty straightforward. Now apps get more and more complex every year. They do more things. Our users demand more features. And oftentimes, these data sources are backed by complex controllers inside of our app. And these controllers can do a variety of things. They can interact with Core Data, and they could talk to web service. They just do a number of different things. And I want to visualize this real quick. And let’s show a conversation between your UI layer and this controller layer that’s doing all this heavy lifting to get your data. All right? And the conversation starts out very civil. It’s like, “Hey, give me a number of items in a section, or give me a cell as we render content.” Very straightforward. And so far, smooth sailing. But things get more complicated over time, right? You get — like let’s say this controller has a web service request that it gets a response from, right? It’s like, oh, I’ve got data for your tweets or whatever, right? Well, now this controller layer, which is complex unto itself, lets the world know, “Hey, I changed. Something changed.” All right, so here’s where things get a bit more complex, right? So now, it’s up to the UI layer to decide, “Hey, things changed. Now, I need to — can change this into updates for our UI layer. And this involves all the mutations that have to occur against TableView and CollectionView. And how to construct those batch updates properly, and mutate your backing store, all those kinds of things. But sometimes, no matter how hard you try — You know, things go wrong as shown in Figure 2. It’s an imperfect world. It’s not an uncommon thing. And it’s really frustrating, right? You hit this, and you’re like, “All right. What did I do wrong?” It’s me. And as you dig through your code, okay, fine. You Google on Stack Overflow, see what’s going on. And eventually, you might get frustrated and just call
reloadData. And we talked about this last year, and that’s fine. That’s correct. Your app looks okay. But when you call reloadData, you get this non-animated effect. And it detracts from the user experience.
What’s the problem? Well, the problem here is where’s our truth? You know? I mean, who got his? And who has all the answers? And the big issue here is that our data controller — or it’s acting as a data source — has its own version of the truth, which changes over time. And the UI has a version of the truth. And the UILayerCode is responsible for mitigating that, making sure that it’s always in sync. As we saw, it’s sometimes hard. So our current approach is error prone. And primarily because there’s just no notion of a centralized truth. All right, so that’s the state-of-the-art. That’s where we are today. But where are we going? Well, I’m happy to announce that for iOS, TVoS, and MacOS this year, we’re bringing a brand new approach.And we’re calling this DiffableDataSource.
DiffableDataSource (New approach)
No performBatchUpdates. Let’s go on. And along with it, all the crashes, hassles, complexity, all of the stuff that you don’t want to deal with, has been jettisoned. Instead, we have a single method we call Apply. What is Apply? Apply is simple, automatic, hassle-free diffing. So we do this with a brand new construct we call a Snapshot. And it’s a very simple idea. It’s effectively the truth of the current UI state. And instead of IndexPaths, it’s an association or a collection of section identifiers that are all unique and item identifiers. And you update these not with IndexPaths, with identifiers.
. So we’ve got these FOO, BAR, and BIF onscreen, right. And that’s what we’re interacting with. These are identifiers in our app. And let’s say that our controller changed. And now we’ve got this brand new Snapshot that we want to apply.But this is our current Snapshot. How do we get from our new truth to the current Snapshot? Well, we can see here we’ve configured a brand new Snapshot with BAR, FOO, and BAZ. And we have some items that are coming along for the ride that have just changed order, and then a new item coming in.So conceptually an Apply knows about the current state and knows about the new state, which are going to apply to the UI element.
so how do we do this? Well, we have four classes across all the platforms. For iOS and TVoS, we have
UITableViewDiffableDataSource. And then on the Mac, we have
NSCollectionViewDiffableDataSource. And common for all the platforms is this Snapshot class, which is responsible for the current UIState
As shown in Figure 8 we created
Tweet Feed collection view Cell , so it prerequisite you know autolayout and how to create custom view through Interface Builder
In Figure 9 we created TweetFeed model. Note it is conform to
Hashable which is mandatory for diffable data source. To use TweetFeed as a diffable data source , you need to add
In Figure 10, all code related to build layout and data sources
In Figure 11.1 we added
tweet3 with same value as
tweet2 and application got crash
Fatal: supplied item identifiers are not unique. because current logic to create hash Value using data in each property , so
As you can see in Figure 5
- For 1 section example we only conform to Hashable which means all value in the object should be same to generate same hash value and same for
- For 2 section example we conform to Hashable and say has should be generate with
serialNumberwhich means if only
serialNumbervalue in object should be same to generate same hash value and for
==operator still whole object value will same
- For last section if serialNumber in objects is same it will produce same hash and both object are equal as well
TweetFeed in a diffable data source. The recommended approach to solve this is to add a
UUID string to the type to serve as a
hash value. As long as any unique value is used as hash , you are good to go. For every
TweetFeed instance. As shown in Figure 13 we make tweet id as a
hashvalue which will be unique to every tweet. If server sending some unique key you can also used that but we aware backend guy don't send same unique key which usually happen if you have a pagination API where you are doing infinite loading stuff.
As shown in Figure 14.1 and14.2 we added one button and we move sapshot logic to some other method
As shown in Figure 15, we add new item with animation and see no perform batch which is cool right.
- To show the animation initially I show only one tweet
- On insert, we first fetch old snapshot and append new tweets and tell apply new snapshot, with animation in a declarative way
- And diffable data source did it’s magic
As shown in Figure 13.1 and 16.2 we deleted item with animation. One thing we did we make data that populated the snapshot as a property
As shown in Figure 17.1 we update “Imran Khan” tweet likes to 2000 and we called reload item and given whole tweet object as an identifier. But we got crash and it is saying “Invalid item identifier specified for reload”
Reloads the data within the specified items in the snapshot.
mutating func reloadItems(_ identifiers: [ItemIdentifierType])
Parameters identifiers The array of identifiers corresponding to the items to reload in the snapshot.
This sums of all, object before update have the same
hashvalue and both are
equal but after update
hashValue is same but it is not equal anymore. In addition to this when a data source compares between two snapshots, it’s going to look at whether two instances of the same object have changed in other ways. For it to be you need to provide Equitable conformance. In our case two objects same if they have all value equals, so if we update
likeCount value both object will not be the same
As shown in Figure 19 we added Equatable conformance, Now two snapshots are equal if the id of both are same
Now you are thinking like if we tap on button “Imran Khan” likes will change to 2000 but if we see logs snapshot
likeCount value still
1000 and our own manage data has
When you tell a snapshot to
reloada certain item, it does not read in the data of the item you supply! It simply looks at the item, as a way of identifying what item, already in the data source, you are asking to reload.
(So, if the item you supply is Equatable to but not 100% identical to the item already in the data source, the “difference” between the item you supply and the item already in the data source will not matter at all; the data source will never be told that anything is different.)
When you then apply that snapshot to the data source, the data source tells the table view to reload the corresponding cell. This results in the data source’s cell provider function being called again.
OK, so the data source’s cell provider function is called, with the usual three parameters — the table view, the index path, and the data from the data source. But we’ve just said that the data from the data source has not changed. So what is the point of reloading at all?
The answer is, apparently, that the cell provider function is expected to look elsewhere to get (at least some of) the new data to be displayed in the newly dequeued cell. You are expected to have some sort of “backing store” that the cell provider looks at. For example, you might be maintaining a dictionary where the key is the cell identifier type and the value is the extra information that might be reloaded. as shown in Figure 21 and Gif 3
This must be legal, because by definition the cell identifier type is Hashable and can therefore serve as a dictionary key, and moreover the cell identifiers must be unique within the data, or the data source would reject the data (by crashing). And the lookup will be instant, because this is a dictionary.
As shown in Figure 22 what we did we update data that we are maintaining and then we create sections and items but still
likesCount not updated , snapshot updated but when diffable datasource is applying diff for them “Imran khan” tweet object is same since it is comparing with
id. Now you are confused what the heck it is
Now what i did i added likeCount in Equtable conformance now diff find snapshot data differences
As shown in Figure 24 , in this case we put likeCount in hasValue ad still working ,
So now we delete item but before delete we updated likeCount so both snapshot diff and it can’t find any , in this case it will not crash
I usually follow this approach
- write your hash function combining all the values that can change in the view ex.
- Create new snapshot and apply it will automatically find optimized diff and reader UI
One more hack to update data 😎here we modifier older snapshot
You can move the Item as well
- No perform batch updated ad only apply
- Note diffable datasource diff operation complexity is O(N)
As shown below we called apply on background queue as it is supported
If you choose this model to call Apply from the background queue, be consistent. Just always call it from the background queue. You never want to mix and match calling it from a background queue or the main queue. Just always do it the same way.
And we’re good citizens. We’ll complain about this if you get it wrong. as shown i Figure 31
What is NSDiffableDataSourceSnapshot `reloadItems` for?
I'm having difficulty finding the use of NSDiffableDataSourceSnapshot reloadItems(_:) : If the item I ask to reload is…