Latest 1.3.0
License MIT
Platforms ios 7.0, requires ARC

CI Status

MUKContentRedux provides a store for immutable data which can be updated only applying actions. It is inspired by ReSwift but very very less ambitious. Please refer to their project to discover the benefits of streamlining the interaction flow in our code.


First of all you should describe the content you expose to user via your UIViewController instance. To do that you have to create a new immutable object which conforms to <MUKContent>, actually an empty protocol.
Say we are making a counter screen:

@interface CounterContent : NSObject <MUKContent>
@property (nonatomic, readonly) NSInteger integerValue;
- (instancetype)initWithIntegerValue:(NSInteger)integerValue NS_DESIGNATED_INITIALIZER;

This CounterContent is a state. It does not behave. The only way to change it is by defining actions by creating other immutable objects which conform to <MUKContentAction>, another empty protocol.

@interface CounterIncrementAction : NSObject <MUKContentAction>

@interface CounterDecrementAction : NSObject <MUKContentAction>

Those actions do not behave. The only carry the request to change state. This request can only be dispatched to a store. MUKContentStore is the only concrete class this library contains. You should not override it but you should create a store providing a reducer. The reducer is an object which conforms to <MUKContentReducer>, a protocol with only a required method: -contentFromContent:handlingAction:. Reducers have the only key job to apply an action to existing content in order to return a new state.

@implementation CounterReducer

- (nullable CounterContent *)contentFromContent:(nullable CounterContent *)oldContent handlingAction:(id<MUKContentAction>)action
    if ([action isKindOfClass:[CounterIncrementAction class]]) {
        return [[CounterContent alloc] initWithIntegerValue:oldContent.integerValue + 1];
    else if ([action isKindOfClass:[CounterDecrementAction class]]) {
        return [[CounterContent alloc] initWithIntegerValue:oldContent.integerValue - 1];
    else {
        return oldContent;

Now you have everything you need to create your store inside your view controller:

MUKContentStore<CounterContent *> *const store = [MUKContentStore storeWithReducer:[CounterReducer new]]; = store;

You send actions to store via -dispatch: method:

- (IBAction)incrementButtonPressed:(id)sender {
    [ dispatch:[CounterIncrementAction new]];

You receive content updates by registering a block with -subscribe: method:

__weak __typeof__(self) weakSelf = self;
[ subscribe:^(CounterContent * _Nullable oldContent, CounterContent * _Nullable newContent) {
    __strong __typeof__(weakSelf) strongSelf = weakSelf;
    [strongSelf updateUI];

I encourage you to note that data flow is unidirectional and always predictable with this system: view controller dispatches an action to store; store asks reducer to apply action to existing content; reducer sends new content to store; store sends content update to subscribers. Each component is well isolated and side effects are minimal.

Everything shines in this golden synchronous world. What about the real async world? This is a typical use case of thunks. A thunk is a function that wraps an expression to delay its evaluation. Normally store can only dispatch actions, but MUKContentThunkMiddleware extends this capability.

You create store with this middleware:

MUKContentStore<CounterContent *> *const store = [[MUKContentStore alloc] initWithReducer:[CounterReducer new] content:nil middlewares:@[ [MUKContentThunkMiddleware new] ]]; = store;

Then you can dispatch every object which conforms to MUKContentThunk protocol. MUKBlockContentThunk is an handy thunk which wraps a block.

+ (id<MUKContentThunk>)requestInfos {
    return [MUKBlockContentThunk thunkWithBlock:^id _Nullable(MUKContentDispatcher _Nullable dispatcher, MUKContentGetter _Nonnull getter)
        Content *const content = getter();

        if (content.status == ContentStatusLoading) {
            return nil; // Already loading

        // Start request (e.g.: this will show spinner)
        id<MUKContentAction> const action = [ActionCreator requestStart];

        [APIClient() fetch:^(NSData *data, NSError *error) {
            // Dispatch actions to respond async fetch event

            if (data) {
                dispatcher([ActionCreator requestFinished:data]);
            else {
                dispatcher([ActionCreator requestFailed:error]);

        return action; // This is optional. You can also return other objects (e.g.: a token to cancel fetch)

You view controller will use thunks transparently:

[ dispatch:[ActionCreator requestInfos]];


  • iOS 7 SDK.
  • Minimum deployment target: iOS 7.


MUKContentRedux is available through CocoaPods. To install
it, simply add the following line to your Podfile:

pod "MUKContentRedux"


Marco Muccinelli, [email protected]


MUKContentRedux is available under the MIT license. See the LICENSE file for more info.

Latest podspec

    "name": "MUKContentRedux",
    "version": "1.3.0",
    "summary": "A structure to store content data in an immutable way using input actions.",
    "description": "A store for immutable data which can be updated only applying actions. Inspired by ReSwift but very very less ambitious.",
    "homepage": "",
    "license": "MIT",
    "authors": {
        "Marco Muccinelli": "[email protected]"
    "source": {
        "git": "",
        "tag": "1.3.0"
    "platforms": {
        "ios": "7.0"
    "requires_arc": true,
    "subspecs": [
            "name": "Core",
            "source_files": "Pod/Core/**/*.{h,m}"
            "name": "Logger",
            "source_files": "Pod/Logger/*.{h,m}",
            "dependencies": {
                "MUKContentRedux/Core": []
            "name": "Thunk",
            "source_files": "Pod/Thunk/*.{h,m}",
            "dependencies": {
                "MUKContentRedux/Core": []

Pin It on Pinterest

Share This