brunch
매거진 ReactiveX

RxSwift, delegate 와 Observable

DelegateProxy, DelegateProxyType

by Tilltue
스크린샷 2016-10-26 오전 1.37.26.png

* 이글은 Swift 3.0 , RxSwift 3.0.0 을 기준으로 작성되었습니다.

Swift 에서 일반적인 delegate / protocol 를 사용하는 구조를 생각해보자.

컬러를 선택하는 커스텀 뷰를 만들었다고 가정하자.

이 뷰는 아래와 같이 선택된 컬러를 전달하는 protocol 을 delegate method 로 사용한다.


- 일반적인 protocol, delegate 메서드 사용방법.


protocol ColorPicker {

func selected(color: UIColor)

}


class ColorPickerView: UIView {

weak var delegate: ColorPicker? = nil

func selected(color: UIColor){

self.delegate?.selected(color: color)

}

}


class ViewController: UIViewController,ColorPicker {

var pickerView: ColorPickerView?

func selected(color: UIColor) {

print("selected color")

}

override func viewDidLoad() {

super.viewDidLoad()

pickerView = ColorPickerView()

pickerView?.delegate = self

}

}


만약 아래와 같은 구문으로 쓴다면 어떨까?

pickerView?.rx_selectedColor.subscribe(onNext: { color in

print("onNext")

}).addDisposableTo(disposeBag)


이렇게 delegate method 를 rx_delegate 형태로 확장해보자.



1. Delegate Proxy 를 만들자.

Rx 의 DelegateProxy 를 상속받고, DelegateProxyType,ColorPicker 프로토콜에 따르도록 아래와 같이 구성한다.


class RxColorPickerDelegateProxy: DelegateProxy,DelegateProxyType,ColorPicker {

internal func selected(color: UIColor) {

}

static func currentDelegateFor(_ object: AnyObject) -> AnyObject? {

let pickerView: ColorPickerView = object as! ColorPickerView

return pickerView.delegate as AnyObject?

}

static func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {

let pickerView: ColorPickerView = object as! ColorPickerView

pickerView.delegate = delegate as? ColorPicker

}

}


2. 원하는 rx_메서드를 delegateProxy 를 통해 연결한다.


extension ColorPickerView {

public var rx_delegate: DelegateProxy {

return RxColorPickerDelegateProxy.proxyForObject(self)

}

public var rx_selectedColor: Observable<UIColor?> {

return rx_delegate.sentMessage(#selector(RxColorPickerDelegateProxy.selected(color:))).map{ a in a[0] as? UIColor }

}

}

* 여기서 a 는 selected 함수 parameter를 의미한다. Observable<[Any]> 로 전달 되므로 필요한 형태로 mapping 해주면 된다.


3. 이제 사용하면 된다!


pickerView?.rx_selectedColor.subscribe(onNext: { color in

print("onNext")

}).addDisposableTo(disposeBag)



부록 : 이미 존재하는 RxCocoa delegate 의 확장

- 간단하게 2번 과정을 통해 바로 observable 을 생성하면 된다.

예를 들어 UIScrollViewDelegate 의 scrollViewWillBeginDragging 를 Observable 로 사용하려면


tableView.rx.delegate

.sentMessage(#selector(UIScrollViewDelegate.scrollViewWillBeginDragging(_:)))

.map{ a in a[0] as? UIScrollView }.subscribe(onNext: { scrollView in


}).addDisposableTo(disposeBag)


이렇게 해줘도 되고 위에서 확장한 것과 같이 UITableView 에서 확장해서 rx 메서드로 만들어 두고 써도 된다.




사견: RxSwift 를 프로젝트 전반적으로 사용한다면 이런 다양한 방법을 통해 코드를 일관성 있게 유지할 수 있을 것 같다. 또 이 렇게 delegate 도 Rx 메서드로 만들어 둔다면 다른 Observable 과의 결합이나, Utility 함수들을 사용하면 더 활용도가 높을 것으로 생각한다.



keyword
매거진의 이전글RxSwift, Hot & Cold Observable