raywenderlich RxSwift Ch1~4
raywenderlich ์ RxSwift ์ฑ ์ ๊ธฐ๋ฐ์ผ๋ก ํ RxSwift ์ ๋ฆฌ ๋ฅผ ๋ณด๊ณ ๊ณต๋ถํ๊ณ ์๋ค. Ch1 ~ Ch3 ์ ๊ฒฝ์ฐ๋ ์์ ์ ๋ฆฌํ๋ ๊ณฐํ๊น๋์ ๊ฐ์์์ ๋ค์ Observable๊ณผ Subject๋ฅผ ์กฐ๊ธ ๋ ์ค์ฉ์ ์ธ ์์ ๋ก ์ดํด๋ณด๊ณ ๋ฌธ์ ๋ ํ์ด๋ณด๋ ์ ๋์ ๋ด์ฉ์ด์๋ค.
Ch4๋ Relay, Subject ๋ฅผ ์ด์ฉํ ์ด๋ฏธ์ง ์ฝ๋ผ์ฃผ ์ดํ๋ฆฌ์ผ์ด์ ์์ ์๋ค. UI๋ ์ด๋ฏธ ๋ค ์์ฑ๋ ๋ผ๋ ์์ RxSwift๋ฅผ ์น์ด์ Clear, Save, + ๋ฒํผ์ ๋ํ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ์ด์๋ค. ์์ ์ฝ๋๋ ์ฌ๊ธฐ์!
PhotosViewController (์ดํ PhotosVC), MainViewController (์ดํ MainVC)
์๋ ์ข์ํ๋ ๊ณต๋ถ ๋ฐฉ์์ธ ์ฝ๋ ์์๋๋ก ๋ฐ๋ผ๊ฐ๋ ๊ฒ ์ ๋ฆฌํ๊ธฐ์ ๊ฒฝ์ฐ๋ ์๊ธ์์ ๋๋ฌด ์ ํด๋์ จ๊ธฐ ๋๋ฌธ์, ์๋ก ์๊ฒ ๋ ํฌ์ธํธ๋ค์ ๋ํด์ ์ ๋ฆฌํด๋ณด๋ ค๊ณ ํ๋ค.
MainVC -> PhotosVC ๋ก ์ด๋ํ์ฌ PhotosVC ์์ ์ฌ์ง์ ๊ณจ๋ผ์ MainVC ์ ์ฌ์ง ๋ฐ์ดํฐ๋ฅผ ๋๊ฒจ์ค์ผํ๋ ์ด๋ฐ ๊ฒฝ์ฐ์๋, ๋ณดํต delegate ๋ฅผ ์ด์ฉํด์ ๋๊ฒจ์ฃผ๊ธฐ ๋ง๋ จ์ด๋ค. PhotosVC ์์๋ delegate ๋ฅผ ์ด์ฉํ์ฌ ์ฒ๋ฆฌ๋ฅผ ์์ํ๊ณ ์ค ์ฒ๋ฆฌ๋ MainVC ์ ํจ์์์ ํ๋ค.
ํ์ง๋ง, Rx์์๋ delegate ํจํด์ ์ฌ์ฉํ์ง ์๋๋ค.
Observable ๋ก ์์ชฝ VC์์ ๋ฐ์ดํฐ์ ํ๋ฆ์ ๊ตฌ๋ ํ๊ฒ ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
// MainVC
@IBAction func actionAdd() {
let photosVC = storyboard?.instantiateViewController(withIdentifier: "PhotosViewController") as! PhotosViewController
navigationController?.pushViewController(photosVC, animated: true)
// ๊ทธ ๋์ ์ด๋ ๊ฒ ์ด๋ํ VC์ Observable ์ ๊ตฌ๋
ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์จ๋ค.
photosVC.selectedPhotos
.subscribe(onNext: { [weak self] image in
guard let images = self?.images else { return }
images.accept(images.value + [image])
}, onDisposed: {
print("photo selection completed")
})
.disposed(by: disposeBag)
}
// PhotosVC
private var selectedPhotosSubject = PublishSubject<UIImage>()
// MainVC ์์ selectedPhotos์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ผ ์ผ์ด ์๊ธฐ ๋๋ฌธ์, ๋ฐ์ดํฐ์ ์์ ์ ๋ง๊ธฐ ์ํด Observable ์ ๊ตฌ๋
ํ๊ฒ ํ๋ค.
var selectedPhotos: Observable<UIImage> {
return selectedPhotosSubject.asObservable()
}
๋์ ์ฌ๊ธฐ์ ์ ๊ฒฝ์จ์ผ ํ๋ ๋ถ๋ถ์, MainVC ์์ selectedPhotos ๋ฅผ ๊ตฌ๋ ํ๊ณ ์๊ธฐ ๋๋ฌธ์, PhotosVC ๊ฐ ์์ด์ง๋ค๊ณ ํด์ selectedPhotosSubject ๊ฐ ๋ฉ๋ชจ๋ฆฌ์์ ํด์ ๋์ง ์๋๋ค๋ ์ ์ด๋ค.
// PhotosVC
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
selectedPhotosSubject.onCompleted()
}
viewWillDisappear ์์ ๋ช ์์ ์ผ๋ก Subject๋ฅผ ์ข ๋ฃ์์ผ์ ํด์ ํ์.
Custom Observable ์ฝ๋๋ฅผ (photo id ํ๋๋ง emit ํ๋) Single ์ ์ด์ฉํด์ refactoring ํด๋ณด์๋ค.
static func save(_ image: UIImage) -> Single<String> {
// return Observable.create({ observer in
return Single.create (subscribe: { observer in
var saveAssetId: String?
PHPhotoLibrary.shared().performChanges({
let request = PHAssetChangeRequest.creationRequestForAsset(from: image)
saveAssetId = request.placeholderForCreatedAsset?.localIdentifier
}, completionHandler: { success, error in
DispatchQueue.main.async {
if success, let id = saveAssetId {
// observer.onNext(id)
// observer.onCompleted()
observer(.success(id))
} else {
observer(.error(error ?? Errors.couldNotSavePhoto))
}
}
})
return Disposables.create()
})
}
}
RxSwift ์์๋ง ์ฐ์ด๋ ๋ด์ฉ์ ์๋์ง๋ง, ๋ถ๋ถ์ ๋ถ๋ฌ์์ผ ํ๋ ์์ ์ค nil ์ด ๋ ๊ฐ๋ฅ์ฑ์ด ์๋ ๊ฒ์ ๋ถ๋ฌ์์ ์ฌ์ฉํ๋ฉด guard ๋ฌธ ๋ค๋ก๋ ? ! ๋ฅผ ๋ถ์ผ ํ์๊ฐ ์์ด์ ํธํ๊ฒ ์ฌ์ฉํ ์ ์๋ค. ์ ๊ทน ํ์ฉํด์ผ๊ฒ ๋ค.
์ ๋ชจ๋ฅด๊ฒ ์ ๋๋ ํ๋ก์ ํธ๋ก ์ง์ ์ฝ๋๋ฅผ ์ณ๋ณด๋๊ฒ ์ต๊ณ ๋ผ๋ ์๊ฐ์ ๊ทธ๋ฅ ์์๋๋ก ์ญ ๋ฐ๋ผ์ ์ฝ๋๋ฅผ ํ๋ฒ ์น๊ณ ์ต๋ํ ํ๋ฆ์ ๊ธฐ์ตํด๋ณด๋ฉด์ ์กฐ๊ธ์ฉ ์นํ ํ๋ฉด์ ์ฝ๋๋ฅผ ๋ ์ณ๋ณด๋ ๋ฐฉ์์ผ๋ก ๊ณต๋ถ๋ฅผ ์งํํ๋ค. ์กฐ๊ธ์ด๋๋ง Observable ์ ์ฌ์ฉํด์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ ์์ ์ตํ ๊ฒ ๊ฐ๋ค.
์ด๋ ์ํฉ์์ ์ด๋ค ๊ฒ์ ์จ์ผํ๋์ง๋ ์์ง ๊ฐ์ด ์ ์์ค์ง๋ง, ๊ฐ์ ์๋ฃ Challenge ์ ์๋ ๊ฒ์ฒ๋ผ Observable๋ก ๋ง๋ค๊ณ Single๋ก ๋ฆฌํฉํ ๋ง ํ๋ ๋ฐฉ์๋ ์ข์ ๋ฐฉ์์ด๋๊น!!! ์ผ๋จ ์ต๋ํ ์ต์ํด์ง์.