PGDev
PGDev

Reputation: 24341

How to handle UICollectionView in UITableViewCell with MVVM

I’m working on an app that has the following View Hierarachy:

ViewController -> contains UITableView -> contains CustomTableViewCell -> contains UICollectionView -> contains CustomCollectionViewCell

Now I've created a ViewModel corresponding to the ViewController. The ViewModel contains the model objects for CustomTableViewCells, i.e. the number of CustomTableViewCells to show and what content to show in each CustomTableViewCell.

class ViewModel
{
    //MARK: Private Properties
    private var libraries = [Library](){
        didSet{
            self.reloadTableViewClosure?()
        }
    }

    //MARK: Internal Properties
    var reloadTableViewClosure: (()->())?
    var numberOfLibraries: Int{
        return self.libraries.count
    }

    //MARK: Internal Methods
    func getLibrary(at indexPath: IndexPath) -> Library
    {
        return self.libraries[indexPath.row]
    }

    //MARK: Initializer
    init()
    {
        self.fetchLibraryList()
    }

    //MARK: Private Methods
    private func fetchLibraryList()
    {
        if let path = Bundle.main.path(forResource: "LibraryList", ofType: "json")
        {
            if let libraryList = try? JSONDecoder().decode([Library].self, from: Data(contentsOf: URL(fileURLWithPath: path)))
            {
                self.libraries = libraryList
            }
        }
    }
}

I want to know how should I handle UICollectionView with MVVM?

  1. Should I make the main ViewController the delegate & dataSource for both UITableView & UICollectionViews?

  2. Where should I keep the model objects for CustomCollectionViewCells? In the same ViewModel or should I make a different one?

Upvotes: 1

Views: 1504

Answers (1)

Kamran
Kamran

Reputation: 15258

What i can figure out in this situation is as below,

You should create 3 ViewModels

  1. ViewModel for ViewController
  2. CustomTableViewCellViewModel for CustomTableViewCellView
  3. CustomCollectionViewCellViewModel for CustomCollectionViewCellView

And here is how your ViewModels should look like,

class ViewModel
{
    private var cellVMs = [CustomTableViewCellViewModel] = []

    var reloadTableViewClosure: (()->())?

    var numberOfLibraries: Int {
        return self.cellVMs.count
    }

    func getLibraryCellVM(at indexPath: IndexPath) -> CustomTableViewCellViewModel
    {
        return self.cellVMs[indexPath.row]
    }

    //MARK: Initializer
    init()
    {
        self.fetchLibraryList()
    }

    //MARK: Private Methods
    private func fetchLibraryList()
    {
        if let path = Bundle.main.path(forResource: "LibraryList", ofType: "json")
        {
            if let libraryList = try? JSONDecoder().decode([Library].self, from: Data(contentsOf: URL(fileURLWithPath: path)))
            {
                libraryList.forEach({
                    cellVMs.append(CustomTableViewCellViewModel(library: $0))
                })
                self.reloadTableViewClosure?()
            }
        }
    }
}

Your CustomTableViewCellViewModel will look like this,

class CustomTableViewCellViewModel {

        var booksVMs: [CustomCollectionViewCellViewModel] = []

        var library: Library!

        init(library: Library) {
            self.library = library

            // Initialize booksVMs
            library.books.forEach({
                booksVMs.append(CustomCollectionViewCellViewModel.init(book: $0))
            })
        }

        var numberOfBooks: Int {
            self.booksVMs.count
        }

        func bookViewModel(at indexPath: IndexPath) -> CustomCollectionViewCellViewModel {
            return self.booksVMs[indexPath.row]
        }
    }

and finally CustomCollectionViewCellViewModel will look like this,

class CustomCollectionViewCellViewModel {

   var book: Book!

   init(book: Book) {
     self.book = book
   }

   var bookName: String? {
      return self.book.name
   }
}

Upvotes: 3

Related Questions