RxSwift: ํ์ต ์ ๋ฆฌ
์ต์ ๋ฒ ํจํด
- 1:N ๊ด๊ณ๋ก ์ด๋ฃจ์ด์ง, ๊ด์ฐฐ ํจํด
- ๊ฐ์ฒด๋ฅผ ๊ตฌ๋
ํ๋ฉด ์ต์ ๋ฒ๊ฐ ์ํ๋ฅผ ์๋ ค์ค
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ
- 1๊ธ ๊ฐ์ฒด
- ๋ณ์๋ ์์์ ์ ์ฅ์ ํ ์ ์์ด์ผ ํจ
- ํจ์์์
return
ํ ์ ์์ด์ผ ํจ - ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํ ์ ์์ด์ผ ํจ
- ์์ ํจ์
-
/* ํ์จ ๊ณ์ฐํด์ฃผ๋ ํ๋ก๊ทธ๋จ ์ด ์ฝ๋๋ ์์ ํจ์๋ผ๊ณ ํ ์ ์๋ค. → ์ ์ญ ์ํ์ ์์กด = ์ฐธ์กฐ ํฌ๋ช ์ฑ์ด ์๋ค */ var rate = 1120 func krw(usd: Int) -> Int { return usd * rate } krw(usd: 2) // 2240 krw(usd: 3) // 3360 rate = 1130 krw(usd: 2) // 2260 krw(usd: 3) // 3390
ReactiveX
- ๊ด์ฐฐ ๊ฐ๋ฅํ ์คํธ๋ฆผ์ ์ด์ฉํ ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ API
- Reactive eXtension
RxSwift
- Observable
- Rx์ ๊ธฐ๋ณธ์ด ๋๋ ์คํธ๋ฆผ์ ๊ฐ์ง๊ณ ์๋ ๊ธฐ๋ณธ ์ํ์ค
- ํ๋ ๋๋ ์ฐ์๋ ํญ๋ชฉ์ ๋ฐฐ์ถ
- ๊ตฌ๋
ํ next, error, completed๋ก ์ฒ๋ฆฌ
- next ์คํธ๋ฆผ
- ์ฐ์๋ ๊ฐ๋ค์ ๋ฐฐ์ถํ๊ณ ์ต์ ๋ฒ๋ next ์คํธ๋ฆผ์ ๊ด์ฐฐ ๋ฐ ๊ตฌ๋ ํด์ ์ํ๋ ํ๋์ ํจ
- error ์คํธ๋ฆผ
- ๊ฐ์ ๋ฐฐ์ถํ๋ค๊ฐ ์๋ฌ๊ฐ ์๊ธฐ๋ฉด error๋ฅผ ๋ฐฐ์ถํ ๋ค์ ํด๋น Observable์ ์คํธ๋ฆผ์ ๋ฉ์ถค
- complete
- Observable์ด ๋ชจ๋ ๊ฐ์ ๋ค ๋ฐฐ์ถํ๋ฉด ์ด ์ํ(?)๊ฐ ๋จ
- error๊ฐ ๋ฐ์ํ๋ฉด complete์ ๋ฐ์ํ์ง ์์!
- next ์คํธ๋ฆผ
- Operator
- Observable ์์์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ์ํ ๋ชจ๋ ์ฐ์ฐ๋ค
- map, filter, merge ๋ฑ ์์ญ๊ฐ์ง์ ์ฐ์ฐ์ด ์์
- ๋ฉ์๋ ์ฒด์ด๋์ผ๋ก ๋ง์ด ์งํํจ
- Single
- Observable ๊ฐ์ด N๋ฒ์ด ์๋, ๋จ๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ์ํ์ค
- ๊ตฌ๋ ํ success, error ๋ ๊ฐ์ง๋ก ์ฒ๋ฆฌ
- ํ๋ฒ๋ง ์ค๊ณ , ํ๋ฒ์ ๋๋จ
- Subject
- ์ฌ๋ฌ๊ฐ์ Observer๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ด์ฐฐํด์ผ ํ ๋ ์ฌ์ฉํ๋ ๊ฐ์ฒด
- Observer ์ Observable ์ฌ์ด์ ๋ธ๋ฆฟ์ง ์ญํ
- ์ฌ๋ฌ ๊ฐ์ Observer๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ด์ฐฐํด์ผํ๋ ๊ฒฝ์ฐ ์์ฃผ ์ฐ์
๊ตณ์ด?
- ์ ๋น์ฉ์ ๋ค์ฌ๊ฐ๋ฉด์?
- ์ ์ ๋์๊ฐ๋ ์ฝ๋๋ฅผ ๋ฐ๊ฟ๊ฐ๋ฉด์?
- ์ ๊ผญ ๋ ๋์ด๋ ์๋ RxSwift๋ฅผ?
์ด์
- ๊ฐ๊ฒฐํด์ง๋ ์ฝ๋
- ์กฐ๊ธ ๋ ๊ธธ์ด์ง๋๋ผ๋, ๊ฐ๋จํ๋ฉด์๋ ๊น๋ํด์ง๋ ์ฝ๋
- ๊ฐํธํ ๋น๋๊ธฐ ๊ด๋ฆฌ
RxSwift ์์๋ณด๊ธฐ 1 ~ 3 ๊ธฐ๋ก
์ ์ํ ๋ฐฐ์ด์ ์๋ฆฌ๋จผํธ๋ฅผ ์ฒดํฌํ๋ ํจ์ (RxSwift)
// items ๋ผ๋ ์ ์ ํ์
์ ๋ฐฐ์ด์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ -> ์ ์ ์ ๋ค๋ฆญ ํ์
์ Observable ๊ฐ์ฒด๋ฅผ ๋ฆฌํด
func checkArrayObservable(items: [Int]) -> Observable<Int> {
return Observable<Int>.create { observer -> Disposable in
for item in items {
if item == 0 {
observer.onError(NSError(domain: "Error: value is zero.", code: 0, userInfo: nil)) // 0์ด๋ฉด ์๋ฌ๋ฅผ ํ๋ ค์ค๋ค
break
}
observer.onNext(item) // 0์ด ์๋๋ฉด ๊ฐ ์๋ฆฌ๋จผํธ๋ฅผ next๋ก ํ๋ ค์ค๋ค
sleep(1)
}
observer.onCompleted() // ๋ชจ๋ ์ํ๊ฐ ๋๋๋ฉด completed ๋์๋ค๋ ๊ฑธ ์๋ฆผ
return Disposables.create()
}
}
Subscribe, Dispose
Subscribe
Observable์ stream์ ๊ด์ฐฐํ๊ณ ๊ตฌ๋ ํด์ ๋ฐ๋ ์ญํ
→ Disposable
์ด๋ผ๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค!
์์ ์ฝ๋
// interval: n์ด๋ง๋ค ์ ์ ํ์
์ ์คํธ๋ฆผ์ด ๋ฐฉ์ถ๋จ
Observable<Int>.interval(RxTimeInterval.seconds(1), scheduler: MainScheduler.instance)
.take(10) // parameter ๋งํผ์ ์คํธ๋ฆผ ํ์ฉ
.subscribe(onNext: { value in
print(value)
}, onError: { error in
print(error)
}, onCompleted: {
print("onCompleted")
}, onDisposed: {
print("onDisposed")
})
// ์์ธ ์ํฉ ๋ฐ์
// ์นด์ดํ
์ด ๋๋๊ธฐ ์ ์ ๋ทฐ ์ปจํธ๋กค๋ฌ๋ฅผ ํด์ ํด ๋ฒ๋ฆฐ๋ค๋ฉด?
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
UIApplication.shared.keyWindow?.rootViewController = nil
} // ์ฑ์ด ์ฃฝ์์์๋ ๋ถ๊ตฌํ๊ณ ๋๊น์ง ์นด์ดํธ๊ฐ ์งํ๋จ
์ด๊ฒ์ ํด๊ฒฐํด๋ณด์.
Disposable
disposable: ์ฒ๋ถํ ์ ์๋, ์ฌ์ฉ ํ ๋ฒ๋ฆด ์ ์๋
public protocol Disposable {
/// Dispose resource.
func dispose()
}
// interval: n์ด๋ง๋ค ์ ์ ํ์
์ ์คํธ๋ฆผ์ด ๋ฐฉ์ถ๋จ
let disposable = Observable<Int>.interval(RxTimeInterval.seconds(1), scheduler: MainScheduler.instance)
.take(10) // parameter ๋งํผ์ ์คํธ๋ฆผ ํ์ฉ
.subscribe(onNext: { value in
print(value)
}, onError: { error in
print(error)
}, onCompleted: {
print("onCompleted")
}, onDisposed: {
print("onDisposed")
})
// ์์ธ ์ํฉ ๋ฐ์
// ์นด์ดํ
์ด ๋๋๊ธฐ ์ ์ ๋ทฐ ์ปจํธ๋กค๋ฌ๋ฅผ ํด์ ํด ๋ฒ๋ฆฐ๋ค๋ฉด?
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
disposable.dispose()
} // ๋ฐํ๋ disposable ๊ฐ์ฒด๋ฅผ ๊ฐ์ง๊ณ ์๋ค๊ฐ ๋ทฐ ์ปจํธ๋กค๋ฌ๊ฐ deinit ๋ ๋ dispose ์คํ
๋ง์ฝ ๊ตฌ๋ ๋ฐ๋ Observable์ด ์ฌ๋ฌ๊ฐ๋ผ๋ฉด?
→ DisposeBag์ ์ฌ์ฉํ์
extension Disposable {
/// Adds `self` to `bag`
///
/// - parameter bag: `DisposeBag` to add `self` to.
public func disposed(by bag: DisposeBag) {
bag.insert(self)
}
}
ํ๋ผ๋ฏธํฐ๋ก ๋ค์ด์ค๋ DisposeBag ๊ฐ์ฒด์ ์์ ์ insert ํ๋ค.
๋ชจ๋ disposable ๊ฐ์ฒด์ disposed๋ฅผ ํด์ฃผ๋ฉด ํด๋น ํ๋ผ๋ฏธํฐ์ธ disposeBag์ ๋ฑ๋ก๋๊ณ disposeBag ๊ฐ์ฒด๊ฐ ํด์ ๋๋ฉด์ ๋ฑ๋ก๋ ๋ชจ๋ disposable์ด ๋ค ๊ฐ์ด dispose ๋์ด๋ฒ๋ฆฐ๋ค.
/// var๋ก ์ ์ธํ ์ด์ : disposeBag์ด ํด์ ๋๋ฉด ๋ชจ๋ disposable์ด dispose๋๋ ์๋ฆฌ๋ฅผ ๊ฐ๋ฐ๋์ค ์ฌ์ฉํ ์ ์์
/// subscribe ์ค์ด๋ disposable์ ์ด๊ธฐํํ๊ณ ์ถ์ผ๋ฉด ์๋ก์ด DisposeBag ๊ฐ์ฒด๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ๋!
var disposeBag = DisposeBag()
// interval: n์ด๋ง๋ค ์ ์ ํ์
์ ์คํธ๋ฆผ์ด ๋ฐฉ์ถ๋จ
Observable<Int>.interval(RxTimeInterval.seconds(1), scheduler: MainScheduler.instance)
.take(10) // parameter ๋งํผ์ ์คํธ๋ฆผ ํ์ฉ
.subscribe(onNext: { value in
print(value)
}, onError: { error in
print(error)
}, onCompleted: {
print("onCompleted")
}, onDisposed: {
print("onDisposed")
})
.disposed(by: disposeBag) // **← ์ฌ๊ธฐ ๋ถ๋ถ!**
RxSwift In 4Hours
๋๊ธฐ/๋น๋๊ธฐ ์ฒ๋ฆฌ
@IBAction func onLoadAsync(_ sender: Any) {
// TODO: async
DispatchQueue.global().async { [weak self] in
guard let self = self else { return }
let image = self.loadImage(from: self.IMAGE_URL)
DispatchQueue.main.async {
self.imageView.image = image
}
}
}
private func loadImage(from imageUrl: String) -> UIImage? {
guard let url = URL(string: imageUrl) else { return nil }
guard let data = try? Data(contentsOf: url) else { return nil }
let image = UIImage(data: data)
return image
}
just
Observable.just("Hello World")
.subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)
just
์ ์ธ์๋ก ๋ฃ์ด์ค "Hello World"
๊ฐ str
๋ก ๋์ด๊ฐ๋ค.
Observable.create()
๋ฅผ ๋์ ํด์ฃผ๋ ๋ฉ์๋- ๋ฐฐ์ด์ ๋ฃ์ผ๋ฉด ๋ฐฐ์ด์ ์ฒ๋ฆฌํจ(์๋ฌด๊ฑฐ๋ ๋ฃ์ด๋ ๋๋จ ์๋ฆฌ)
from
Observable.from(["RxSwift", "In", "4", "Hours"])
.subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)
from
์ ๋ฐฐ์ด์ด ๋ค์ด๊ฐ๋ฉด ํ๋์ฉ ์คํํ๋ค.
map
Observable.just("Hello")
.map { str in "\(str) RxSwift" }
.subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)
์คํ์์
just(Hello)
map { str in "\(str) RxSwift" }
onNext: { str in print(str) }
- ๊ฒฐ๊ณผ: Hello RxSwift
Observable.just(["Hello", "World"])
.map { str in "\(str) RxSwift" }
.subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)
์คํ์์
just(["Hello", "World"])
map { str in "\(str) RxSwift" }
onNext: { str in print(str) }
- ๊ฒฐ๊ณผ: ["Hello", "World"] RxSwift
Observable.from(["Hello", "World"])
.map { str in "\(str) RxSwift" }
.subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)
์คํ์์
just(["Hello", "World"])
→ ์ฒซ๋ฒ์งธ ์์map { str in "\(str) RxSwift" }
onNext: { str in print(str) }
- ๊ฒฐ๊ณผ: Hello RxSwift
just(["Hello", "World"])
→ ๋๋ฒ์งธ ์์map { str in "\(str) RxSwift" }
onNext: { str in print(str) }
- ๊ฒฐ๊ณผ: World RxSwift
filter
Observable.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
.filter { $0 % 2 == 0 }
.subscribe(onNext: { n in
print(n)
})
.disposed(by: disposeBag)
true
์ผ ๋๋ง ๋ฐ์ดํฐ๊ฐ ๋ฐ์ผ๋ก ๋ด๋ ค๊ฐ
์ด๋ฏธ์ง๋ฅผ ๋ค์ด๋ฐ๋ ๊ณผ์
์์์ ๋ฐฐ์ด ๊ฑธ๋ก ํด์ํด๋ณด์.
Observable.just("800x600")
.map { $0.replacingOccurrences(of: "x", with: "/") } // → "800/600"
.map { "https://picsum.photos/\($0)/?random" } // → "https://picsum.photos/800/600/?random"
.map { URL(string: $0) } // → URL ๊ฐ์ฒด๋ก ๋ณํ
.filter { $0 != nil } // → URL ๊ฐ์ฒด๊ฐ nil์ด๋ฉด ๋ด๋ ค๊ฐ์ง ์์
.map { $0! } // → ๋ด๋ ค์ค๋ฉด ๊ฐ์ ์ธ๋ฉํ (์์์ ์ฒดํฌํ์ผ๋๊น ๊ฐ์ ์ธ๋ํํด๋ ๋จ)
.map { try Data(contentsOf: $0) } // → URL์ ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ด
.map { UIImage(data: $0) } // → UIImage?
.subscribe(onNext: { image in
self.imageView.image = image
})
.disposed(by: disposeBag)
observeOn
Observable.just("800x600")
.observeOn(ConcurrentDispatchQueueScheduler.init(qos: .default)) // ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋๋ฆด ๋
.map { $0.replacingOccurrences(of: "x", with: "/") } // → "800/600"
.map { "https://picsum.photos/\($0)/?random" } // → "https://picsum.photos/800/600/?random"
.map { URL(string: $0) } // → URL ๊ฐ์ฒด๋ก ๋ณํ
.filter { $0 != nil } // → URL ๊ฐ์ฒด๊ฐ nil์ด๋ฉด ๋ด๋ ค๊ฐ์ง ์์
.map { $0! } // → ๋ด๋ ค์ค๋ฉด ๊ฐ์ ์ธ๋ฉํ (์์์ ์ฒดํฌํ์ผ๋๊น ๊ฐ์ ์ธ๋ํํด๋ ๋จ)
.map { try Data(contentsOf: $0) } // → URL์ ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ด
.map { UIImage(data: $0) } // → UIImage?
.observeOn(MainScheduler.instance) // ๋ฉ์ธ ์ค๋ ๋์์ ๋๋ฆฌ๋๊ฑฐ
.subscribe(onNext: { image in
self.imageView.image = image
})
.disposed(by: disposeBag)
observeOn
์ ํด์ค ๋ค์์ค ๋ถํฐ ์ํฅ์ด ๋ฏธ์น๋ค.
subscribeOn
subscribe
๊ฐ ์คํ๋๋ ์์ ๋ถํฐ ๋์ํ๋ค. ์๋ฌด ์์น์๋ ๋ฃ์ด๋ ์๊ด์์!
side-effect
side-effect๋ฅผ ํ์ฉํ๋ ๊ฑด ๋ ๊ฐ๊ฐ ์๋ค.
- do
- subscribe
์ฐธ๊ณ ์๋ฃ
[RxSwift] RxSwift ์์ํ๊ธฐ
RxSwift ์์๋ณด๊ธฐ(ReactiveX ์ ๋ํด์) - 01 → ์๋ฆฌ์ฆ ์ฐธ๊ณ !
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote