adam tecle

sharing video with UIActivityController and rxswift

I use RxSwift, RxJS, and RxJava a lot in my day to day work, and I’ve developed a deep appreciation for the abstractions that ReactiveX provides. The power of reactive programming is to be able to concisely and simply express logic that relies on many asynchronous events – which is often the case in mobile development. Without Rx, you can find yourself in callback hell pretty frequently. Here are some examples of situations where I think Rx really shines:

I definitely would not call myself an Rx evangelist (there’s a steep learning curve and sometimes debugging can be a huge PITA) but I’ve found it to make things easier in general.

I wanted to share a use case that I thought was kinda neat. Imagine you have a video sharing iOS app and you want to build a feature that allows users to save a video to their camera roll. The user should be able to tap a share button to bring up a UIActivityController, and there should be button that lets the user download the video to their camera roll. So, our main task is this - download the video to get a local file URL, so we can set that local file URL in the activityItems parameter of the UIActivityController init method.

Here’s how that code could look with RxSwift. (In order to keep this code sample contained, I’ve consolidated all the logic in one place and made it a bit pseudo-code-y)


func setupUI() {

    /// Observable<URL> where URL is a remote video URL
    shareButtonTaps
        .map { try? Data(contentsOf: $0) }
        .filterNil()
        /// write is an extension on `Data` (detailed below)
        /// It writes to disk and returns the file URL as a `Single`
        .flatMap { $0.write(to: "temp.mov").asObservable() }
        .subscribe(onNext: { fileURL in
          let activityViewController = UIActivityViewController(
            activityItems: [fileURL],
            applicationActivities: nil
          )

          self?.present(activityViewController, animated: true)
        }).disposed(by: disposeBag)
}

And here’s the code for that func write(to path: String) method

extension Data {

    enum Error: Swift.Error {
        case writeFailed(Swift.Error?)
    }

    func write(to path: String) -> Single<URL> {
        return Single.create { observer in
            let filePath = NSTemporaryDirectory() + path
            let tempURL = URL(fileURLWithPath: filePath)

            DispatchQueue.global(qos: .background).async {
                do {
                    try self.write(to: tempURL, options: [.atomic])
                    if FileManager.default.fileExists(atPath: filePath) {
                        observer(.success(tempURL))
                    } else {
                        observer(.error(Data.Error.writeFailed(nil)))
                    }
                } catch {
                    observer(.error(Data.Error.writeFailed(error)))
                }
            }

            return Disposables.create()
        }
    }
}

A few lines of code later and we have a video sharing feature! The main thing that I wanted to illustrate here is that done right, Rx makes async code super concise and readable – and a task like writing to disk is a great use case to show off its power. Also, judicious use of extensions that Rx-ify some behavior can make a huge difference in avoiding long, convoluted chains.


Hi internet guest, I’m Adam. I’m a software engineer based in Brooklyn, NY. I specialize in iOS development, but I have some experience with Android and across the stack with a variety of languages and technologies.

Some things I’ve made

Hire me

I’m available for freelance work! I love collaborating with teams and individuals that are building great products.

Don’t hesitate to get in touch.