Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save andrewsardone/9022130 to your computer and use it in GitHub Desktop.
Save andrewsardone/9022130 to your computer and use it in GitHub Desktop.

Here's a quick attempt at trying to build off of some quick tweets:

@cdzombak @vitalekj @a_j_r nice! Another underlying point is that the delegate example is easy. I've seen experienced devs struggle w/ it.

— Andrew Sardone (@andrewa2) February 15, 2014
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

@cdzombak @vitalekj @a_j_r I think many devs' love of callbacks helps demonstrate this

— Andrew Sardone (@andrewa2) February 15, 2014
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

I think the original post takes for granted familiarity of Cocoa's delegate pattern with being better code. In my limited experience, established and knowledgeable developers have struggled to grasp's Cocoa's routine usage of this pattern, especially within UIs. It takes a little bit of reasoning to:

  1. Jump to the definition of -[RSOWebServices fetchQuestionsWithDelegate:] and view the required & optional messages defined in the protocol, processing which ones are needed for the task at hand.
  2. For implementation and later maintenance, jump to a method implementation elsewhere in the delegate object, grasping what belongs here and what doesn't, and fighting off intermingling of concerns.

As a Cocoa developer, you do this a lot, practically to the point of muscle memory. But I think with the introduction of blocks in iOS 4, we saw many APIs ditch the explicit delegate pattern in a lot of scenarios. For many developers, they just find it a lot easier to grasp the notion of some block when used as a callback for completion/failure of some asynchronous chunk of work. I think AFNetworking's early success is an example of this phenomenon, along with things like BlocksKit and other blocks-all-the-things projects.

Anyway, these thoughts haven't been properly put together, but I just don't want us all to assume that protocol delegation is somehow natural easy and approachable.


Also, just for the hell of it, here's a stab at restructuring the Reactive Cocoa example code. Reactive Cocoa versus delegates versus callbacks versus etc. misses the point. As Chris's post outlines, it's all about better ways to express intent and structure code for better locality, reduced mutable state, and ultimately fewer bugs and better software.

  1. Remove the need for a @weakify/@strongify song-and-dance with self. Try to limit explicit side-effects to the doError: and doCompleted: operations.
  2. Improve how we express the intent of a refresh signal: it's the next values from the topQuestionsSignal whenever the refresh control fires off its UIControl event.
  3. Define a relationship between the refresh signal and two plain-old methods:
    • -loadQuestions: will continuously be passed the next values from the refreshSignal.
    • -displayError:title: will be passed some next values that we've transformed from the error events of refreshSignal.
- (void)viewDidLoad {
    [super viewDidLoad];

    self.refreshControl = ({
        UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];

        RACSignal *refreshSignal = [[[topQuestionsSignal

            // only send the next top questions when the refresh control triggers a
            // refresh via UIControlEventValueChanged
            sample:[refreshControl rac_signalForControlEvents:UIControlEventValueChanged]]

            // Inject a side-effect of stoping the refresh control's animation
            // whenever the signal errors
            doError:^(NSError *error) {
                [refreshControl endRefreshing];
            }]

            // Inject a side-effect of stopping the refresh control's animation
            // whenever the signal errors
            doCompleted:^{
                [refreshControl endRefreshing];
            }];

        // Lift the `-loadQuestions:` method into the reactive world.
        // Set up a binding relationship that whenever the `refreshSignal` sends a
        // next value, pass it in as the parameter for `-loadQuestions:`.
        [self rac_liftSelector:@selector(loadQuestions:) withSignals:refreshSignal, nil];

        // Lift the `-displayError:title:` method into the reactive world.  Set up
        // a binding relationship that whenever the `refreshSignal` sends an error,
        // let's catch it an massage the value into parameters that can be passed
        // to `-displayError:title:`
        [self
            rac_liftSelector:@selector(displayError:title:)
            withSignalsFromArray:@[
                [refreshSignal catch:^RACSignal *(NSError *error) {
                    return [RACSignal return:error.localizedDescription];
                }],
                [RACSignal return:@"An error occurred"],
            ]];

        refreshControl;
    });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment