首页 > 代码库 > 让RAC支持进度消息

让RAC支持进度消息

     我们在使用RAC的时候,有时候处理一个主消息之外可能还需要其他的辅助消息。比如说,我们在上次图片或者下载的时候。往往除了需要知道结果,还需要过程的进度。简单的做法就是外部自己创建一个subject,然后给具体做事情的模块来手动设置subject的next值了。这是一个经常用的东西,所以我这边参考了AFNetworking+RACExtension中得代码。自己重建了支持进度的signal和subscribe。我是在RAC 2.4.2上面搞的。所以不一定支持其他版本的哦。  废话不说,直接上代码。

     

//
//  RACSubscriber+KLProgressSupport.h
//  KoalaUtilKit
//
//  Created by Tommy on 15/1/8.
//  Copyright (c) 2015年 Koala Team. All rights reserved.
//

#import "ReactiveCocoa.h"

@interface KLRACSubscriber: NSObject<RACSubscriber>

+ (instancetype)subscriberWithNext:(void (^)(id x))next progress:(void (^)(float progress))progress error:(void (^)(NSError *error))error completed:(void (^)(void))completed;

- (void)sendProgress:(float)p;
- (void)sendNext:(id)value;
- (void)sendError:(NSError *)error;
- (void)sendCompleted;
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;

@end

@interface RACSignal (KLProgressSubscriptions)


// Convenience method to subscribe to the `progress` and `next` events.
- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock ;

// Convenience method to subscribe to the `progress`, `next` and `completed` events.
- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock;

// Convenience method to subscribe to the `progress`, `next`, `completed`, and `error` events.
- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock;


- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress completed:(void (^)(void))completedBlock;

// Convenience method to subscribe to `progress`, `next` and `error` events.
- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock;

// Convenience method to subscribe to `progress`, `error` and `completed` events.
- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock;

@end

@interface RACSubject (KLProgressSending)

- (void)sendProgress:(float)value;

@end



//
//  RACSubscriber+KLProgressSupport.m
//  KoalaUtilKit
//
//  Created by Tommy on 15/1/8.
//  Copyright (c) 2015年 Koala Team. All rights reserved.
//

#import "RACSubscriber+KLProgressSupport.h"
#import "RACPassthroughSubscriber.h"
#import <objc/runtime.h>
#import "KLLog.h"


@interface RACPassthroughSubscriber (KLProgress)
- (void)sendProgress:(float)p;
@end

@implementation RACPassthroughSubscriber(KLProgress)
- (void)sendProgress:(float)p{
    
    RACDisposable *disposable = [self performSelector:@selector(disposable)];
    if (disposable.disposed) return;
    
    id<RACSubscriber> innerSubscriber = [self valueForKey:@"innerSubscriber"];
    if([innerSubscriber isKindOfClass:[RACPassthroughSubscriber class]]){
        [(RACPassthroughSubscriber*)innerSubscriber sendProgress:p];
    }else if([innerSubscriber isKindOfClass:[KLRACSubscriber class]]){
        [(KLRACSubscriber*)innerSubscriber sendProgress:p];
    }else{
        NSAssert(0, @"not recognized object");
    }

}

@end



static NSString *KLProgress_Block_Key;
@interface KLRACSubscriber()

@property(nonatomic,strong)id<RACSubscriber> subscriber;
@property (nonatomic, copy) void (^_progress)(float progress);

@end


@implementation KLRACSubscriber

+ (instancetype)subscriberWithNext:(void (^)(id x))next progress:(void (^)(float progress))progress error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
    Class subscriberCls = NSClassFromString(@"RACSubscriber");
    SEL createSel = sel_registerName("subscriberWithNext:error:completed:");
    static int supportCreate = -1;
    id<RACSubscriber> proxySubscriber = nil;
    void *obj = nil;
    if(-1 == supportCreate){
        supportCreate = [subscriberCls respondsToSelector:createSel];
    }

    if(YES == supportCreate){
        NSMethodSignature *sig= [subscriberCls methodSignatureForSelector:createSel];
        if(sig && !strcmp(sig.methodReturnType, @encode(id))){
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
            [invocation setTarget:subscriberCls];
            [invocation setSelector:createSel];
            
            next = [next copy];
            error = [error copy];
            completed = [completed copy];
            
            [invocation setArgument:&next atIndex: 2];
            [invocation setArgument:&error atIndex: 3];
            [invocation setArgument:&completed atIndex: 4];
            [invocation retainArguments];
            
            [invocation invoke];
            
            [invocation getReturnValue:&obj];
            proxySubscriber = (__bridge id<RACSubscriber>)(obj);
        }
    }
    KLRACSubscriber *subscriber = nil;
    if(proxySubscriber){
        subscriber = [[KLRACSubscriber alloc]init];
        subscriber.subscriber = proxySubscriber;
        subscriber._progress = progress;
    }else{
        NSAssert(0, @"not create RACSubscriber");
    }
    
    return subscriber;

}

