From a3cb12e9546ecc012e4e83bcba7728f05443ae23 Mon Sep 17 00:00:00 2001 From: JH713 Date: Tue, 30 Jan 2024 16:22:08 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20diffable=20datasource,=20combine=20?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iBox/Sources/Model/Bookmark.swift | 13 ++ iBox/Sources/Model/Folder.swift | 6 +- .../Presenter/BoxList/BoxListCell.swift | 56 +++++++ .../Presenter/BoxList/BoxListView.swift | 149 +++++++++++------- iBox/Sources/Utils/UserDefaultsManager.swift | 2 +- .../ViewModel/BoxListCellViewModel.swift | 27 ++++ .../ViewModel/BoxListSectionViewModel.swift | 38 +++++ iBox/Sources/ViewModel/BoxListViewModel.swift | 59 +++++++ 8 files changed, 283 insertions(+), 67 deletions(-) create mode 100644 iBox/Sources/Model/Bookmark.swift create mode 100644 iBox/Sources/Presenter/BoxList/BoxListCell.swift create mode 100644 iBox/Sources/ViewModel/BoxListCellViewModel.swift create mode 100644 iBox/Sources/ViewModel/BoxListSectionViewModel.swift create mode 100644 iBox/Sources/ViewModel/BoxListViewModel.swift diff --git a/iBox/Sources/Model/Bookmark.swift b/iBox/Sources/Model/Bookmark.swift new file mode 100644 index 0000000..2091151 --- /dev/null +++ b/iBox/Sources/Model/Bookmark.swift @@ -0,0 +1,13 @@ +// +// Bookmark.swift +// iBox +// +// Created by 이지현 on 1/30/24. +// + +import Foundation + +struct Bookmark: Codable { + let name: String + let url: String +} diff --git a/iBox/Sources/Model/Folder.swift b/iBox/Sources/Model/Folder.swift index cbb30cf..12f21e6 100644 --- a/iBox/Sources/Model/Folder.swift +++ b/iBox/Sources/Model/Folder.swift @@ -10,11 +10,7 @@ import Foundation struct Folder { let name: String let color: ColorName - let webs: [Web] + let bookmarks: [Bookmark] var isOpened: Bool = true } -struct Web: Codable { - let name: String - let url: String -} diff --git a/iBox/Sources/Presenter/BoxList/BoxListCell.swift b/iBox/Sources/Presenter/BoxList/BoxListCell.swift new file mode 100644 index 0000000..1e27de9 --- /dev/null +++ b/iBox/Sources/Presenter/BoxList/BoxListCell.swift @@ -0,0 +1,56 @@ +// +// BoxListCell.swift +// iBox +// +// Created by 이지현 on 1/30/24. +// + +import UIKit + +import SnapKit + +class BoxListCell: UITableViewCell { + var viewModel: BoxListCellViewModel? + static let reuseIdentifier = "boxListCell" + + private lazy var cellImageView = { + let view = UIImageView() + view.image = UIImage(systemName: "ellipsis.rectangle.fill") + return view + }() + + private lazy var label = { + let label = UILabel() + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupLayout() { + contentView.addSubview(cellImageView) + cellImageView.snp.makeConstraints { make in + make.top.leading.bottom.equalToSuperview() + make.width.equalTo(50) + } + + contentView.addSubview(label) + label.snp.makeConstraints { make in + make.top.trailing.bottom.equalToSuperview() + make.leading.equalTo(cellImageView.snp.trailing) + } + } + + func configure(_ viewModel: BoxListCellViewModel) { + self.viewModel = viewModel + label.text = viewModel.name + } + +} diff --git a/iBox/Sources/Presenter/BoxList/BoxListView.swift b/iBox/Sources/Presenter/BoxList/BoxListView.swift index 2c6d5b9..59f4ca4 100644 --- a/iBox/Sources/Presenter/BoxList/BoxListView.swift +++ b/iBox/Sources/Presenter/BoxList/BoxListView.swift @@ -5,6 +5,7 @@ // Created by 이지현 on 1/3/24. // +import Combine import UIKit import SnapKit @@ -14,18 +15,28 @@ protocol BoxListViewDelegate: AnyObject { } class BoxListView: BaseView { + var viewModel: BoxListViewModel? + private var boxListDataSource: UITableViewDiffableDataSource! weak var delegate: BoxListViewDelegate? - var folderArr = [ - Folder(name: "기본 폴더", color: .gray, webs: [ - Web(name: "42 Intra", url: "https://profile.intra.42.fr/"), - Web(name: "42Where", url: "https://www.where42.kr/"), - Web(name: "42Stat", url: "https://stat.42seoul.kr/"), - Web(name: "집현전", url: "https://42library.kr/") - ]), - Folder(name: "새 폴더", color: .green, webs: [Web(name: "Cabi", url: "https://cabi.42seoul.io/")], isOpened: false), - Folder(name: "새 폴더(2)", color: .yellow, webs: [Web(name: "24HANE", url: "https://24hoursarenotenough.42seoul.kr/")], isOpened: false) - ] + private var cancellables = Set() + + override init(frame: CGRect) { + super.init(frame: frame) + + backgroundColor = .systemBackground + viewModel = BoxListViewModel() + + setupLayout() + configureDataSource() + initDataSource() + bindViewModel() + viewModel?.input.send(.viewDidLoad) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } private lazy var backgroundView = { let view = UIView() @@ -43,9 +54,8 @@ class BoxListView: BaseView { private lazy var tableView = { let tableView = UITableView() - tableView.dataSource = self tableView.delegate = self - tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + tableView.register(BoxListCell.self, forCellReuseIdentifier: BoxListCell.reuseIdentifier) tableView.sectionHeaderTopPadding = 0 // tableView.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) @@ -55,17 +65,6 @@ class BoxListView: BaseView { tableView.separatorColor = .clear return tableView }() - - override init(frame: CGRect) { - super.init(frame: frame) - backgroundColor = .systemBackground - - setupLayout() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } private func setupLayout() { addSubview(backgroundView) @@ -74,33 +73,72 @@ class BoxListView: BaseView { make.leading.trailing.bottom.equalTo(safeAreaLayoutGuide).inset(20) } } -} - -extension BoxListView: UITableViewDataSource { - func numberOfSections(in tableView: UITableView) -> Int { - return folderArr.count + private func configureDataSource() { + boxListDataSource = UITableViewDiffableDataSource(tableView: tableView) { [weak self] tableView, indexPath, itemIdentifier in + guard let self, let viewModel = self.viewModel else { fatalError() } + guard let cell = tableView.dequeueReusableCell(withIdentifier: BoxListCell.reuseIdentifier, for: indexPath) as? BoxListCell else { fatalError() } + cell.configure(viewModel.viewModel(at: indexPath)) + return cell + } } - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if !folderArr[section].isOpened { - return 0 + private func initDataSource() { + guard let viewModel else { return } + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections(viewModel.boxList.map{ $0.id }) + for folder in viewModel.boxList { + snapshot.appendItems(folder.boxListCellViewModels.map { $0.id }, toSection: folder.id) } - return folderArr[section].webs.count + boxListDataSource.apply(snapshot, animatingDifferences: true) } - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) - cell.selectionStyle = .none - cell.backgroundColor = .clear - cell.textLabel?.text = folderArr[indexPath.section].webs[indexPath.row].name - cell.imageView?.image = UIImage(systemName: "ellipsis.rectangle.fill") - cell.imageView?.tintColor = ColorPalette.webIconColor - return cell + private func bindViewModel() { + guard let viewModel else { return } + let output = viewModel.transform(input: viewModel.input.eraseToAnyPublisher()) + + output.receive(on: DispatchQueue.main) + .sink { [weak self] event in + switch event { + case .toggleFolder: + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections(viewModel.boxList.map{ $0.id }) + for folder in viewModel.boxList { + if folder.isOpen { + snapshot.appendItems(folder.boxListCellViewModels.map { $0.id }, toSection: folder.id) + } + } + self?.boxListDataSource.apply(snapshot, animatingDifferences: true) + } + }.store(in: &cancellables) } - } +//extension BoxListView: UITableViewDataSource { +// +// func numberOfSections(in tableView: UITableView) -> Int { +// return viewModel.folderArr.count +// } +// +// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { +// if !viewModel.folderArr[section].isOpened { +// return 0 +// } +// return viewModel.folderArr[section].bookmarks.count +// } +// +// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { +// let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) +// cell.selectionStyle = .none +// cell.backgroundColor = .clear +// cell.textLabel?.text = viewModel.folderArr[indexPath.section].bookmarks[indexPath.row].name +// cell.imageView?.image = UIImage(systemName: "ellipsis.rectangle.fill") +// cell.imageView?.tintColor = ColorPalette.webIconColor +// return cell +// } +// +//} + extension BoxListView: UITableViewDelegate { public func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { @@ -125,9 +163,10 @@ extension BoxListView: UITableViewDelegate { } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - let button = FolderButton(isOpen: folderArr[section].isOpened) - button.setFolderName(folderArr[section].name) - button.setFolderColor(folderArr[section].color.toUIColor()) + guard let viewModel else { return nil } + let button = FolderButton(isOpen: viewModel.boxList[section].isOpen) + button.setFolderName(viewModel.boxList[section].name) + button.setFolderColor(viewModel.boxList[section].color.toUIColor()) button.tag = section button.addTarget(self, action: #selector(handleOpenClose), for: .touchUpInside) @@ -136,27 +175,15 @@ extension BoxListView: UITableViewDelegate { } @objc private func handleOpenClose(button: FolderButton) { - let section = button.tag - - var indexPaths = [IndexPath]() - for row in folderArr[section].webs.indices { - let indexPath = IndexPath(row: row, section: section) - indexPaths.append(indexPath) - } - - folderArr[section].isOpened.toggle() + guard let viewModel else { return } + viewModel.input.send(.folderTapped(section: button.tag)) button.toggleStatus() - - if folderArr[section].isOpened { - tableView.insertRows(at: indexPaths, with: .fade) - } else { - tableView.deleteRows(at: indexPaths, with: .fade) - } } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let webUrl = folderArr[indexPath.section].webs[indexPath.row].url - let webName = folderArr[indexPath.section].webs[indexPath.row].name + guard let viewModel else { return } + let webUrl = viewModel.boxList[indexPath.section].boxListCellViewModels[indexPath.row].url + let webName = viewModel.boxList[indexPath.section].boxListCellViewModels[indexPath.row].name delegate?.didSelectWeb(at: webUrl, withName: webName) } } diff --git a/iBox/Sources/Utils/UserDefaultsManager.swift b/iBox/Sources/Utils/UserDefaultsManager.swift index a60c061..479734c 100644 --- a/iBox/Sources/Utils/UserDefaultsManager.swift +++ b/iBox/Sources/Utils/UserDefaultsManager.swift @@ -19,7 +19,7 @@ final class UserDefaultsManager { ) static let favorite = UserDefaultValue( key: .favorite, - defaultValue: Web(name: "42 Intra", url: "https://profile.intra.42.fr/") + defaultValue: Bookmark(name: "42 Intra", url: "https://profile.intra.42.fr/") ) } diff --git a/iBox/Sources/ViewModel/BoxListCellViewModel.swift b/iBox/Sources/ViewModel/BoxListCellViewModel.swift new file mode 100644 index 0000000..68d89aa --- /dev/null +++ b/iBox/Sources/ViewModel/BoxListCellViewModel.swift @@ -0,0 +1,27 @@ +// +// BoxListCellViewModel.swift +// iBox +// +// Created by 이지현 on 1/30/24. +// + +import Foundation + +class BoxListCellViewModel: Identifiable { + private let bookmark: Bookmark + + init(bookmark: Bookmark) { + self.bookmark = bookmark + } + + let id = UUID() + + var name: String { + bookmark.name + } + + var url: String { + bookmark.url + } + +} diff --git a/iBox/Sources/ViewModel/BoxListSectionViewModel.swift b/iBox/Sources/ViewModel/BoxListSectionViewModel.swift new file mode 100644 index 0000000..c440e57 --- /dev/null +++ b/iBox/Sources/ViewModel/BoxListSectionViewModel.swift @@ -0,0 +1,38 @@ +// +// BoxListSectionViewModel.swift +// iBox +// +// Created by 이지현 on 1/30/24. +// + +import Foundation + +class BoxListSectionViewModel: Identifiable { + private var folder: Folder + var boxListCellViewModels: [BoxListCellViewModel]! + + init(folder: Folder) { + self.folder = folder + boxListCellViewModels = folder.bookmarks.map { BoxListCellViewModel(bookmark: $0) } + } + + let id = UUID() + + var name: String { + folder.name + } + + var color: ColorName { + folder.color + } + + var isOpen: Bool { + get { + folder.isOpened + } + + set { + folder.isOpened = newValue + } + } +} diff --git a/iBox/Sources/ViewModel/BoxListViewModel.swift b/iBox/Sources/ViewModel/BoxListViewModel.swift new file mode 100644 index 0000000..a94e0b1 --- /dev/null +++ b/iBox/Sources/ViewModel/BoxListViewModel.swift @@ -0,0 +1,59 @@ +// +// BoxListViewModel.swift +// iBox +// +// Created by 이지현 on 1/30/24. +// + +import Combine +import Foundation + +class BoxListViewModel { + + var boxList = [ + BoxListSectionViewModel(folder: Folder(name: "기본 폴더", color: .gray, bookmarks: [ + Bookmark(name: "42 Intra", url: "https://profile.intra.42.fr/"), + Bookmark(name: "42Where", url: "https://www.where42.kr/"), + Bookmark(name: "42Stat", url: "https://stat.42seoul.kr/"), + Bookmark(name: "집현전", url: "https://42library.kr/") + ])), + BoxListSectionViewModel(folder: Folder(name: "새 폴더", color: .green, bookmarks: [Bookmark(name: "Cabi", url: "https://cabi.42seoul.io/")], isOpened: false)), + BoxListSectionViewModel(folder: Folder(name: "새 폴더(2)", color: .yellow, bookmarks: [Bookmark(name: "24HANE", url: "https://24hoursarenotenough.42seoul.kr/")], isOpened: false)) + ] + + enum Input { + case viewDidLoad + case folderTapped(section: Int) + } + + enum Output { + case toggleFolder + } + + let input = PassthroughSubject() + private let output = PassthroughSubject() + private var cancellables = Set() + + func transform(input: AnyPublisher) -> AnyPublisher { + input.sink { [weak self] event in + switch event { + case .viewDidLoad: + print("viewDidLoad") + case let .folderTapped(section): + self?.toggleFolder(section: section) + } + }.store(in: &cancellables) + return output.eraseToAnyPublisher() + } + + func viewModel(at indexPath: IndexPath) -> BoxListCellViewModel { + return boxList[indexPath.section].boxListCellViewModels[indexPath.row] + } + + func toggleFolder(section: Int) { + + boxList[section].isOpen.toggle() + output.send(.toggleFolder) + } + +} From 4252c197cbf10f7aa7fe21ab068a4f2cf10eb3d6 Mon Sep 17 00:00:00 2001 From: JH713 Date: Tue, 6 Feb 2024 01:13:36 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor:=20=EA=B5=AC=EC=A1=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presenter/BoxList/BoxListCell.swift | 10 ++-- .../Presenter/BoxList/BoxListView.swift | 48 +++---------------- .../ViewModel/BoxListSectionViewModel.swift | 10 ++-- iBox/Sources/ViewModel/BoxListViewModel.swift | 14 ++---- 4 files changed, 26 insertions(+), 56 deletions(-) diff --git a/iBox/Sources/Presenter/BoxList/BoxListCell.swift b/iBox/Sources/Presenter/BoxList/BoxListCell.swift index 1e27de9..d457aac 100644 --- a/iBox/Sources/Presenter/BoxList/BoxListCell.swift +++ b/iBox/Sources/Presenter/BoxList/BoxListCell.swift @@ -16,6 +16,8 @@ class BoxListCell: UITableViewCell { private lazy var cellImageView = { let view = UIImageView() view.image = UIImage(systemName: "ellipsis.rectangle.fill") + view.tintColor = .label + view.contentMode = .scaleAspectFit return view }() @@ -26,6 +28,7 @@ class BoxListCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) + backgroundColor = .systemGroupedBackground setupLayout() } @@ -37,14 +40,15 @@ class BoxListCell: UITableViewCell { private func setupLayout() { contentView.addSubview(cellImageView) cellImageView.snp.makeConstraints { make in - make.top.leading.bottom.equalToSuperview() - make.width.equalTo(50) + make.leading.equalToSuperview().inset(20) + make.top.bottom.equalToSuperview().inset(10) + make.width.equalTo(30) } contentView.addSubview(label) label.snp.makeConstraints { make in make.top.trailing.bottom.equalToSuperview() - make.leading.equalTo(cellImageView.snp.trailing) + make.leading.equalTo(cellImageView.snp.trailing).offset(10) } } diff --git a/iBox/Sources/Presenter/BoxList/BoxListView.swift b/iBox/Sources/Presenter/BoxList/BoxListView.swift index 59f4ca4..9a7bddb 100644 --- a/iBox/Sources/Presenter/BoxList/BoxListView.swift +++ b/iBox/Sources/Presenter/BoxList/BoxListView.swift @@ -29,7 +29,6 @@ class BoxListView: BaseView { setupLayout() configureDataSource() - initDataSource() bindViewModel() viewModel?.input.send(.viewDidLoad) } @@ -58,11 +57,11 @@ class BoxListView: BaseView { tableView.register(BoxListCell.self, forCellReuseIdentifier: BoxListCell.reuseIdentifier) tableView.sectionHeaderTopPadding = 0 -// tableView.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) tableView.clipsToBounds = true tableView.layer.cornerRadius = 20 tableView.backgroundColor = .clear tableView.separatorColor = .clear + tableView.rowHeight = 50 return tableView }() @@ -83,11 +82,10 @@ class BoxListView: BaseView { } } - private func initDataSource() { - guard let viewModel else { return } + private func applySnapshot(with: [BoxListSectionViewModel]) { var snapshot = NSDiffableDataSourceSnapshot() - snapshot.appendSections(viewModel.boxList.map{ $0.id }) - for folder in viewModel.boxList { + snapshot.appendSections(with.map{ $0.id }) + for folder in with { snapshot.appendItems(folder.boxListCellViewModels.map { $0.id }, toSection: folder.id) } boxListDataSource.apply(snapshot, animatingDifferences: true) @@ -100,45 +98,13 @@ class BoxListView: BaseView { output.receive(on: DispatchQueue.main) .sink { [weak self] event in switch event { - case .toggleFolder: - var snapshot = NSDiffableDataSourceSnapshot() - snapshot.appendSections(viewModel.boxList.map{ $0.id }) - for folder in viewModel.boxList { - if folder.isOpen { - snapshot.appendItems(folder.boxListCellViewModels.map { $0.id }, toSection: folder.id) - } - } - self?.boxListDataSource.apply(snapshot, animatingDifferences: true) + case .sendBoxList(boxList: let boxList): + self?.applySnapshot(with: boxList) } }.store(in: &cancellables) } } -//extension BoxListView: UITableViewDataSource { -// -// func numberOfSections(in tableView: UITableView) -> Int { -// return viewModel.folderArr.count -// } -// -// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { -// if !viewModel.folderArr[section].isOpened { -// return 0 -// } -// return viewModel.folderArr[section].bookmarks.count -// } -// -// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { -// let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) -// cell.selectionStyle = .none -// cell.backgroundColor = .clear -// cell.textLabel?.text = viewModel.folderArr[indexPath.section].bookmarks[indexPath.row].name -// cell.imageView?.image = UIImage(systemName: "ellipsis.rectangle.fill") -// cell.imageView?.tintColor = ColorPalette.webIconColor -// return cell -// } -// -//} - extension BoxListView: UITableViewDelegate { public func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { @@ -164,7 +130,7 @@ extension BoxListView: UITableViewDelegate { func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { guard let viewModel else { return nil } - let button = FolderButton(isOpen: viewModel.boxList[section].isOpen) + let button = FolderButton(isOpen: viewModel.boxList[section].isOpened) button.setFolderName(viewModel.boxList[section].name) button.setFolderColor(viewModel.boxList[section].color.toUIColor()) button.tag = section diff --git a/iBox/Sources/ViewModel/BoxListSectionViewModel.swift b/iBox/Sources/ViewModel/BoxListSectionViewModel.swift index c440e57..70a928c 100644 --- a/iBox/Sources/ViewModel/BoxListSectionViewModel.swift +++ b/iBox/Sources/ViewModel/BoxListSectionViewModel.swift @@ -9,11 +9,15 @@ import Foundation class BoxListSectionViewModel: Identifiable { private var folder: Folder - var boxListCellViewModels: [BoxListCellViewModel]! + private var originalBoxListCellViewModels: [BoxListCellViewModel]! init(folder: Folder) { self.folder = folder - boxListCellViewModels = folder.bookmarks.map { BoxListCellViewModel(bookmark: $0) } + originalBoxListCellViewModels = folder.bookmarks.map { BoxListCellViewModel(bookmark: $0) } + } + + var boxListCellViewModels: [BoxListCellViewModel] { + return isOpened ? originalBoxListCellViewModels : [] } let id = UUID() @@ -26,7 +30,7 @@ class BoxListSectionViewModel: Identifiable { folder.color } - var isOpen: Bool { + var isOpened: Bool { get { folder.isOpened } diff --git a/iBox/Sources/ViewModel/BoxListViewModel.swift b/iBox/Sources/ViewModel/BoxListViewModel.swift index a94e0b1..cb73926 100644 --- a/iBox/Sources/ViewModel/BoxListViewModel.swift +++ b/iBox/Sources/ViewModel/BoxListViewModel.swift @@ -27,7 +27,7 @@ class BoxListViewModel { } enum Output { - case toggleFolder + case sendBoxList(boxList: [BoxListSectionViewModel]) } let input = PassthroughSubject() @@ -36,11 +36,13 @@ class BoxListViewModel { func transform(input: AnyPublisher) -> AnyPublisher { input.sink { [weak self] event in + guard let self else { return } switch event { case .viewDidLoad: - print("viewDidLoad") + output.send(.sendBoxList(boxList: boxList)) case let .folderTapped(section): - self?.toggleFolder(section: section) + boxList[section].isOpened.toggle() + output.send(.sendBoxList(boxList: boxList)) } }.store(in: &cancellables) return output.eraseToAnyPublisher() @@ -49,11 +51,5 @@ class BoxListViewModel { func viewModel(at indexPath: IndexPath) -> BoxListCellViewModel { return boxList[indexPath.section].boxListCellViewModels[indexPath.row] } - - func toggleFolder(section: Int) { - - boxList[section].isOpen.toggle() - output.send(.toggleFolder) - } }