Observable을 감싸고 있는 구조체임
.asObservable()
사용시 다시 기본 Observable로 돌아갈 수 있음
단 하나의 요소
나 error
만 방출하는 Observable의 변형인 Traits중 하나이다.
언제 사용하냐?
보통 HTTP 요청의 결과로 response와 error를 딱 리턴하므로 많이 쓰임
특징
.asSingle()
사용시 Single로 변환 가능func fetch() -> Single<[Project]> {
var projects = [Project]()
return Single.create { single in
self.realm.objects(ProjectRealm.self).forEach { projectRealm in
let project = Project(projectRealm: projectRealm)
projects.append(project)
}
single(.success(projects))
return Disposables.create()
}
}
func getRepo(_ repo: String) -> Single<[String: Any]> {
return Single<[String: Any]>.create { single in
let task = URLSession.shared.dataTask(with: URL(string: "https://api.github.com/repos/\(repo)")!) { data, _, error in
if let error = error {
single(.error(error))
return
}
guard let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves),
let result = json as? [String: Any] else {
single(.error(DataError.cantParseJSON))
return
}
single(.success(result))
}
task.resume()
return Disposables.create { task.cancel() }
}
}
getRepo("ReactiveX/RxSwift")
.subscribe { event in
switch event {
case .success(let json):
print("JSON: ", json)
case .error(let error):
print("Error: ", error)
}
}
.disposed(by: disposeBag)
completed
나 error
만 방출하는 Observable의 변형인 Traits중 하나이다.
ex) Firebase에 데이터를 업데이트하고 업데이트 완료했다고 completable로 알려주면
구독하고 있는 곳에서 데이터 업데이트가 되고 나서 수행해야할 동작을 수행할 수 있음
Observable.create하는거랑 같은 방법이다.
똑같이 create의 리턴 타입이 Disposable이라서 return
Disposables.create() 해야함
//Completable.create
func delete(_ project: Project) -> Completable {
return Completable.create { completable in
self.dataBase
.collection("users")
.document(project.id.description)
.delete()
completable(.completed)
return Disposables.create()
}
}
//Completable 사용할 때는 subscribe해서 사용
Completable.zip(
remoteDataSource.delete(project),
localDataSource.delete(project)
).subscribe(onCompleted: { [self] in
var currentProjects = projects.value
if let row = currentProjects.firstIndex(where: { $0.id == project.id }) {
currentProjects.remove(at: row)
}
projects.accept(currentProjects)
}).disposed(by: disposeBag)
local, remote datasource의 delete 메서드를 실행하는 곳(repo)에서 Completable.zip으로 둘을 묶어서 구독하여, 두 작업 모두가 끝난 후에 바로 모델 업데이트할 수 있게 구현한 예시이다.
Completable들이 배열에 담긴: [Completable]을 하나의 Completable 형태로 바꿔주는 메서드 zip
1st. [Project] → [Completable]
배열의 뎁스는 유지하면서 filter의 조건으로 localProject, remoteProject 둘다를 사용하고 싶어서
이렇게 flatMap과 filter을 겹쳐서 사용함
let sameIDCompletable: [Project] = intersectingRemoteProjects.flatMap { remoteProject in
intersectingLocalProjects.filter { localProject in
localProject.updatedAt > remoteProject.updatedAt
}
}
여기서 .map 를 해서 배열 안의 Project 타입을 Completable로 바꾼 것임
self.remoteDataSource.update($0)의 리턴값이 Completable
let sameIDCompletable: [Completable] =
intersectingRemoteProjects.flatMap { remoteProject in
intersectingLocalProjects.filter { localProject in
localProject.updatedAt > remoteProject.updatedAt
}.map {
return self.remoteDataSource.update($0)
}
}
let sameIDCompletable: [[Completable]] =
intersectingRemoteProjects.map { remoteProject in
intersectingLocalProjects.filter { localProject in
localProject.updatedAt > remoteProject.updatedAt
}.map {
return self.remoteDataSource.update($0)
}
}
2nd. [Completable] → Completable
Completable.zip을 사용하여 최종적으로 [Completable] 타입에서 Completable 타입으로 변경해줌
let sameIDCompletable = Completable.zip(
intersectingRemoteProjects.flatMap { remoteProject in
intersectingLocalProjects.filter { localProject in
localProject.updatedAt > remoteProject.updatedAt
}.map {
return self.remoteDataSource.update($0)
}
}
)
https://github.com/ReactiveX/RxSwift/blob/main/Documentation/Traits.md