- (void)set_progress:(void (^)(float))_progress {
    objc_setAssociatedObject(self, &KLProgress_Block_Key, _progress, OBJC_ASSOCIATION_COPY);
}

- (void (^)(float))_progress {
    return objc_getAssociatedObject(self, &KLProgress_Block_Key);
}

- (void)sendProgress:(float)p {
    [[self performSelector:@selector(disposable)] dispose];
    if (self._progress != NULL) self._progress(p);
}

- (void)sendNext:(id)value{
    return [self.subscriber sendNext:value];
}

- (void)sendError:(NSError *)error{
    return [self.subscriber sendError:error];
}

- (void)sendCompleted{
    return [self.subscriber sendCompleted];
}

- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable{
    [self.subscriber didSubscribeWithDisposable:disposable];
}


- (void)forwardInvocation:(NSInvocation *)invocation{
    NSAssert(self.subscriber, @"subscriber is nil");
    if(!self.subscriber)
        KLLogError(@"subscriber is nil");
    [invocation invokeWithTarget:self.subscriber];
}
                                   
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
    NSAssert(self.subscriber, @"subscriber is nil");
    if(!self.subscriber)
        KLLogError(@"subscriber is nil:%@",NSStringFromSelector(sel));
    NSObject* tmpSub = self.subscriber;
    return [tmpSub methodSignatureForSelector:sel];
}

- (void)dealloc{
    self.subscriber = nil;
}

@end

@implementation RACSignal (KLProgressSubscriptions)

- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock {
    NSParameterAssert(progress != NULL);
    NSParameterAssert(nextBlock != NULL);
    
    KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:nextBlock progress:progress error:NULL completed:NULL];
    
    return [self subscribe:o.subscriber];
}

- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock {
    NSParameterAssert(progress != NULL);
    NSParameterAssert(nextBlock != NULL);
    NSParameterAssert(completedBlock != NULL);
    
    KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:nextBlock progress:progress error:NULL completed:completedBlock];
    
    return [self subscribe:o];
}

- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock {
    NSParameterAssert(progress != NULL);
    NSParameterAssert(nextBlock != NULL);
    NSParameterAssert(errorBlock != NULL);
    NSParameterAssert(completedBlock != NULL);
    
    KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:nextBlock progress:progress error:errorBlock completed:completedBlock];
    
    return [self subscribe:o];
}

- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress completed:(void (^)(void))completedBlock {
    NSParameterAssert(progress != NULL);
    NSParameterAssert(completedBlock != NULL);
    
    KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:NULL progress:progress error:NULL completed:completedBlock];
    
    return [self subscribe:o];
}

- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock {
    NSParameterAssert(progress != NULL);
    NSParameterAssert(nextBlock != NULL);
    NSParameterAssert(errorBlock != NULL);
    
    KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:nextBlock progress:progress error:errorBlock completed:NULL];
    
    return [self subscribe:o];
}

- (RACDisposable *)subscribeProgress:(void (^)(float progress))progress error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock {
    NSParameterAssert(progress != NULL);
    NSParameterAssert(errorBlock != NULL);
    NSParameterAssert(completedBlock != NULL);
    
    KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:NULL progress:progress error:errorBlock completed:completedBlock];
    
    return [self subscribe:o];
}

@end

@implementation RACSubject (KLProgressSending)

- (void)sendProgress:(float)value {
    void (^subscriberBlock)(id<RACSubscriber> subscriber) = ^(id<RACSubscriber> subscriber){
        if([subscriber isKindOfClass:[RACPassthroughSubscriber class]]){
            [(RACPassthroughSubscriber*)subscriber sendProgress:value];
        }
    };
    
    SEL performBlockSel = sel_registerName("enumerateSubscribersUsingBlock:");
    if([self respondsToSelector:performBlockSel]){
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [self performSelector:performBlockSel withObject:subscriberBlock];
#pragma clang diagnostic pop
    }else{
        NSAssert(0, @"not found enumerateSubscribersUsingBlock:");
    }
}

@end


让RAC支持进度消息