[RxSwift] 7. FlatMap?
FlatMap?
프로젝트내에 이미지 업로드 로직을 RxSwift로 교체하는 과정에서 flatMap연산자를 활용 하게 되어
한번 내용을 정리해 보려고 한다
What is flatMap
- 새로운 시퀀스를 반환해야할때 사용하는 연산자이다
- flatMap은 블록 내에서 Observable을 리턴해야 한다
- flatMap 안에서 API를 호출하여 응답값을 Observable로 리턴하여 사용한다
- 스트림 안에서 throw가 하나라도 방출되면, 해당 스트림은 dispose되므로
사용자가 계속 재시도 할 수 있는 이벤트 처리에는 부적합하다
example Code
1. 이미지를 업로드할 s3 url 호출 함수
private let mediaProvider = APIService.shared.mediaAPI // media관련 network MoyaProvider
private func getPath(_ type: MediaType) -> Observable<MediaResponse> {
let path = mediaProvider.rx
.request(.getMediaPath(type: type)) // getMediaPath를 통해 주소를 받음
.filterSuccessfulStatusCodes()
.map(MediaResponse.self) // path, url 로 나뉘어진 model
return path.asObservable()
}
2. 이미지리스트를 받아 업로드 로직을 실행할 함수 생성
func uploadImages(_ images: [UIImage]) -> Observable<(Int, String?)> {
return Observable.from(images)
.flatMap { image -> Observable<(UIImage, MediaResponse)> in
Observable.zip(Observable.just(image), self.getPath(.image))
// zip을 통해 image와 mediaResponse를 하나의 observable로 리턴시켜줌
}
.flatMap { image, mediaResponse -> Observable<(Moya.Response, String)> in
let uploadURL = URL(string: mediaResponse.url)!
let uploadResponse = self.mediaProvider.rx.request(.uploadImage(image, url: uploadURL))
// mediaProvider를 통해 uploadResponse를 single형태로 리턴받는다
return Observable.zip(
uploadResponse.asObservable(), // single을 observable로 변환시켜줌
Observable.just(mediaResponse.path)
)
}
.enumerated() // index를 반환시키기 위한 연산자
.map { index, response -> (Int, String?) in
guard response.0.response?.statusCode ?? 500 == 200 else {
return (index, nil) // 오류처리를 위한 nil 반환
}
return (index, response.1)
}
}
3. 이미지 Array를 파라미터로 전달할 업로드 함수 생성
private var imagePathList: [String] = [] //생성된 imagePath값을 저장할 Array
func upload(_ images: [UIImage]) {
MediaUploadManager().uploadImages(images).subscribe(onNext: { [weak self] index, path in
guard let self = self else { return }
guard let path = path else { // 네트워크 에러시 path값이 방출되지 않으므로 오류처리
print("uploadImage_error: errorIndex = \(index)")
self.imagePathList.removeAll()
return
}
self.imagePathList.append(path) //시퀀스가 진행됨에 따라 image의 path값을 저장해준다
if index == images.count - 1 {
// index와 업로드할 이미지갯수 - 1 이 같아지면 피드에 이미지 path어레이를 담아 업로드한다
self.uploadFeed(imagePathList: self.imagePathList) // 피드 업로드 로직 실행
}
})
.disposed(by: disposeBag)
}
4. 업로드 로직 호출하여 사용
private var selectedImages: [UIImage] = [] // 선택한 이미지가 담길 Array
@objc func didTapDoneButton(_ sender: UIButton) {
upload(selectedImages)
}