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 без уважительной причины. Логика завершения должна выполняться только в блоке завершения.