iOS 개발/번역기 앱(RxSwift)

번역기 앱 - 5. 보관함 화면 구현

Skillist 2022. 2. 1. 15:57
반응형

안녕하세요. Skillist입니다

 

오늘은 보관함 VC를 구현해보겠습니다.

이전에 구현한 기록 VC와는 다른 부분이 있습니다.

RxDataSource를 활용하여 tableView를 구현합니다.

이부분을 중점으로 보시면 될것 같아요.

 

———————————————————————————————————————————————————

 

viewModel부터 볼게요!

 

12라인 : RxDataSource의 AnimatableSectionModel을 사용합니다. section과 아이템 타입(HistoryModel)입니다.

18라인 : bookmark delete 이벤트를 view에서 viewModel로 전달할거에요.

21라인 : view의 tableView가 사용할 목록이에요. section 모델 배열을 방출합니다.  section을 1개 밖에 사용하지 않지만요. 또, 기본값을 가지기 위해서 BehaviorRelay를 사용했습니다. 

22라인 : 데이터 소스 입니다. view의 tableview가 datasource를 활용하여 section과 cell을 구성할거에요.

 

21라인의 bookmarks는 tableView의 section 어레이 정보를 담고,

22라인의 datasource는 tableView의 section과 Cell을 구성합니다. 기존 view에서 작성한 cell 코드를 viewModel에서 구현했다고 생각하면 됩니다.

 

41라인 : 이니셜라이저 입니다.

42라인 : UserDefaults.standard.rx.observe를 활용하여, userDefaults의 bookmark를 관찰합니다.

값을 받아와, map을 통해 sectionModel로 전달하여 "bookmarks"에 bind합니다.

 

57라인 : bookmark를 삭제하면, userDefaults의 bookmark에서 삭제 합니다.

그럼 userDefaults의 bookmark가 변경되면, 42번 라인이 수행되겠죠?

 

저는 RxSwift를 공부할때, 이벤트 흐름과, 어떤 옵저버블과 옵저버, 서브젝트 등 사용할지에 대해서 고민하고 있습니다.

 

———————————————————————————————————————————————————

 

이번엔 View를 보겠습니다.

17라인 : 테이블뷰입니다.

25라인 : viewDidLoad에서 기본 설정과 레이아웃을 설정합니다.

32라인 : bind입니다.

33라인 : viewModel을 저장 프로퍼티에 할당합니다. 33라인이 없다면, 보관함 VC에 정상동작하지 않는 버그가 발생하는데, 왜 발생하는지 잘 모르겠습니다. 아시는분 계시면 알려주세요!!

 

35라인 : viewModel의 bookmarks(목록)을 tableview에 바인딩합니다.

40라인 : tableView에서 delete한 경우, viewModel의 deletaBookmark에 바인딩 합니다.

비즈니스 로직은 viewModel로 이전합니다. 결과적으로 View는 깔끔해졌습니다. 

 

45라인 : view에 대한 기본 설정합니다.

50라인 : 스냅킷을 통해서 tableView의 제약사항을 구성합니다.

 

———————————————————————————————————————————————————

 

UserDefault의 Bookmark 또한, History와 다를바가 없습니다.

 

bookmark에 대한 add와 delete를 구현했습니다.

따로 설명하지 않아도 되겠죠?

 

———————————————————————————————————————————————————

 

어때요??? 이벤트 흐름을 생각해보면 이해하기 쉬워집니다.

익숙하지 않다면, 특정 UI컴포넌트에 대한 흐름을 살펴보고 다른 UI컴포넌트 흐름을 보면 될거에요.

먼저, 기능 정의가 돼있다면, 구현할때에 훨씬 수월해집니다.

 

이렇게 BookmarkView에 대해서 구현해봤습니다.

 

잘못되거나 부족한 내용 등, 피드백 감사합니다!

 

https://github.com/DeveloperSkillist/TranstorKing

 

 

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓  전체 코드  ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

class BookMarkView: UIViewController {
    let disposeBag = DisposeBag()
    var viewModel: BookMarkViewModel?
    
