시간이 꽤 많다고 생각했지만, 현재 진행중인 프로젝트에 추가적인 기능을 넣고자 계획을 해둔 것을 고려하면 시간이 많지 않다 느껴진 하루였다. 아무래도 과제 선정에 있어 완성도를 높이고자 조금 쉬운 내용을 선정했던 것을 생각하면, 추가적인 기능 개발 및 코드 수정은 필수적이라고 생각한다. 금요일 발표 준비까지 고려하면, 내일 점심 이후부터는 도전 및 추가 기능 구현에 시간을 쏟아야할 것이다.
오늘은 마이페이지 레이아웃 수정 및 지난번에 만들어 둔 검색 페이지를 상세페이지와 연결하는 작업을 진행했다.
페이지의 로직을 담당하는 ViewModel이다. 마찬가지로, 처음에는 MVC 패턴을 통해 작업을 진행하고 완성 이후 MVVM을 적용했다. ViewController에서 사용하기 위한 데이터를 가공 및 처리하는 로직을 담당했으며, Logout기능 수행 시 UserDefaults에서 데이터를 지우는 메서드, 해당 셀 클릭 시 로그아웃 기능이 동작하도록 closure와 메서드를 구현했다.
func didSelectMenuItem(index: Int) {
let item = menuItems[index]
if item.isLogout {
showLogoutAlert?()
return
}
navigateTo?(index)
}
func confirmLogout() {
removeUserData()
onLogout?()
}
func removeUserData() {
UserDefaults.standard.set(false, forKey: "isLogin")
UserDefaults.standard.removeObject(forKey: "userName")
UserDefaults.standard.removeObject(forKey: "userEmail")
}
ViewModel에서 가공 및 처리되어 넘어온 데이터를 View에 표시하기 위해 로직을 구현했으며, collectionView의 dataSource, delegate를 연결해준다. 또한 bind를 통해 viewModel로부터 넘어온 데이터를 처리한다. collectionView의 section 별로 사용되는 cell을 정의하고 config하도록 한다.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let sectionType = viewModel.sections[indexPath.section]
switch sectionType {
case .profile:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyPageProfileCell.id, for: indexPath) as? MyPageProfileCell else { return UICollectionViewCell() }
cell.config(name: viewModel.userName, email: viewModel.userEmail)
return cell
case .menu:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyPageMenuCell.id, for: indexPath) as? MyPageMenuCell else { return UICollectionViewCell() }
let item = viewModel.menuItems[indexPath.item]
cell.config(title: item.title, subTitle: item.subTitle, iconName: item.iconName, isLogout: item.isLogout)
return cell
case .info:
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyPageInfoCell.id, for: indexPath) as? MyPageInfoCell else { return UICollectionViewCell() }
let item = viewModel.infoItems[indexPath.item]
cell.config(count: item.count, title: item.title)
return cell
}
}
createLayout 메서드를 이용하여 section 별로 생성하는 Layout을 구성해준다. MyPageSectionType이라는 enum을 정의하여 case로 섹션의 타입들인 profile, menu, info를 생성한다. 해당 타입을 switch 문으로 받아서 사용하며, 섹션별로 사용할 메서드를 각각 구현해준다. CollectionView를 많이 사용해보지 않아서 분리하여 작업을 하는 것이 더 가독성에도 좋을 것이라 생각했으며 코드를 작성하는 본인도 헤매거나 수정이 필요할 때 찾기 편했다.
셀 내부에 들어갈 UI들에 대한 배치와 받아올 값을 메서드로 정의해준다.
마이페이지는 API 통신을 통해 데이터를 받아와서 표시하지 않는다. 추후에 CoreData에 저장된 정보와 UserDefaults에 저장된 정보를 통해 표시하겠지만, 셀에 들어가는 기본적인 정보들에 대해서는 구조체로 정의해두는 것이 사용하기 편할 것이라 생각하여 MyPageData.swift를 생성했다. 해당 파일에는 Menu Section과 Info Section에서 사용되는 기본적인 데이터들에 대해 정의해두었으며 해당 내용은 ViewController의 DataSource에서 사용한다.
검색 기능은 정상적으로 동작한다. 수정이 필요한 부분은 검색하여 표시된 정보들을 통해 버튼을 클릭하면 상세 페이지로 연결할 수 있도록 구현하는 것이다. 일단 셀의 버튼을 클릭했을 경우에 따라 처리해야 하기 때문에 SearchTableViewCellDelegate를 생성하여 viewController에 delegate를 선언할 수 있도록 구성한다. 또한, 버튼에 addTarget을 통해 클릭되었을 시 동작하는 메서드를 생성해준다.
버튼이 클릭되면 delegate인 ViewController에서 동작을 처리할 수 있도록 protocol을 채택하고 didTapDetailButton을 구현한다. 팀원분이 구현해둔 ViewModel을 생성자 방식으로 주입하여 ViewController에 넣어서 구현했으며, tableView.indexPath(for: cell)을 통해 indexPath를 받아와서 쉽게 구현할 수 있었다.
extension SearchViewController: SearchTableViewCellDelegate {
func didTapDetailButton(cell: SearchTableViewCell) {
guard let indexPath = searchView.tableView.indexPath(for: cell) else { return }
let movie = viewModel.getMovieData(index: indexPath.row)
let detailVM = MovieDetailViewModel(movie: movie)
let detailVC = MovieDetailViewController(viewModel: detailVM)
navigationController?.pushViewController(detailVC, animated: true)
}
}