티스토리 뷰

TIL

[TIL] 2022 / 11 / 1

희철 2022. 11. 1. 19:30

debug()

 

 

Rx를 사용하게되면 스트림이 흐르면서 데이터 타입이 많이 바뀜

 

근데 이때 데이터 연산이 많으면 원했던 형태로 안나올 수도 있고, 이걸 중간에서 체크하기 어려움

 

Rx를 사용하기 전에는 중간에 print구문을 이용해 현재 데이터를 출력할 수 있었는데, debug도 똑같음

단순하게 print라고 생각하면됨

 

 

RxDatasource

 

 

아직 직접 적용해보진 않았고 간단하게만 확인해봄

 

테이블뷰나 컬렉션뷰의 섹션이 한 개가 아닌 경우엔 RxDatasource를 반드시 써야함.

 

디퍼블 데이터소스랑 구현 방식이 비슷함.

    lazy var dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(configureCell: { dataSource, tableView, indexPath, item in
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
        cell.textLabel?.text = "\(item)"
        return cell
        
    }) //title은 String, item이 int

 

        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        
        //이 코드는 bind보다 위에 있어야함
        dataSource.titleForHeaderInSection = { dataSource, index in //index는 각 섹션에 대한 인덱스정보
            
            return dataSource.sectionModels[index].model
        }
        
        Observable.just([
            SectionModel(model: "title1", items: [1, 2, 3]), //타이틀이 헤더가 되고 item이 셀의 정보가됨
            SectionModel(model: "title2", items: [1, 2, 3]),
            SectionModel(model: "title3", items: [1, 2, 3])
        ])
            .bind(to: tableView.rx.items(dataSource: dataSource))
            .disposed(by: disposeBag)

    }

 

위와 같이 작성하게되면 테이블뷰에는 세 개의 섹션이 있는 것.

 

각 섹션에는 1, 2, 3이 각각 셀 하나에 출력되어 있는 형태

 

 

 

Input / Output

 

 

MVVM 패턴을 적용하면 UI로직과 비즈니스 로직을 분리해줄 경우, Input과 Output을 통해 데이터의 흐름을 명확하게 구현할 수 있음.

 

Input

- View로부터 전달된 데이터를 ViewModel에서 받게 되는 입력 데이터

- 버튼의 탭, 텍스트필드에 입력하는 텍스트 등

 

output

- 입력받은 데이터를 수정/변경하여 View에 표현하기 위한 출력 데이터

- View의 상태, 텍스트, 화면전환, 얼럿 등

 

 

이런식으로 인풋과 아웃풋을 정해주면됨

 

이때, 저 타입들은 뷰컨트롤러에서 찾아볼 수 있음

 

Input, Output을 이용하게되면 뷰컨트롤러에서는 subscribe, bind, drive같은 메서드만 남게됨.

 

여기서 알 수 있듯이, 구독을 제외한 그 전까지의 스트림 진행 후의 타입을 확인해서 선언하면됨.

요런 느낌으로 viewModel의 validText는 asDriver()까지 진행되면 타입이 Driver<String>이므로 Output 구조체에서 타입이 저렇게 선언된 것.

 

그래서 asDriver()까지는 output.text로 접근하고 뷰컨트롤러에서는 drive메서드만 실행하도록해줌

        //MARK: 데이터를 처리하기 위해 뷰모델에서 데이터를 한번에 넘김
        let input = ValidationViewModel.Input(text: nameTextField.rx.text, tap: stepButton.rx.tap) //구조체 타입을 바로 가지고오기위해 ValidationViewModel에 바로 접근
        let output = viewModel.transform(input: input)

뷰컨트롤러에서 위와 같이 선언해서 접근하면됨.

 

근데 지금까지는 생략된 코드들을 따로 작성해주지 않았음.

 

그럼 이거는 어디에 작성하냐

 

output을 보면 transform이 보일것임.

 

transform도 뷰모델에 선언된 메서드인데 말그대로 인풋으로 들어온 요소의 타입을 바꿔줘서 리턴해주는 메서드라고 생각하면됨.

    //인풋으로 들어온 요소를 아웃풋으로 바꿔줘야하는ㄴ데 타입을 바꿔주는 시퀀스를 담당하는 메서드
    func transform(input: Input) -> Output {
        let valid = input.text
            .orEmpty
            .map { $0.count >= 8 }
            .share()
        
        let text = validText.asDriver()
        
        return Output(validation: valid, tap: input.tap, text: text)
    }

 

그렇기때문에 뷰컨트롤러에서 output프로퍼티를 이용할 수 있는 것임.

 

근데 뷰모델이 엄청나게 많은 경우를 생각해보면 매번 설정해주기 귀찮고 힘듦

 

 

여기서 프로토콜을 이용하면 편하게 할 수 있음

 

프로토콜로 뷰모델 구조를 추상화하는거임

 

 

이렇게 프로토콜을 만들고 뷰모델에서 채택하면됨.

 

 

associatedtype

 

제네릭을 생각하면되고, 프로토콜에서 사용될 타입에 대한 네임스페이스, 플레이스홀더라고함.

 

 

 

위에서 Input Output을 선언할때 왜 associatedtype을 썼는지 나오는데 다시말하면

 

뷰모델이 여러 개인 경우에 각각의 Input Output 구조체가 똑같지가 않기 때문임.

 

 

이름은 똑같지만 내부 구조가 다름.

 

그래서 associatedtype을 사용한 것임.

 

이렇게 프로토콜을 선언하고 뷰모델에서 채택하게되면 아래와 같이 떠서 편하게 만들 수 있음.

class bvm: CommonViewModel {
    
    func transform(input: Input) -> Output {
        <#code#>
    }
    
    struct Input {
        
    }
    
    struct Output {
        
    }
}

실제로는 typealias가 뜰텐데 굳이 사용안하고 위처럼 사용해도됨.

 

또 associatedtype을 이용할때 프로토콜 제약을 추가할 수 있음.

protocol Test {
    
    associatedtype TestA: Codable
    associatedtype TestB: Codable
}

'TIL' 카테고리의 다른 글

[TIL] 2023 / 03 / 16 - Python  (0) 2023.03.16
[TIL] 2022 / 12 / 19 - SwiftUI  (0) 2022.12.19
[TIL] 2022 / 10 / 27 - Rx  (0) 2022.10.27
[TIL] 2022 / 10 / 26 - Rx  (0) 2022.10.26
[TIL] 2022 / 10 / 25 - Rx  (0) 2022.10.25
댓글
최근에 올라온 글
Total
Today
Yesterday