jyothepro / signals-ios

Typeful eventing

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Signals

Build Status Cocoapods Compatible Carthage Compatible License Platform

Signals is an eventing framework that enables you to implement the Observable pattern without using error prone and clumsy NSNotifications or delegates.

Features

  • Type-safety
  • Attach-and-forget observation
  • Specify operation queue to observe events on
  • Comprehensive Unit Test Coverage

Installation

Cocoapods

To integrate Signals into your project add the following to your Podfile:

pod 'UberSignals', '~> 1.0'

Carthage

To integrate Signals into your project using Carthage add the following to your Cartfile:

github "uber/signals-ios" ~> 1.0

Introduction

NSNotifications are inherently error prone. If a listener doesn’t de-register itself from a notification when it’s deallocated, firing the notification will crash the application. If you refactor the data you send with a notification, the compiler won't warn you but your app might crash at runtime.

NSNotifications are also unnecessarily broad. Anyone can listen in on them which couples separate components in your application implicitly together.

With NSNotifications you register a selector to be invoked when a notification fires. This makes code less readable by separating where you register for notifications and where you handle notifications.

NSNotifications also require a lot of boilerplate code to register unique names to use as notification identifiers.

Signals solves all of the above problems and provides an inline, type-safe and attach-and-forget way to observe events being fired by objects. It is also a great replacement for delegates when there is no need to return data from the delegates.

Usage

Make a class observable by declaring a Signals in its header and implementing it in its initializer:

// Defines a new Signal type. This type is named "NetworkResult", and has two parameters 
// of type NSData and NSError. Note that the postfix "Signal" is automatically added to 
// the type name. Also note that only objects are allowed in Signal signatures.
CreateSignalType(NetworkResult, NSData *result, NSError *error)

// In your header you define the signal
@property (nonatomic, readonly) UBSignal<NetworkResultSignal> *onNetworkResult;

// In the initializer the instance creates the signal
_onNetworkResult = (UBSignal<NetworkResultSignal> *)
      [[UBSignal alloc] initWithProtocol:@protocol(NetworkResultSignal)];

// Whenever the instance receives a network result (with NSData and NSError), it
// fires off the signal.
_onNetworkResult.fire(myData, myError);

Any class who has access to the NetworkResult instance, can now register itself as a listener and get notified whenever the network operation has loaded:

[networkRequest.onNetworkResult addObserver:self 
            callback:^(typeof(self) self, NSData *data, NSError *error) {
    // Do something with the result. The self passed into the block is weakified by Signals
    // to guard against retain cycles.
}];

To cancel a single observer, call cancel on the returned UBSignalObserver:

UBSignalObserver *observer = [networkRequest.onNetworkResult addObserver:self 
        callback:^(typeof(self) self, NSData *data, NSError *error) {
    ...
}];
...
[observer cancel];

You can also configure the observer to cancel itself after it has observed a signal firing once:

[networkRequest.onNetworkResult addObserver:self 
            callback:^(typeof(self) self, NSData *data, NSError *error) {
    ...
}].cancelsAfterNextFire = YES;

The callback is by default called on the same NSOperationQueue than the signal fires on. To change this, simply change the operationQueue parameter of the returned UBSignalObserver.

[networkRequest.onNetworkResult addObserver:self 
            callback:^(typeof(self) self, NSData *data, NSError *error) {
    ....
}].operationQueue = NSOperationQueue.mainQueue;

Signal naming

Each signal type created with the CreateSignalType macro creates a new protocol so that the compiler can enforce type safety. This means that the name you choose for your signal types need to be unique to your project.

Frequently, a signal will fire no parameters or one parameter of the basic ObjC types. Signals therefore predefines a set of signal types that you can use:

EmptySignal, fires no parameters
IntegerSignal, fires a NSNumber
FloatSignal, fires a NSNumber
DoubleSignal, fires a NSNumber
BooleanSignal, fires a NSNumber
StringSignal, fires a NSString
ArraySignal, fires a NSArray
MutableArraySignal, fires a NSMutableArray
DictionarySignal, fires a NSDictionary
MutableDictionarySignal, fires a NSMutableDictionary

About

Typeful eventing

License:MIT License


Languages

Language:Objective-C 98.9%Language:Ruby 1.1%