    private var tableView: UITableView = {
        var tableView = UITableView()
        tableView.backgroundColor = .secondarySystemBackground
        tableView.separatorStyle = .none
        tableView.register(BookMarkTableViewCell.self, forCellReuseIdentifier: BookMarkTableViewCell.identify)
        return tableView
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        attribute()
        layout()
    }
    
    func bind(_ viewModel: BookMarkViewModel) {
        self.viewModel = viewModel
        
        viewModel.bookmarks
            .debug()
            .bind(to: tableView.rx.items(dataSource: viewModel.datasource))
            .disposed(by: disposeBag)
        
        tableView.rx.modelDeleted(HistoryModel.self)
            .bind(to: viewModel.deleteBookmark)
            .disposed(by: disposeBag)
    }
    
    private func attribute() {
        view.backgroundColor = .secondarySystemBackground
        navigationItem.title = "bookmark_title".localize
    }
    
    private func layout() {
        view.addSubview(tableView)
        
        tableView.snp.makeConstraints {
            $0.edges.equalToSuperview()
        }
    }
}
import RxSwift
import RxCocoa
import RxDataSources

typealias BookmarkSectionModel = AnimatableSectionModel<Int, HistoryModel>

struct BookMarkViewModel {
    let disposeBag = DisposeBag()
    
    //view -> viewModel
    let deleteBookmark = PublishRelay<HistoryModel>()
    
    //viewModel -> view
    let bookmarks = BehaviorRelay<[BookmarkSectionModel]>(value: [])
    let datasource: RxTableViewSectionedAnimatedDataSource<BookmarkSectionModel> = {
        let dataSource = RxTableViewSectionedAnimatedDataSource<BookmarkSectionModel>(configureCell: {
            (dataSource, tableView, indexPath, bookmark) -> UITableViewCell in
            guard let cell = tableView.dequeueReusableCell(
                withIdentifier: BookMarkTableViewCell.identify
            ) as? BookMarkTableViewCell else {
                return UITableViewCell()
            }
            cell.selectionStyle = .none
            cell.setup(bookmark: bookmark)
            return cell
        })
        
        dataSource.canEditRowAtIndexPath = { _, _ in
            return true
        }
        return dataSource
    }()
    
    init() {
        UserDefaults.standard.rx.observe(Data.self, UserDefaults.standard.bookmarkKey)
            .map {
                guard let data = $0 else {
                    return []
                }
                
                return (try? PropertyListDecoder().decode([HistoryModel].self, from: data)) ?? []
            }
            .map {
                [BookmarkSectionModel(model: 0, items: $0)]
            }
            .debug()
            .bind(to: bookmarks)
            .disposed(by: disposeBag)
        
        deleteBookmark
            .subscribe(onNext: {
                UserDefaults.standard.deleteBookmark(historyModel: $0)
            })
            .disposed(by: disposeBag)
    }
}
extension UserDefaults {
    
    var bookmarkKey: String {
        return "bookmark"
    }
    
    var bookmark: [HistoryModel] {
        get {
            guard let data = UserDefaults.standard.data(forKey: bookmarkKey) else {
                return []
            }
            
            return (try? PropertyListDecoder().decode([HistoryModel].self, from: data)) ?? []
        }
        set {
            UserDefaults.standard.setValue(try? PropertyListEncoder().encode(newValue), forKey: bookmarkKey)
        }
    }
    
    func addBookmark(historyModel: HistoryModel) {
        var bookmarks = UserDefaults.standard.bookmark
        if let index = bookmarks.firstIndex(of: historyModel) {
            bookmarks.remove(at: index)
        }
        
        self.bookmark = [historyModel] + bookmarks
    }
    
    func deleteBookmark(historyModel: HistoryModel) {
        var bookmarks = UserDefaults.standard.bookmark
        if let index = bookmarks.firstIndex(of: historyModel) {
            bookmarks.remove(at: index)
            self.bookmark = bookmarks
        }
    }
}
반응형