If you are a (great) mobile developer, you likely run a bunch of operations/instructions in the background. Unfortunately, you might have some chained callbacks. By chained callbacks I mean a situation where you run an asynchronous operation that depends on another one before starting. For example, you need to fetch some data from one server before fetching another one to obtain the final outcome.

Even if some great libraries such as RxJava or RxSwift help you limiting these chains, you might still have some around.

To prevent this spaghetti code (also known as “pyramid of doom”), I created this simple helper I named Chain.

🤖 Your attention please 🤖: this object is not related to the Chain of Responsibility pattern. It just helps you flattening your codebase.

It’s really straightforward to use it. My original codebase looks like this:

fetchFirstServer() { firstData in
    fetchSecondServer(firstData) { finalData in
        let outcome = finalData.map { $0.name }

        completionHandler(data)
    }
}

And I am transforming it into:

Chain.startWith { next in
    fetchFirstServer() { data in
        next(data)
    }
}.then { data, next in
    fetchSecondServer(data) { finalData in
        next(finalData)
    }
}.endWith { rawData in
    let data = rawData.map { $0.name }
    completionHandler(data)
}

Elegant, isn’t it?

If it was not obvious, data represents the data set passed from the previous block, next is the function you use for triggering the trailing block.

You can find both Swift and Java snippets below. Action classes are the same objects as the ones you have in the Rx suite.

public class Chain<T> {
    private var runCommand: (Void -> Void)?
    private var nextCommand: (T? -> Void)?

    private init() { }

    private func next(t: T?) {
        self.nextCommand?(t)
    }

    public func then<U>(command: (T?, U? -> Void) -> Void) -> Chain<U> {
        let e = Chain<U>()

        nextCommand = { command($0, e.next) }
        e.runCommand = runCommand

        return e
    }

    public func endWith(command: T? -> Void) {
        nextCommand = command
        runCommand!()
    }

    public static func startWith<U>(command: (U? -> Void) -> Void) -> Chain<U> {
        let e = Chain<U>()

        e.runCommand = { command(e.next) }

        return e
    }
}

public class Chain<T> {
    private Action0   runCommand;
    private Action<T> nextCommand;

    private Chain() {
    }

    public <U> Chain<U> then(final Action2<T, Action<U>> command) {
        final Chain<U> e = new Chain<>();

        nextCommand = new Action<T>() {
            @Override
            public void run(T t) {
                command.run(t, e.nextCommand);
            }
        };
        e.runCommand = runCommand;

        return e;
    }

    public void endWith(Action<T> command) {
        nextCommand = command;
        runCommand.run();
    }

    public static <U> Chain<U> startWith(final Action<Action<U>> command) {
        final Chain<U> e = new Chain<>();

        e.runCommand = new Action0() {
            @Override
            public void run() {
                command.run(e.nextCommand);
            }
        };

        return e;
    }
}