Objc-C: RACObserve быстро меняющиеся значения (RACSignal для AFHTTPRequestOperation)

Я использую реактивное какао для создания файла загрузки с сервера. У меня есть файл DownloadMapFileOperation.m со свойством прогресса (значение прогресса, currentBytes, totalBytes). Это очень быстро меняется в моем следующем RACSignal метод и плавающий прогресс (это бесполезно, но

@property (nonatomic, strong, readwrite) NSMutableDictionary *progressDictionary;
@property (nonatomic, assign, readwrite) float progress;

а также RACSignal метод создания

- (RACSignal *)signalForDownloadMap:(Place *)place
{
    return [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSString *mapUrlString = [NSString stringWithFormat:@"http://myurl/%@", place.mapFileName];
        NSURL *url = [NSURL URLWithString:mapUrlString];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];

        _downloadFileOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

        NSString *documentsDirectory = [paths objectAtIndex:0];

        NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[url lastPathComponent]];

        [_downloadFileOperation setOutputStream:[NSOutputStream outputStreamToFileAtPath:fullPath append:NO]];

        [_downloadFileOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation , id response)
         {
             NSLog(@"Downloaded via compl block");
             [subscriber sendNext:nil];
             [subscriber sendCompleted];

         } failure:^(AFHTTPRequestOperation *operation, NSError *error)
         {
             [subscriber sendError:error];
         }];

        @weakify(self)
        self.progressDictionary = [[NSMutableDictionary alloc] init];
        self.progressDictionary[@"progress"] = @(0);
        self.progressDictionary[@"current"] = @(0);
        self.progressDictionary[@"total"] = @(0);
        self.progress = @(0);

        [_downloadFileOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
         {
             @strongify(self)
             self.progressDictionary[@"current"] = @(totalBytesRead);
             self.progressDictionary[@"total"] = @(totalBytesExpectedToRead);
             self.progressDictionary[@"progress"] = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
             self.progress = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
             self.progress = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
         }];

        [_downloadFileOperation start];
        return nil;
    }] filter:^BOOL(id value) {
        return !(.mapFileName == nil);
    }]
        deliverOn:[RACScheduler mainThreadScheduler]
    ];
}

Я просто хочу показать прогресс на мой взгляд через RACObserve в моем ViewController.

[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
     subscribeNext:^(AFHTTPRequestOperation *operation) {
        NSString *alertMessage = @"Download complete";
        [[[UIAlertView alloc] initWithTitle:@"Complete" message:alertMessage delegate:self cancelButtonTitle:@"Close" otherButtonTitles: nil] show];
        downloadMapView.progressLabel.text = @"Download Complete";
    }];

    [[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place] subscribeError:^(NSError *error) {
        [[[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show];
    }];

    RAC(downloadMapView, progressView.progress) = [RACObserve([DownloadMapFileOperation sharedInstance], progress)
                                                   map:^id(id value) {
                                                       return value;
                                                   }];

  RAC(downloadMapView, progressLabel.text) = [RACObserve([DownloadMapFileOperation sharedInstance], progressDictionary)
                                                    map:^id(NSDictionary *value) {
                                                        return [value[@"progress"] isEqual: @(1)] ? @"Download Complete" : [NSString stringWithFormat:@"Downloaded %@ of %@", value[@"current"], value[@"total"]];
                                                    }];

Но мой взгляд на прогресс иногда меняется с 60% до 50% (или 45 - 35 и т. Д.) NSLog(@"Downloaded via compl block"); показывает, прежде чем прогресс достигает 100% (иногда он достигает и начинается снова примерно с 70%). И если я использую progressDictionary(как во втором RACObserve) - это не работает (показывает 0 прогресса и загрузок 0 из 0)

РЕДАКТИРОВАНИЕ

Я также пытаюсь добавить методы в мой файл DownloadMapFileOperation.m, который приостанавливает, возобновляет, отменяет операцию загрузки.

- (RACSignal *)signalForPauseDownloadMap
{
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [_downloadFileOperation pause];
        [subscriber sendNext:nil];
        [subscriber sendCompleted];
        return nil;
    }];
}

- (RACSignal *)signalForResumeDownloadMap
{
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [_downloadFileOperation resume];
        [subscriber sendNext:nil];
        [subscriber sendCompleted];
        return nil;
    }];
}

- (RACSignal *)signalForCancelDownloadMap
{
    return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [_downloadFileOperation cancel];
        [subscriber sendNext:nil];
        [subscriber sendCompleted];
        return nil;
    }] filter:^BOOL(id value) {
        return (_downloadFileOperation);
    }];
}

Но: Моя операция загрузки файла не останавливается. Когда я использую в моем ВК

[[_downloadMapFileOperation signalForPauseDownloadMap]
    subscribeNext:^(id x) {
        [downloadMapView setTitleForStartDownloadButton:@"Resume download"];
        _currentDownloadState = kHTDownloadStatePaused;
    }];

Мое значение прогресса работает правильно (не переходит от значения к значению). Помоги мне, пожалуйста. Благодарю.

1 ответ

Решение

Если исходный вопрос о скачкообразном прогрессе загрузки, проблема в том, что вы создаете подписку на две копии сигнала, что приводит к двум загрузкам. А именно, этот бит кода:

[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
 subscribeNext:^(AFHTTPRequestOperation *operation) {
    NSString *alertMessage = @"Download complete";
    [[[UIAlertView alloc] initWithTitle:@"Complete" message:alertMessage delegate:self cancelButtonTitle:@"Close" otherButtonTitles: nil] show];
    downloadMapView.progressLabel.text = @"Download Complete";
}];

[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place] subscribeError:^(NSError *error) {
    [[[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show];
}];

каждый subscribeNext/subscribeError/так далее. создаст новую подписку и начнет загрузку. То, что вы хотите использовать для подписки, выглядит следующим образом:

[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
 subscribeNext:^(AFHTTPRequestOperation *operation) {
    NSString *alertMessage = @"Download complete";
    [[[UIAlertView alloc] initWithTitle:@"Complete" message:alertMessage delegate:self cancelButtonTitle:@"Close" otherButtonTitles: nil] show];
    downloadMapView.progressLabel.text = @"Download Complete";
} error:^(NSError *error) {
    [[[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil] show];
}];

Для паузы и возобновления я не думаю, что создание дальнейших сигналов имеет смысл. Если у вас есть доступ к операции загрузки файла, просто вызовите паузу / возобновить непосредственно на нем. Исходная подписка будет оставаться действительной и должна приостанавливать / возобновлять обновление в зависимости от поведения в созданном сигнале.

Что касается отмены, то, что вы действительно хотите сделать, это использовать одноразовые. в [RACSignal createSignal:] позвоните, вы не хотите возвращаться nil, Вместо этого установите одноразовое устройство, которое отменяет загрузку при утилизации одноразового использования.

Редактировать:

В идеале вы хотите отправить прогресс через сигнал. в setDownloadProgressBlock вы бы вместо этого:

NSMutableDictionary *progressDictionary = [[NSMutableDictionary alloc] init];
progressDictionary[@"current"] = @(totalBytesRead);
progressDictionary[@"total"] = @(totalBytesExpectedToRead);
progressDictionary[@"progress"] = @((float)totalBytesRead / (float)totalBytesExpectedToRead);
[subscriber sendNext:[progressDictionary copy]];

После этого вы сможете прочитать progressDictionary при подписке, например, так:

[[[DownloadMapFileOperation sharedInstance] signalForDownloadMap:self.place]
subscribeNext:^(NSDictionary *progressDictionary) {
    // Do stuff with dictionary here, like update UI with progress
} error:^(NSError *error) {
    // Error related stuff
} completed:^{
    // Completed stuff
}];

Примечание: вы не должны отправлять ноль через sendNext без уважительной причины. Логика завершения должна выполняться только в блоке завершения.

Другие вопросы по тегам