Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- UICollectionView
- rxswift
- tableView
- Human interface guide
- collectionview
- SWIFT
- uitableview
- RxCocoa
- Observable
- 클린 코드
- combine
- 애니메이션
- UITextView
- HIG
- ribs
- 스위프트
- 리펙토링
- swiftUI
- ios
- Clean Code
- Protocol
- 리팩토링
- map
- Xcode
- swift documentation
- Refactoring
- 리펙터링
- uiscrollview
- MVVM
- clean architecture
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] MVVM구조에서 UITableView 사용 시, dataSource 관리 방법 (#프로토콜 지향 프로그래밍) 본문
iOS 응용 (swift)
[iOS - swift] MVVM구조에서 UITableView 사용 시, dataSource 관리 방법 (#프로토콜 지향 프로그래밍)
jake-kim 2023. 5. 28. 01:33dataSource 관리 방법
- dataSource를 관리하다보면 보통 UI에서 사용되는 데이터와 비즈니스로직이 들어갈 ViewModel에서 사용되는 dataSource 두 벌을 관리하는 경우가 있는데, 상태 관리를 한곳에서 사용하지 않으면 데이터 정합성 문제가 발생
- protocol을 사용하여 dataSource를 관리하면 해결
- viewController에서 dataSource에 접근할 때는 protocol에 의존하게끔하면 해결
MVVM 구조 먼저 준비
- MVVM구조를 쉽게 구현하기 위해 예제에 사용할 라이브러리
pod 'RxSwift'
pod 'RxCocoa'
- View
- ViewModel 구현체에 의존하지 않도록, ViewModelable 프로토콜을 생성하고 이것에 의존하도록 구현
- UI에 관한 input을 쉽게 viewModel에 전달하기 위해서 enum으로 Action 정의
import UIKit
import RxCocoa
import RxSwift
enum Action {
case viewDidLoad
}
protocol ViewModelable {
var output: Observable<State> { get }
func input(_ action: Action)
}
class ViewController: UIViewController {
// MARK: Properties
let viewModel: ViewModelable = ViewModel()
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
bind()
}
private func configureUI() {
// TODO
}
private func bind() {
}
}
- ViewModel
- UI에서 오는 인풋을 처리하는 함수를 구현하고, UI에서 해당 ViewModel로 상태에 관한 바인딩을 시킬 것이므로 enum State와 output을 정의
- 내부적으로 outputSubject로 State를 방출하고, 이것은 output에서 구독하고 있어서 UI쪽에서 바인딩할 수 있도록 구현
import RxSwift
import RxCocoa
enum State {
case updateUI(itmes: [String])
}
final class ViewModel: ViewModelable {
// MARK: State
// MARK: Output
var output: RxSwift.Observable<State> {
outputSubject
}
private var outputSubject = PublishSubject<State>()
// MARK: Input
func input(_ action: Action) {
switch action {
case .viewDidLoad:
print("todo")
}
}
}
- DataSourceable이라는 프로토콜을 생성하여 viewModel에서 dataSource를 가지도록 선언
protocol DataSourceable {
var dataSource: [String] { get }
}
protocol ViewModelable: DataSourceable {
var output: Observable<State> { get }
func input(_ action: Action)
}
- ViewModel에 데이터가 업데이트 되었다는 이벤트만 주면, viewController에서는 viewModel.dataSource로 접근이 가능하여 viewModel 한곳에서만 dataSource를 관리하도록 정의
ViewController) UI에서 viewDidLoad이벤트를 viewModel에 던짐
// ViewController
override func viewDidLoad() {
...
viewModel.input(.viewDidLoad)
}
ViewModel) UI에서 viewDidLoad이벤트가 오면 dataSource를 초기화한 후 updateUI이벤트 방출
// ViewModel
enum State {
case updateUI
}
final class ViewModel: ViewModelable {
// MARK: State
var dataSource = (1...10).map(String.init)
// MARK: Output
var output: RxSwift.Observable<State> {
outputSubject
}
private var outputSubject = PublishSubject<State>()
// MARK: Input
func input(_ action: Action) {
switch action {
case .viewDidLoad:
print("todo")
}
}
}
ViewController) viewModel을 바인딩 하고 있다가 업데이트요청이 오면 reload 시키고, cellForRow에서는 viewModel의 dataSource를 바라보고 있는 상태
// ViewController
private func bind() {
viewModel.output
.observe(on: MainScheduler.instance)
.bind { [weak self] state in
guard let self else { return }
switch state {
case .updateUI:
tableView.reloadData()
}
}
.disposed(by: disposeBag)
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
viewModel.dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = viewModel.dataSource[indexPath.row]
return cell!
}
}
(완성)
'iOS 응용 (swift)' 카테고리의 다른 글
Comments