guard let url = URL(string: imageUrl) else { return nil }
guard let data = try? Data(contentsOf: url) else { return nil }
let image = UIImage(data: data)
imageView.image = image
DispatchQueue.global().async{
guard let url = URL(string: imageUrl) else { return nil }
guard let data = try? Data(contentsOf: url) else { return nil }
let image = UIImage(data: data)
DispatchQueue.main.async {
self.imageView.image = image
}
}
지금은 이미지만 가져오는 간단한 작업이라 코드가 간결해보이지 않지만 복잡한 작업을 할 때 유용하다고 한다.
rxswiftLoadImage(from: LARGER_IMAGE_URL)
.observeOn(MainScheduler.instance)
.subscribe({ result in
switch result {
case let .next(image):
self.imageView.image = image
case let .error(err):
print(err.localizedDescription)
case .completed:
break
}
}).disposed(by: disposeBag)
func rxswiftLoadImage(from imageUrl: String) -> Observable<UIImage?> {
return Observable.create { seal in
asyncLoadImage(from: imageUrl) { image in
seal.onNext(image)
seal.onCompleted()
}
return Disposables.create()
}
}
작업의 시작을 알림. subscribe은 Disposable을 반환한다.
func subscribe(_ on: @escaping (Event<UIImage?>) -> Void) -> Disposable
var disposable: Disposable?
let disposable = rxswiftLoadImage(from: LARGER_IMAGE_URL)
.observeOn(MainScheduler.instance)
.subscribe({ result in
switch result {
case let .next(image):
self.imageView.image = image
case let .error(err):
print(err.localizedDescription)
case .completed:
break
}
})
disposable?.dispose() // cancel
var disposable: Disposable?
var disposeBag: DisposeBag = DisposeBag()
// DisposeBag에 넣을 수 있는 방법
// 1. insert를 사용해 넣어준다.
let disposable = rxswiftLoadImage(from: LARGER_IMAGE_URL)
.observeOn(MainScheduler.instance)
.subscribe({ result in
switch result {
case let .next(image):
self.imageView.image = image
case let .error(err):
print(err.localizedDescription)
case .completed:
break
}
})
disposeBag.insert(disposable) // 해당 disposable을 넣어줌
// 2. disposed(by: )를 사용하여 1번보다 더 간편하게 넣어준다.
rxswiftLoadImage(from: LARGER_IMAGE_URL)
.observeOn(MainScheduler.instance)
.subscribe({ result in
switch result {
case let .next(image):
self.imageView.image = image
case let .error(err):
print(err.localizedDescription)
case .completed:
break
}
}).disposed(by: disposeBag) // <---
disposeBag = DisposeBag() //disposeBag을 새로 만들어주면 기존에 disposeBag에 있는 작업들이 다 dispose 된다.
Observable.just("Hello World")
.subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)
// Hello World 출력
Observable.just(["Hello", "World"])
.subscribe(onNext: { arr in
print(arr)
})
.disposed(by: disposeBag)
// ["Hello", "World"] 출력
Observable.from(["Hello", "World"])
.subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)
// ** 출력 **
// Hello
// World
Observable.from(["Hello", "World"])
.map { $0.count }
.subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)
// ** 출력 **
// 5
// 5
Observable.from([1, 2, 3])
.map { $0 * 10 }
.subscribe(onNext: { str in
print(str)
})
.disposed(by: disposeBag)
// ** 출력 **
// 10
// 20
// 30
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)
// ** 출력 **
// 2
// 4
// 6
// 8
// 10
Observable.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
.first()
.subscribe(onNext: { n in
print(n)
})
.disposed(by: disposeBag)
// ** 출력 **
// 1
Observable.from([1])
.single()
.subscribe(onNext: { n in
print(n)
})
.disposed(by: disposeBag)
// ** 출력 **
// 1
Observable.combineLatest( idField.rx.text.orEmpty
.map(checkEmailValid),
pwField.rx.text.orEmpty
.map(checkPasswordValid),
resultSelector: {s1, s2 in s1 && s2})
.subscribe(onNext: { b in
self.loginButton.isEnabled = b
}).disposed(by: disposeBag)
어느 Scheduler에서 실행시킬지 결정
순서에 영향을 받는다. 즉, 중간중간에 스케쥴러를 바꿔도 됨.
Observable.just("800x600")
.observeOn(ConcurrentDispatchQueueScheduler.init(qos: .default)) // ** 여기서부터 ConcurrentDispatchQueueScheduler에서 처리하겠다.
.map { $0.replacingOccurrences(of: "x", with: "/") }
.map { "https://picsum.photos/\($0)/?random" }
.map { URL(string: $0) }
.filter { $0 != nil }
.map { $0! }
.map { try Data(contentsOf: $0) }
.map { UIImage(data: $0) }
.observeOn(MainScheduler.instance) // ** UI관련 작업은 메인쓰레드에서 처리
.subscribe(onNext: { image in
self.imageView.image = image
})
.disposed(by: disposeBag)코드를 입력하세요
// 만약 observeOn에 순서를 바꾼다면?
Observable.just("800x600")
.map { $0.replacingOccurrences(of: "x", with: "/") }
.map { "https://picsum.photos/\($0)/?random" }
.map { URL(string: $0) }
.filter { $0 != nil }
.map { $0! }
.map { try Data(contentsOf: $0) }
// ** Data를 가져오는 부분보다 뒤에서 비동기처리를 하니 데이터를 가져오는 것 까진 메인쓰레드에서 동기 처리.
// ** 즉, 데이터를 받아올 때 까지 아무것도 못한다.
.observeOn(ConcurrentDispatchQueueScheduler.init(qos: .default))
.map { UIImage(data: $0) }
.observeOn(MainScheduler.instance) // ** UI관련 작업은 메인쓰레드에서 처리
.subscribe(onNext: { image in
self.imageView.image = image
})
.disposed(by: disposeBag)
observeOn과 달리 순서에 영향을 받지 않음. subscribe 쓰는 순간 부터 처리함.
Observable.just("800x600")
.map { $0.replacingOccurrences(of: "x", with: "/") }
.map { "https://picsum.photos/\($0)/?random" }
.map { URL(string: $0) }
.filter { $0 != nil }
.map { $0! }
.map { try Data(contentsOf: $0) }
.map { UIImage(data: $0) }
.subscribeOn(ConcurrentDispatchQueueScheduler.init(qos: .default))
.observeOn(MainScheduler.instance)
.subscribe(onNext: { image in
self.imageView.image = image
})
.disposed(by: disposeBag)
Observable.just(["Hello", "World"])
.subscribe{ event in
switch event {
case .next(let str):
print("next : \(str)")
break
case .error(let err):
print("error : \(err.localizedDescription)")
break
case .completed:
print("completed")
break
}
}.disposed(by: disposeBag)
// ** 출력 **
// next : Hello
// next : World
// completed
Observable.just(["Hello", "World"])
.single()
.subscribe{ event in
switch event {
case .next(let str):
print("next : \(str)")
break
case .error(let err):
print("error : \(err.localizedDescription)")
break
case .completed:
print("completed")
break
}
}.disposed(by: disposeBag)
// ** 출력 **
// next : Hello
// error: The operation couldn't be completd.
// single은 하나만 들어와야되는데 여러개가 들어오니 에러
Observable.just(["Hello", "World"])
.subscribe(onNext: { s in
print(s)
}, onError: { err in
print(err.localizedDescription)
}, onCompleted: {
print("completd")
}, onDisposed: {
print("disposed")
}).disposed(by: disposeBag)
// ** 출력 **
// next : Hello
// next : World
// completd
// disposed
말 그대로 외부에 영향을 주는 부분을 담당. subscribe와 do에서만 처리하자.
.subscribe(onNext: { image in
self.imageView.image = image // 외부에 있는 imageView의 image를 바꿀 때
})
.do(onNext: { image in
print(image?.size)
})
textField의 입력될 때마다 호출되는 부분을 Delegate를 사용하지 않고 RxCocoa를 활용하여 구현.
@IBOutlet var idField: UITextField!
// .rx : 비동기로 받겠다.
// orEmpty : .filter{ $0 != nil}.map{$0!} nil값을 체크
idField.rx.text.orEmpty
.map(checkEmailValid)
.subscribe(onNext: { b in
self.idValidView.isHidden = b
})
.disposed(by: disposeBag)
// 따로 분리해서 쓸 수도 있음
let idInputOb : Observable<String> = idField.rx.text.orEmpty.asObservable()
let idValidOb = idInputOb.map(checkEmailValid)
idValidOb.subscribe(onNext: { b in
self.idValid.onNext(b)
self.idValidView.isHidden = b
})
.disposed(by: disposeBag)
말로만 듣던 Rx 오늘 처음 공부해봤는데 생각보다 굉장히 유용한것 같다. gesture 부분을 rx로 간편하게 구현할 수 있는거 보고 감탄의 감탄을 👏🏻⠀전세계 사람들 다 코드 길게 치기 귀찮아서 오픈소스 열심히 만드나 보다. 감사합니다 ㅎㅎ 내일은 곰튀김님의 RxSwift 시즌2를 들어야겄다..