Traits
traits์ ๊ธฐ์กด Observable ๋ณด๋ค ์ข๋ ์์ ๋ฒ์์ ๊ธฐ๋ฅ์ ์ํํ๋ Observable ์ด๋ค ํ์๋ก ์์์ผ ํ๋ ๊ฐ๋ ์ ์๋๋ค
ํ์ง๋ง ๋ด๊ฐ ์์ฑํ ์ฝ๋๋ฅผ ๋ค๋ฅธ ์ฌ๋์ด ๋ณผ๋ ์ข๋ ๋ช ํํ๊ฒ ๋ณผ ์ ์๋ค. ๊ผญ ํ์ํ ๊ฒ์ ์๋๋ ๋ง์
Single : .success(value) == .nextํน์ .error == .completed ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ๋ค ๋ฐ์ดํฐ๋ฅผ ๋ค์ด๋ก๋ ํ๊ฑฐ๋ ๋์คํฌ๋ก ๋ถํฐ ๋ก๋ฉ์ด ๊ฑธ๋ฆด ๋ ์ฌ์ฉ๋๋ ๋ณด๋ค
Completable .completed ํน์ .error ์ด๋ฒคํธ๋ฅผ ๋ฐํ. ์ด๋ ํ ๊ฐ๋ ๋ฐฉ์ถํ์ง ์๋๋ค. ๋ง์ฝ์ ์ค์ง ์ฑ๊ณต๊ณผ ์คํจ๋ก ๋๋๋ ์คํผ๋ ์ดํฐ๊ฐ ํ์ํ ๊ฒฝ์ฐ ์ฌ์ฉ๋ ์ ์๋ค. ์๋ฅผ ๋ค๋ฉด ํ์ผ์ ์ฝ๊ณ ๊ทธ๊ฒ์ด ์ฑ๊ณตํ๋์ง ์ํ๋์ง ๋ฐฉ์ถํ ์ ์๋ค.
Maybe single๊ณผ completable ์ ๋ชจ๋ ๋ฐฉ์ถํ ์ ์๋ค. succeed์ fail๋ ๊ฐ๋ฅํ๊ณ ๊ฐ๊ณผ ํจ๊ป success๋ ๊ฐ๋ฅํ๋ค.
์ค์ง ์ฝ๊ธฐ ์ํ ์ต์ ๋ฒ๋ธ ์ํ์ค ํ๋กํผํฐ๋ฅผ struct๋ก ์๋ ํ ํด์ ์ฌํํ๊ฒ ์ฌ์ฉํ ์ ์๋ค
์ต์ ๋์ธ ์ด์ ๊ฐ ๊ผญ Traits๋ฅผ ์ฌ์ฉํ์ง ์๋๋ผ๋ ๋ค๋ฅธ operator์ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ธ๋ฐ ์ Traits๋ฅผ ์ฌ์ฉํด์ผ ํ๋๋?
์ฝ๊ฒ ์ํ์ค๋ฅผ ์ ๋ฌํ๊ณ ํ์ธ ํ ์ ์์ผ๋ฉฐ ๊ฐ๊ฐ ํน๋ณํ๋ ํน์ง๊ณผ ๊ตฌ์ฒด์ ์ธ ๋ถ๋ถ์ด ์์ด์ ๊ตฌํํ๊ณ ์ ํ๋ ๊ธฐ๋ฅ์ ์ ํํํ ์ ์์ผ๋ฉฐ ๊ฐ๋ฐ์์ ์๋๋ฅผ ๋ค๋ฅธ ๊ฐ๋ฐ์์ ์ฝ๊ฒ ๊ณต์ ๊ฐ๋ฅํ๋ค๋ ๊ฒ์ด๋ค(์ธ์ ๋ ๋๋ผ๋ ๊ฒ์ด์ง๋ง ๊ฐ์ด Rx์ ๋ํ ์ดํด๊ฐ ์ถฉ๋ถํ ๋ ๋ค๋ฅธ ๊ฐ๋ฐ์์์ ์ํต์ด ์ํํ๋ค๊ณ ์๊ฐํ๋ค ๋๋ Rx์ธ๋ฐ ๋ค๋ฅธ ํ์์ด Rx ์ฝ๋๋ฅผ ๋ณธ๋ค? ๊ทธ๊ฑด ์ํต์ด ์๋๋ ๊ฒ์. ์ฝ๋๊ฐ ์๋ฌด๋ฆฌ ์ข์๋ ์์ฌ์ํต๋ ์ค์ํ๋ค)
Single
success์ error ๋๊ฐ์ ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ๊ณ success์ผ ๋๋ ํ๋์ element๋ฅผ ๋ฐฉ์ถํ๋ traits ์ด๋ค
๋คํธ์ํฌ ํต์ ์ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์ค๊ฑฐ๋ ๊ฒฝ๋ก๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด ๋ค์ผ ๋ ์ฌ์ฉํ ์ ์๋ค ์ฑ๊ณตํ๋ฉด ๋ฐ์ดํฐ๋ฅผ(single element)๋ฅผ ์คํจํ๋ฉด .error(NotFound)๋ฅผ ๋ฐฉ์ถํ๋ฉด ๋๋ค
๊ธฐ์ตํ ๊ฒ์ success ์ผ๋ ํ๋์ element ์๋๋ฉด error์ ๋ฐฉ์ถํ๋ฉด ๋๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค
๊ทธ๋ฐ๋ฐ error ์์ ๋นจ๊ฐ ์ค ๊ทธ์ด๋๋ฐ ํ๋ฒ ์ดํด๋ณด๋ฉด
๊ฒฐ๊ตญ SingleEvent๋ Result๋ก ๋์ด ์๋๋ฐ .success์ .failure๋ผ .error ์ฐ๋ฉด ๋น๋๊ฐ ์๋์๋ ๊ฒ.
import UIKit
import RxSwift
public enum NetworkError: Error {
case networkingFailed
}
class TraitsViewController: UIViewController {
var isDataDownloaded = true
override func viewDidLoad() {
super.viewDidLoad()
let disposeBag = DisposeBag()
loadText(from: "test")
.subscribe { element in
print(element)
} onFailure: { error in
print(error)
} onDisposed: {
print("dispossed")
}.disposed(by: disposeBag)
}
func loadText(from name: String) -> Single<String> {
return Single.create { single in
if self.isDataDownloaded {
// ๋ค์ด๋ก๋ ์๋ฃ์
single(.success("์๋ก์ด ๋ฐ์ดํฐ๋ก ๊ต์ฒด๋จ"))
} else {
single(.failure(NetworkError.networkingFailed))
}
return Disposables.create()
}
}
}
Single์์ success๋ .next + .complete์ ์๋ฏธ๋ก ๋ณผ ์ ์๋๋ฐ ์ด๋ .complete๋ฅผ ์๋ฏธํ๋ ๊ฒ์ด ์๋๋ค traits๊ฐ observable๋ณด๋ค ์ข์ ๋ฒ์์ด๊ธฐ ๋๋ฌธ์ Single์ .complete ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ ์ ์๋ค!
๋คํธ์ํน ํต์ ํ ๋ ์ฑ๊ธํค์ ์ฌ์ฉํ๋๋ฐ ์ฑ๊ธํค์์ ๋คํธ์ํน ํ๋ ํจ์์ ๋ฐํ๊ฐ์ Single ๋ก ๋ง๋ค๋ฉด subscribe ํด์ ์ฌ์ฉํ๋ฉด ์ข๊ฒ ๋ค๋ ์๊ฐ์ด ๋ ๋น
Completable
ํ๋์ element๋ฅผ ๋ฐฉ์ถํ๋ Single๊ณผ ๋ฌ๋ฆฌ ์๋ฌด๋ฐ ๊ฐ๋ ๋ฐฉ์ถํ์ง ์๋ traits ์ด๋ค
๊ทธ๋์ ๋ฐ์ดํฐ ์์ด .completed์ .error ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ๋ค ์ด๋ฒคํธ๋ก ๊ฒฐ๊ณผ๋ง ๋ฐ์ ์ ์๊ธฐ ๋๋ฌธ์ ๊ฐ์ด ์๊ด ์๋ ๊ณณ์์ ์ฌ์ฉํ ์ ์๋๋ฐ ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ ํ ์๋ฆผ์ ๋ฐ์ ๋๋ผ๋์ง ๋ฐ์ดํฐ๋ ํ์ ์๊ณ ๋ค์ด๋ก๋ ๋ฐ ์ ๋ฐ์ดํธ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋์๋์ง๋ง ํ์ธ ํ๊ณ ์ถ์ ๊ฒฝ์ฐ(์คํ๋ ์ ๊ฐ์..)์๋ง ์ฌ์ฉํ๋ฉด ๋๊ฒ ๋ค!
์์ ์ดํด๋ณธ Single๊ณผ ๋ฌ๋ฆฌ Completable์ ์ด๋ฒคํธ ๋ฐฉ์ถ์ Element๊ฐ ์๋ค!
func loadCompletable(isAvailable: Bool) -> Completable {
return Completable.create { event in
if isAvailable {
event(.completed)
} else {
event(.error(NetworkError.networkingFailed))
}
return Disposables.create()
}
}
loadCompletable(isAvailable: true)
.subscribe { event in
print(event)
}.disposed(by: disposeBag)
>>> completed
๊ฐ ์ฝ์์ฐฝ์ ์ฐํ๋น
Maybe
Single๊ณผ Completable ๊ธฐ๋ฅ์ ์ํ ํ ์ ์๋ Traits ์ด๋ค! .success ์ .completed, .error ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถ ๊ฐ๋ฅํ๋ค
Completable ์ .completed ์์์ ๊ฐ์ด ์๋ฌด๋ฐ element ์์ด ์ด๋ฒคํธ ๋ฐฉ์ถ๋ ๊ฐ๋ฅํ๋ค
=> ์๋ฌ ์ด๋ฒคํธ ๋ฐฉ์ถ ๊ฐ๋ฅ or element์ ํจ๊ป .success ์ด๋ฒคํธ ๋ฐฉ์ถ, element ์์ด .completed ์ด๋ฒคํธ ๋ฐฉ์ถ ๊ฐ๋ฅ
var previousString = "old data"
func loadStringWithMaybe() -> Maybe<String> {
return Maybe.create { event in
let newString = "new String"
if self.previousString == newString {
event(.completed)
} else if self.previousString != newString {
event(.success(newString))
} else {
event(.error(NetworkError.networkingFailed))
}
return Disposables.create {
print("maybe disposed")
}
}
}
์ ์ญ๋ณ์๋ก ๋์ด ์๋ previousString๊ณผ ์๋ก์ด ๋ฐ์ดํฐ๊ฐ ๊ฐ์ ๊ฒฝ์ฐ .completed ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ๊ณ
๋ง์ฝ ๋ฐ์ดํฐ๊ฐ ๋ค๋ฅด๋ฉด ํด๋น ๋ฐ์ดํฐ์ ํจ๊ป .success ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถํ๋๋ก ํ ์ฝ๋์ด๋ค
์ด ์ฝ๋๋ฅผ .success ์ด๋ฒคํธ์ element๋ก ํจ๊ป ๋ฐฉ์ถํ๋ ์ด์ ๋ ๋ค๋ฅธ ๊ณณ์์ ์ ๋ฐ์ดํธ ํ๋ ค๊ณ ํด๋์ ๊ฒ์ธ๋ฐ,,,
์ฌ์ค ์ด ์ฝ๋ ๋ด๋ถ์์ ํด๋ ์๊ด ์๋๋ฐ ์์ ๋ฅผ ์๊ฐํด ๋ณด๋ค๊ฐ ์ฐ์ ํ ์คํธ๋ง ํด๋ณด๊ณ ์ ๊ฐ๋จํ๊ฒ ๋ง๋ค์๋น
subscribe๋ ์ด๋ ๊ฒ ์์ฑํ๋ฉด ๋๋ค
loadStringWithMaybe()
.subscribe { maybe in
switch maybe {
case .success(let newData):
self.previousString = newData
print(self.previousString)
case .error(let error):
print(error)
case .completed:
print("completed maybe")
}
}
.disposed(by: disposeBag)
.success์์๋ง element๋ฅผ ๋ฐ์ ์ ์๋ค!
Do
side effect
do()๋ก event๋ฅผ ์ฐ์ด๋ณด๋ฉด์ ๋๋ฒ๊น ๊ฐ๋ฅ
์ด๋ฐ ์์ผ๋ก ์ด๋ฒคํธ๊ฐ ๋ฐฉ์ถ ๋ ๋ ๋ง๋ค Do์์ ์ ์ํด์ ๊ฐ์ ์ฌ์ฉ ํ ์ ์๋ค
๋๋ฒ๊น ํ ๋ print๋ก ์ฐ์ด ๋ณผ ์ ์์
.next ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋๋ง๋ค do๊ฐ ํธ์ถ ๋จ
side effet๋ observable ๋ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ด๋ณด๋ด๋ ํ์๋ฅผ ๋งํ๋๋ฐ
์ํ์ค์์ next๋ก ์ฌ๋ฌ event๋ค์ด ๋ฐฉ์ถ๋๋ฉด ์ค๊ฐ ์ค๊ฐ ๊ฐ์ ์บ์นํด์ ์ฐ๋๋ณด๋ค ์๋ ๋ชฉ์ ์ผ๋ก ์ฐ์ด์ง ์๊ณ ๋ถ์์ฉ์ ์ผ์ผํค๋๊ฒ ์ฌ์ด๋ ์ดํํธ๋ผ๊ณ ์๊ฐํ๋๋ฐ
์๋ Observable ์ฒ๋ผ ์ฐ์ด์ง ์๊ณ side๋ก ์ฐ์ฌ์ ๊ทธ๋ฐ๊ฑธ๊น?
side effect๋ฅผ ํตํด์ ์ฝ๋ ํ์ฌ ํ๋ฆ ํ์ธ ๊ฐ๋ฅ, ๋ฉ์๋ ์คํ๋๊ธฐ ์ ์ ์์ ์ง์ ๊ฐ๋ฅํ๋ค๋ ์ฅ์ ์ด ์์
let disposeBag = DisposeBag()
Observable.of(1, 2, 3)
.do(onNext: { element in
print("์ต์ ๋น do : \(element)")
})
.subscribe(onNext: {
print("next event : \($0)")
})
.disposed(by: disposeBag)
์ต์ ๋น do : 1
next event : 1
์ต์ ๋น do : 2
next event : 2
์ต์ ๋น do : 3
next event : 3
์ด๋ฒคํธ๊ฐ ๋ฐฉ์ถ ๋ ๋๋ง๋ค do์์ ํด๋น ์ด๋ฒคํธ์ element์ ์ํ๋ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค!
Subjects
observable๊ณผ observer์ ๋์์ ์ํํ ์ ์์
์ด์ ์ ๋ฐฐ์ ๋ Observer์ ๋ค๋ฅธ ์ ์ multicast ๋ฐฉ์์ด๋ผ๋ ๊ฒ์ด๋ค
์ฌ๋ฌ Observer๊ฐ ๋์์ ๊ตฌ๋ ํ์ฌ ์ด๋ฒคํธ๋ฅผ ๋ฐํ์ํฌ ์ ์๋ค
๊ฐ์ฅ ๋ํ์ ์ผ๋ก PublishSubject ๊ฐ ์๋น
์์ง ๊ณต๋ถ ์ค์ด๋ผ ์ ๋ชจ๋ฅด๊ฒ ๋๋ฐ ์ด๊ฑธ๋ก ์๋ง ๋น๋๊ธฐ์ ์ธ๊ฑธ ๋๊ธฐ์ ์ฒ๋ผ ์ธ ์ ์์ ๋ชจ์์ด๋น
ํท๊ฐ๋ฆฌ๋ Observer์ ๋ค์ ๋ณด๋ฉด
let observer = Observable.of("1", "2") // ์ต์ ๋ฒ๋ธ : ์ด๋ฒคํธ ์ ๋ฌ
// ์ต์ ๋ฒ๋ธ์ ๊ฐ์ํ๊ณ ์๋ค๊ฐ ์ ๋ฌ๋๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํจ
let subscription3 = observer.subscribe(onNext: { element in
print("3 : \(element)")
})
let subscription4 = observer.subscribe(onNext: { element in
print("4 : \(element)")
})
์ต์ ๋ฒ๋ธ์ ์์ฑ
์ต์ ๋ฒ๋ธ์ ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์ถ์ํจ๋ค Observer๋ subscribe๋ก ์ต์ ๋ฒ๋ธ์ ๊ฐ์ํ๊ณ ์๋ค๊ฐ ์ ๋ฌ๋๋ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ฒ ๋๋ค
์ต์ ๋ฒ๋ธ์ด ์๋ค -> ๊ตฌ๋ ํ๋ค -> ์ต์ ๋ฒ๋ธ์ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ์ฒ๋ฆฌํ๋ค
์ ์ฝ๋๋ฅผ ๋ง๋ธ ๋ค์ด์ด๊ทธ๋จ์ผ๋ก ํํํ๋ฉด ์ด๋ฌํ๋น
subejct๋ ํท๊ฐ๋ฆฌ๋ฉด ์๋๋ ๊ฒ์ด ๊ฐ์ ๊ตฌ๋ ํ๋ค๋ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋๊น subscription3์ subscription 4์ ์ํ์ค๋ ๋ค๋ฅด๋ค ๊ฐ๊ฐ ๋ฐ๋ก ๊ฐ๋ค 1:1๋ก ๊ตฌ์ฑ๋์ด ์๋ ๊ฒ
๊ทธ๋ฐ๋ฐ subject๋ Subject๊ฐ ์๊ณ ์ด๋ฅผ ๊ตฌ๋ ํ๋ ์ฌ๋ฌ๊ฐ์ subscriber๊ฐ ์๋ค
๋น์ฐํ ์ํ์ค๋ ๋ฐ๋ก ๊ฐ๋ค
let subject = PublishSubject<String>()
subject.onNext("1") // publisher๋ ์์ง๋ง Observer๊ฐ ์์ผ๋ฏ๋ก ์๋ฌด๊ฒ๋ ์ถ๋ ฅ๋์ง ์๋๋ค
let subscriptionOne = subject
.subscribe(onNext: { element in
print("one : \(element)")
})
subject.onNext("3")
let subscriptionTwo = subject
.subscribe(onNext: { element in
print("two : \(element)")
})
subject.onNext("2")
๋ฐ์ดํฐ๋ฅผ subscriber์๊ฒ ๋ฟ๋ ค์ค ์ ์์
subscriber๋ subscribe ํ ์์ ์ดํ์ ๋ฐ์๋๋ ์ด๋ฒคํธ๋ง ์ ๋ฌ ๋ฐ๋๋ค
๋ ๊ทธ๋ฆผ์ ๋ณด๋ฉด ์ ์ ์๋ฏ์ด Observer์ Subject๋ observable์ ์ญํ์ ํ๊ณ
Subject๋ ์ฌ๊ธฐ์ Observer์ ์ญํ๋ ํ๋ค๊ณ ๋ณผ ์ ์๋ค
์ฐธ๊ณ ์ฌ์ดํธ ๋ฐ ๋์
https://medium.com/@priya_talreja/rxswift-traits-4408d66cb6ad
rxswift reactive programming with swift _ raywenderlich
ใด https://www.raywenderlich.com/books/rxswift-reactive-programming-with-swift
https://velog.io/@sangjin98/iOS-RxSwift-Observable-Disposable
ใด side effect ์ฅ์ ์ฐธ๊ณ
'๐ iOS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[RxSwift] Relays์ ๋ํด์ ์์๋ณด๊ธฐ (0) | 2022.01.10 |
---|---|
[RxSwift] Subject ์์๋ณด๊ธฐ (0) | 2022.01.09 |
[RxSwift] DisposeBag ๊ณผ Operator (0) | 2022.01.06 |
[RxSwift] Observable, Operators ๊ทธ๋ฆฌ๊ณ subscribe (0) | 2022.01.05 |
[iOS/Swift] Custom Font ์ฌ์ฉํ๊ธฐ (0) | 2021.12.19 |
๋๊ธ