Swift - InstantSearch - Create complex model as data source

Hello guys,

since I am really happy with the usage of InstantSearch, I wanted to optimize my code. I have following issue:

I can fetch data from the server and display them in a collection view. This works perfect when the content of my collection view cell, represents the object from the index. Now, I want to add more ui components to my cell which also show data from other objects (not being fetched from algolia).

It is technically not a problem to fetch the related data from the database BUT currently, I can only trigger async queries.

class HomeViewController: UIViewController, HitsController {

var singleIndexHitsInteractor: HitsInteractor<JSON> = .init(infiniteScrolling: .on(withOffset: 10), showItemsOnEmptyQuery: **true** )
var challengeCollectionView:UICollectionView?
public weak var hitsSource: HitsInteractor<JSON>?

func reload() {
       challengeCollectionView?.reloadData()
  }
..
}

extension HomeViewController:UICollectionViewDataSource {
  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let photoCell = collectionView.dequeueReusableCell(withReuseIdentifier: "mainViewCollectionCell", for: indexPath) as! MainViewCollectionViewCell
                  
  let challengeObject = Challenge(json: (hitsSource?.hit(atIndex: indexPath.row))!)

Well, since I have the challengeObject, I could make async calls in the cellForItemAt method and load the related object to display its information in the cell, but this approach doesn’t fit to the MVC pattern. Also, using this way is not good when it comes to the performance since I would have to query the database everytime when I scroll down or up in the collection view.

I somehow need a more complex data model that consists of both objects. Either I add additional fields into my index or what I wanted to know :

Is it possible that I have a data model where I fetch first the results as it is above, then use them to fetch the related objects from my database and after finishing with the async calls, refreshing the collection view with the updated data source? In this case, I could iterate through the model and update the cell synchronously.

I had a look into one of your demo projects with InstantSearch - MovieList
The images are also download asynchronously

  struct MovieHitCellViewState {
  
  func configure(_ cell: UIView & MovieCell) -> (Hit<Movie>) -> () {
    return { movieHit in
      let movie = movieHit.object
      cell.artworkImageView.sd_setImage(with: movie.image) { (_, _, _, _) in
        DispatchQueue.main.async {
          cell.setNeedsLayout()
        }
      }

But depending on the scrolling, we would load the image again and again, wouldn’t we?

Thanks!

Hi @kaan548,

In this example we use SDWebImage library. It caches the fetched images under the hood so each image loaded only once, so the network usage is optimal. As the image loading is asynchronous, it doesn’t affect the scrolling performance.

In your case you can use the same approach by doing an asynchronous call to your database on each cell appearance with in-memory cache in the middle. To optimize the number of requests, you can subscribe to onResults event of your Searcher instance, triggered on each results page reception. When you can launch a batch request to your database to fetch the necessary data matching the list of hits received within the results page.

The data flow may look as follows:

  1. Algolia search request triggered
  2. Results page P received
  3. Database request for the items on the page P triggered
  4. Results page from the database received and cached

Then depending on the structure of your data you can reload your CollectionView immediately after the step 2 with partial data in your cells fetched from Algolia and reload it one more time after the step 4 completing the missing data in your cells with DB data.

Different approach is to merge the Algolia results with your database results after the step 4 and fill with them another datasource with this result that will supply your CollectionView. In this case you don’t have to use the hitsSource.hit(atIndex:) method, but access your custom data source with merged results. By doing so, you have to reload your CollectionView just once, but the latency may increase that risks to affect the user experience.

I have expected a quality answer and that happened - as always :smiley: Thanks @vladislav.fitc