Giter VIP home page Giter VIP logo

reflow's Introduction

Reflow

A unidirectional data flow framework for Objective-C inspired by Flux, Redux and Vue.

Motivation

When writing Objective-C code, we are used to defining properties of models as nonatomic. However, doing this may lead to crash in multithread environment. For example, calling the method methodA below the app will crash with a EXC_BAD_ACCESS exception.

// TodoStore.h
@interface TodoStore : NSObject
@property (nonatomic, copy) NSArray *todos;
@end

// xxx.m
- (void)methodA
{
    TodoStore *todoStore = [[TodoStore alloc] init];
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            todoStore.todos = [[NSArray alloc] initWithObjects:@1, @2, @3, nil];
        }
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            NSLog(@"%@", todoStore.todos);
        }
    });
}

A simple approach to the crash here might be defining the properties as atomic. However this approach doesn't solve other issues like race condition.

Another danger is not keeping views up to date since propeties are mutable and we can change them any where throughout the app. If the todos property from the above example is binding to the cells of a table view, adding or removing a todo item and forgetting to notify the table view to do reloadData will cause another crash with a NSInternalInconsistencyException in the table view.

Core Concepts

Reflow solves the above problems by imposing a more principled architecture, with the following concepts.

Store

Like Redux, the state of your whole application is stored in a certain layer, store, and you concentrate your model update logic in the store too. Note that store in Reflow is an abstract concept. The state in the store can be in a database or in memory or both. What really concerns is that we only have a single source of truth.

As our application grows in scale, the whole state can get really bloated. Reflow allows us to divide our store into modules to help with that. Each store module is a subclass of RFStore.

@interface TodoStore : RFStore

#pragma mark - Getters

- (NSArray *)visibleTodos;
- (VisibilityFilter)visibilityFilter;

#pragma mark - Actions

- (void)actionAddTodo:(NSString *)text;
- (void)actionToggleTodo:(NSInteger)todoId;

- (void)actionSetVisibilityFilter:(VisibilityFilter)filter;

@end

Actions

Actions are defined as normal methods on store modules, except with a name prefix "action". Reflow will do some magic tricks with that.

@implementation TodoStore

...

#pragma mark - Actions

- (void)actionAddTodo:(NSString *)text {
    Todo *todo = ...
    
    self.todos = [self.todos arrayByAddingObject:todo];
}

- (void)actionToggleTodo:(NSInteger)todoId {
    self.todos = [self.todos map:^id(Todo *value) {
        if (value.todoId == todoId) {
            Todo *todo = ...
            return todo;
        }
        return value;
    }];
}

- (void)actionSetVisibilityFilter:(VisibilityFilter)filter {
    self.filter = filter;
}

@end

Subscriptions

When subclassing RFStore, we can subscribe to all actions on a store module.

@implementation TodoTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.todoStore = [[TodoStore alloc] init];
    self.todos = [self.todoStore visibleTodos];
    self.filterButton.title = [self stringFromVisibilityFilter:[self.todoStore visibilityFilter]];
    
    self.subscription = [self.todoStore subscribe:^(RFAction *action) {
        if (action.selector == @selector(actionSetVisibilityFilter:)) {
            self.filterButton.title = [self stringFromVisibilityFilter:[self.todoStore visibilityFilter]];
        }
        self.todos = [self.todoStore visibleTodos];
        [self.tableView reloadData];
    }];
}

...
@end

On completion of each call of a action method, a store module will execute the block passed in subscribe:, passing in an instance of RFAction as a parameter of the block. Each instance of RFAction contains infomation like the object that the action method is sent to, the selector of the action method and the arguments that are passed in to the action method.

@interface RFAction : NSObject

@property (nonatomic, readonly) id object;
@property (nonatomic, readonly) SEL selector;
@property (nonatomic, readonly) NSArray *arguments;

@end

Note that we should retain the returned subscription when calling subscribe:. Reflow will not retain it internally and when all strong references to the subscription are gone, the subscription will call unsubscribe automatically.

We can subscribe to a single store module multiple times and we can subcribe to all actions of all store modules.

[RFStore subscribeToAllStores:xxx];

Principles

Reflow is more of a pattern rather than a formal framework. It can be described in the following principles:

  • Immutable model
  • Single source of truth
  • Centralized model update

Installation

Just copy the source files into your project.

Required

  • RFAspects.h/m
  • RFStore.h/m

Optional

  • NSArray+Functional.h/m

reflow's People

Contributors

zepo avatar

Watchers

James Cloos avatar 令狐虫 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.