Post

BehaviorRelay

BehaviorRelay

RxSwift BehaviorRelay란?

  • BehaviorRelay는 최신 값을 항상 저장하고, .value로 읽을 수 있는 RxSwift의 래퍼(Wrapper) 객체입니다.
  • 값을 변경할 때는 .accept()를 사용하며, BehaviorSubject와 다르게 .completed 또는 .error 상태가 없습니다.

  • RxSwift에서 BehaviorRelayBehaviorSubject의 대체재
  • 초기값을 설정해야 하며, 항상 최신 값을 유지
1
2
let relay = BehaviorRelay(value: "초기값")

  • *.accept()를 사용하여 값을 업데이트 (.onNext() 대신)*
1
relay.accept("새로운 값")
  • 에러 없이 계속 동작 (.completed, .error 없음)
  • 구독 시 최신 값을 바로 전달함
1
2
3
4
5
// 구독하여 값 변경 감지
let disposeBag = DisposeBag()
relay.subscribe {
    print("구독된 값: \($0)")
}.disposed(by: disposeBag)

UI에서의 사용 예제

  • UI 상태 관리 (예: ViewModel의 데이터 바인딩)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
      class ViewModel {
          let username = BehaviorRelay(value: "사용자 이름")
      }
        
      let viewModel = ViewModel()
      viewModel.username.subscribe { print("이름 변경: \($0)") }
          .disposed(by: disposeBag)
        
      viewModel.username.accept("새 이름") // 출력: 이름 변경: 새 이름
        
    
  • RxSwift에서 끊기지 않는 스트림을 유지하고 싶을 때

    • BehaviorSubject.completed 또는 .error가 발생하면 더 이상 이벤트를 받을 수 없음
    • BehaviorRelay끊기지 않기 때문에 UI에서 지속적으로 상태를 관리하는 데 적합
    • print("이름 변경: \($0)") 이 코드는 username에 값이 변경될때마다 동작함.

didSet과의 차이점

차이점didSetBehaviorRelay
초기값 감지 여부❌ 감지 안 됨✅ 감지됨 (구독 시 바로 전달)
비동기 처리 가능 여부❌ 불가능 (동기적 실행)✅ 가능 (observeOn, subscribeOn 활용 가능)
여러 곳에서 감지 가능 여부❌ 불가능 (1개만 감지)✅ 가능 (여러 구독자가 감지 가능)
UI 바인딩 가능 여부❌ 직접 구현해야 함bind(to:), subscribe(onNext:)로 가능
RxSwift 연동 가능 여부❌ 불가능✅ 가능

1. 초기값 감지 여부

didSet (초기값 감지 ❌)

1
2
3
4
5
6
7
8
9
10
11
12
class ViewModel {
    var username: String = "사용자 이름" {
        didSet {
            print("이름 변경: \(username)")
        }
    }
}

let viewModel = ViewModel()
// 초기값 출력 없음
viewModel.username = "새 이름"  
// 출력: 이름 변경: 새 이름

BehaviorRelay (초기값 감지 ✅)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import RxSwift
import RxRelay

class ViewModel {
    let username = BehaviorRelay(value: "사용자 이름")
}

let disposeBag = DisposeBag()
let viewModel = ViewModel()

// 구독 시 초기값 자동 방출됨
viewModel.username.subscribe {
    print("이름 변경: \($0)")
}.disposed(by: disposeBag)

// 값 변경
viewModel.username.accept("새 이름")

2. 비동기 처리 가능 여부

didSet (비동기 처리 ❌) - DispatchQueue를 사용해야 가능

1
2
3
4
5
6
7
8
9
10
11
12
class ViewModel {
    var username: String = "사용자 이름" {
        didSet {
            DispatchQueue.global().async {
                print("이름 변경: \(self.username)")
            }
        }
    }
}

let viewModel = ViewModel()
viewModel.username = "새 이름"

BehaviorRelay (비동기 처리 ✅)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import RxSwift
import RxRelay

class ViewModel {
    let username = BehaviorRelay(value: "사용자 이름")
}

let disposeBag = DisposeBag()
let viewModel = ViewModel()

// 구독 (비동기 처리 가능)
viewModel.username
    .observe(on: MainScheduler.instance) // 메인 스레드에서 실행
    .subscribe {
        print("이름 변경: \($0)")
    }
    .disposed(by: disposeBag)

// 백그라운드에서 값 변경
DispatchQueue.global().async {
    viewModel.username.accept("새 이름")
}

3. 여러 곳에서 감지 가능 여부

didSet (한 곳에서만 감지 가능)

1
2
3
4
5
6
7
8
9
10
11
class ViewModel {
    var username: String = "사용자 이름" {
        didSet {
            print("이름 변경: \(username)")
        }
    }
}

let viewModel = ViewModel()
viewModel.username = "새 이름"  // ✅ 값 변경 감지
// 하지만 두 번째 감지기는 추가할 수 없음 (한 번만 실행됨)

BehaviorRelay (여러 곳에서 감지 가능)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import RxSwift
import RxRelay

class ViewModel {
    let username = BehaviorRelay(value: "사용자 이름")
}

let disposeBag = DisposeBag()
let viewModel = ViewModel()

// a파일
viewModel.username.subscribe {
    print("구독 1: \($0)")
}.disposed(by: disposeBag)

// b파일
viewModel.username.subscribe {
    print("구독 2: \($0)")
}.disposed(by: disposeBag)

// 값 변경
viewModel.username.accept("새 이름")

1
2
3
4
5
//출력 결과 - 여러곳에서 감지 가능
구독 1: 사용자 이름 // a 파일
구독 2: 사용자 이름 // b 파일
구독 1:  이름 // a 파일
구독 2:  이름 // b 파일

4. UI 바인딩 가능 여부

didSet (UI 바인딩 직접 구현 필요)

1
2
3
4
5
6
7
8
9
10
11
12
13
class ViewModel {
    var username: String = "사용자 이름" {
        didSet {
            label.text = username // UI 업데이트 (직접 해야 함)
        }
    }
}

// 사용 예시 (UIKit)
let label = UILabel()
let viewModel = ViewModel()
viewModel.username = "새 이름"

BehaviorRelay (UI 바인딩 가능)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import RxSwift
import RxRelay
import UIKit

class ViewModel {
    let username = BehaviorRelay(value: "사용자 이름")
}

let disposeBag = DisposeBag()
let viewModel = ViewModel()
let label = UILabel()

// Rx를 활용한 UI 바인딩
viewModel.username
    .bind(to: label.rx.text)
    .disposed(by: disposeBag)

// 값 변경
viewModel.username.accept("새 이름")

This post is licensed under CC BY 4.0 by the author.