MUKContentRedux 1.3.0

MUKContentRedux 1.3.0

测试已测试
Lang语言 Obj-CObjective C
许可 MIT
发布上次发布2016年2月

Marco Muccinelli 维护。



  • Marco Muccinelli

MUKContentRedux 为不可变数据提供一个存储,这些数据只能通过应用操作来更新。它受到了 ReSwift 的启发,但野心要小得多。请参考他们的项目,以了解在代码中简化交互流程的好处。

用法

首先,您应该通过您的 UIViewController 实例描述您向用户暴露的内容。为此,您需要创建一个新的符合 <MUKContent> 协议的不可变对象,实际上是一个空的协议。假设我们正在制作一个计数器屏幕

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

这个 CounterContent 是一个状态,它不具备行为。唯一改变它的方式是通过定义操作,创建其他符合 <MUKContentAction> 协议的不可变对象,另一个空的协议。

@interface CounterIncrementAction : NSObject <MUKContentAction>
@end

@interface CounterDecrementAction : NSObject <MUKContentAction>
@end

这些操作不具备行为。它们唯一的携带改变状态的需求。这个需求只能分发给存储。 MUKContentStore 是这个库中唯一的具体类。您不应该覆盖它,但您应该创建一个提供还原函数的存储。还原器是一个符合 <MUKContentReducer> 协议的对象,一个只有一个必需方法的协议:-contentFromContent:handlingAction:。还原器的唯一任务是应用操作到现有内容,以返回一个新的状态。

@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;
    }
}

现在,您已经在视图控制器中拥有创建存储所需的一切

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

您通过 -dispatch: 方法向存储发送操作

- (IBAction)incrementButtonPressed:(id)sender {
    [self.store dispatch:[CounterIncrementAction new]];
}

您通过将一个块注册到 -subscribe: 方法来接收内容更新的通知

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

我鼓励您注意,在这个系统中,数据流是单向的并且总是可预测的:视图控制器将操作分发给存储;存储要求还原器将操作应用到现有内容;还原器将新的内容发送到存储;存储将内容更新发送到订阅者。每个组件都得到了良好的隔离,副作用最小。

在这个美妙的同时世界里,什么是真实的异步世界呢?这是使用算子的典型用例。算子是一个延迟表达式求值的函数。通常,存储只能分发操作,但是 MUKContentThunkMiddleware 扩展了这种功能。

您可以使用这个中间件创建存储

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

然后,您可以为符合 MUKContentThunk 协议的每个对象分发操作。 MUKBlockContentThunk 是一个方便的算子,它包装了一个块。

+ (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];
        dispatcher(action);

        [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)
    }
}

您的视图控制器将无缝地使用算子

[self.store dispatch:[ActionCreator requestInfos]];

需求

  • iOS 7 SDK。
  • 最低部署目标:iOS 7。

安装

作者

Marco Muccinelli, [email protected]

授权协议

MUKContentRedux 遵循MIT授权协议。请参阅LICENSE文件获取更多信息。