3개 모두 적용. ㅇㅋ.

<aside> ✏️ 2022.10.20 ~ 2022.10.21

새로운 컬렉션 뷰 API를 작성할 때는 순서를 주의해야겠다. 그리고 MVVM을 나눌 때 애매하면 뷰모델에서는 import UIKit를 절대 !! 절대 !! 하지 말자 !!!

</aside>

작업

주차장 상세 화면

// MARK: - CollectionView

extension ParkingDetailViewController {
    private func createCompositionalLayout() -> UICollectionViewCompositionalLayout {
        return UICollectionViewCompositionalLayout { (sectionIndex, NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
            let itemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1),
                heightDimension: .fractionalHeight(1))
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            
            if sectionIndex == 0 {
                let groupSize = NSCollectionLayoutSize(
                    widthDimension: .fractionalWidth(1),
                    heightDimension: .absolute(109))
                let group = NSCollectionLayoutGroup.horizontal(
                    layoutSize: groupSize,
                    subitems: [item])
                
                let headerSize = NSCollectionLayoutSize(
                    widthDimension: .fractionalWidth(1),
                    heightDimension: .absolute(0))
                let header = NSCollectionLayoutBoundarySupplementaryItem(
                    layoutSize: headerSize,
                    elementKind: ParkingDetailViewController.sectionHeaderElementKind,
                    alignment: .bottom
                )
                
                let section = NSCollectionLayoutSection(group: group)
                section.boundarySupplementaryItems = [header]
                return section
            } else {
                let groupSize = NSCollectionLayoutSize(
                    widthDimension: .fractionalWidth(1),
                    heightDimension: .absolute(60))
                let group = NSCollectionLayoutGroup.horizontal(
                    layoutSize: groupSize,
                    subitems: [item])
                
                let headerSize = NSCollectionLayoutSize(
                    widthDimension: .fractionalWidth(1),
                    heightDimension: .absolute(60))
                let header = NSCollectionLayoutBoundarySupplementaryItem(
                    layoutSize: headerSize,
                    elementKind: ParkingDetailViewController.sectionHeaderElementKind,
                    alignment: .top
                )
                
                let section = NSCollectionLayoutSection(group: group)
                section.boundarySupplementaryItems = [header]
                return section
            }
        }
    }
    
    private func createLayout() -> UICollectionViewLayout {
        let configuration = UICollectionViewCompositionalLayoutConfiguration()
        let layout = createCompositionalLayout()
        layout.configuration = configuration
        return layout
    }
    
    private func configureDataSource() {
        let locationCellRegistration = UICollectionView.CellRegistration<ParkingDetailLocationCollectionViewCell, (String, String)> { cell, indexPath, itemIdentifier in
            cell.setData(itemIdentifier.0, itemIdentifier.1)
        }
        
        let cellRegistration = UICollectionView.CellRegistration<ParkingDetailCollectionViewCell, String> { cell, indexPath, itemIdentifier in
            if indexPath.section == 1 {
                let arr = ["기본", "추가"]
                cell.setData(arr[indexPath.item], itemIdentifier)
            } else if indexPath.section == 2 || indexPath.section == 3 {
                let arr = ["평일", "토요일", "공휴일"]
                cell.setData(arr[indexPath.item], itemIdentifier)
            } else {
                cell.setData("주차장", itemIdentifier)
            }
        }
        
        let headerRegistration = UICollectionView.SupplementaryRegistration
        <ParkingDetailHeaderView>(elementKind: ParkingDetailViewController.sectionHeaderElementKind) { (supplementaryView, string, indexPath) in
            switch indexPath.section {
            case 1: supplementaryView.title = "요금"
            case 2: supplementaryView.title = "무/유료"
            case 3: supplementaryView.title = "운영시간"
            case 4: supplementaryView.title = "전화번호"
            default: return
            }
        }
        
        dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, itemIdentifier in
            switch itemIdentifier {
            case .location(let name, let location):
                let cell = collectionView.dequeueConfiguredReusableCell(using: locationCellRegistration, for: indexPath, item: (name, location))
                return cell
            case .defatultFee(let value), .additionalFee(let value):
                let cell = collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: value)
                return cell
            case .isWeekdayFree(let value), .isSaturdayFree(let value), .isHolidayFree(let value):
                let cell = collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: value)
                return cell
            case .weekdayOperatingTime(let value), .saturdayOperatingTime(let value), .holidayOperatingTime(let value):
                let cell = collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: value)
                return cell
            case .contact(let value):
                let cell = collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: value)
                return cell
            }
        })
        
        dataSource.supplementaryViewProvider = { (view, kind, index) in
            return self.collectionView.dequeueConfiguredReusableSupplementary(
                using: headerRegistration, for: index)
        }
    }
}

이슈

MVVM Pattern

아직 감이 안잡혀서 애매했지만 .. 가장 큰 기준은 UIKit를 import하면 안된다는 것 !!!

import UIKit

import SnapKit
import Then

import NiCarNaeCar_Util
import NiCarNaeCar_Resource

// MARK: - ParkingDetailViewModel

final class ParkingDetailViewModel {
    
    // MARK: - Property
    
    var parkingLot: CObservable<ParkingLot> = CObservable(ParkingLot())
    
    // MARK: - Data Method
    
    func fetchParkingDetailInfo(_ item: ParkingDetailInfo) {
        parkingLot.value.name = item.parkingName
        parkingLot.value.location = item.addr
        
        processFee(item)
        
        parkingLot.value.isWeekdayFree = item.payNm
        parkingLot.value.isSaturdayFree = item.saturdayPayNm
        parkingLot.value.isHolidayFree = item.holidayPayNm
        
        processOperatingTime(item)
        
        parkingLot.value.contact = item.tel
    }
    
    private func processFee(_ item: ParkingDetailInfo) {
        parkingLot.value.defaultFee = "\\(item.rates)원/\\(item.timeRate)분"
        parkingLot.value.additionalFee = "\\(item.addRates)원/\\(item.addTimeRate)분"
    }
    
    private func processOperatingTime(_ item: ParkingDetailInfo) {
        parkingLot.value.weekdayOperatingTime = "\\(stringFormatter(item.weekdayBeginTime)) ~ \\(stringFormatter(item.weekdayEndTime))"
        parkingLot.value.saturdayOperatingTime = "\\(stringFormatter(item.weekdayBeginTime)) ~ \\(stringFormatter(item.weekdayEndTime))"
        parkingLot.value.holidayOperatingTime = "\\(stringFormatter(item.holidayBeginTime)) ~ \\(stringFormatter(item.holidayEndTime))"
    }
    
    private func stringFormatter(_ str: String) -> String {
        var time = str
        time.insert(":", at: time.index(time.startIndex, offsetBy: 2))
        return time
    }
}