Dealing With Resources in RxSwift
The Observable.using(_:observableFactory:)
function is designed to tie the lifetime of a resource to the subscription of an Observable. In order to use it, you provide a closure that creates the resource and another closure that returns the Observable that will be tied to the resource. Then anytime the Observable returned by using
is subscribed to, the operator will call both closures in order to create the resource and return an Observable, bound to the one from the second function. When the Observable is disposed, the using
operator will call dispose()
on the resource and then deallocate it.
Making a Resource
All this is pretty abstract though. What does it look like in practice? I recently was working on a screen that played audio files. One way to do that is to use the AVAudioPlayer
class; a resource.
final class AudioSession: Disposable {
let audioPlayer: AVAudioPlayer
init(url: URL) throws {
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.play()
}
func dispose() {
audioPlayer.stop()
}
}
When the above resource is created, it will make the audio player and start playing. When the resource is disposed, it will stop the audio player. Now we can use it in the using
operator like so:
let session = Observable.using({
try AudioSession(url: url)
},
observableFactory: { audioSession in
Observable<Never>.never()
})
When you subscribe to session
it will create the AudioSession resource, and thus start playing the sound file. When you dispose that subscription, it will stop playing the sound file and delete the audio player object.
Getting Information Out of a Resource
As written, the above observable doesn’t emit anything. Its only reason for existence is to create and dispose the resource, but it can emit information about the observable. For example, you could have it emit the current play time:
return Observable<Int>.interval(
.milliseconds(100),
scheduler: MainScheduler.instance
)
.flatMap { _ in
Observable.just(audioSession.audioPlayer.currentTime)
}
Putting the above code into the observableFactory closure will let you know something about what’s going on with the resource. More information can be added as well.
Sending Information Into a Resource
You can also send information into the resource using an Observable. For example toggling from the play/pause state.
If you give your resource a dispose bag, you can add yet more to that closure:
togglePlay // an Observable<Void>
.subscribe(onNext: audioSession.audioPlayer.toggle)
.disposed(by: audioSession.disposeBag)
(The toggle()
function is an extension I wrote to toggle between play and pause.)
Other Uses for using
So any time you need to create and release resources in your Rx code, the using
operator is there to help you. However, it can be used for even more. Anytime you want to call some code during subscription and call other code during dispose, the using
operator is your friend.
One of the most interesting ideas I’ve had for the operator is when I treated a UIViewController as a resource. Resource creation makes the view controller and presents it, and disposing the resource dismisses the view controller.
Examples
I have created a generic Resource
type that makes it easier to wrap resources along with several examples. If you make resources that you would like to share, by all means send a pull request and I’d love to add it.
Check out my RxResource repository for more.