From c2deaf1693ae32e54111b750e922b98e6653c499 Mon Sep 17 00:00:00 2001 From: Steven Masini Date: Wed, 20 Mar 2019 16:09:46 +0800 Subject: [PATCH 1/7] feat: broadcast a notification when image succeed to download --- ios/FastImage/FFFastImageView.m | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ios/FastImage/FFFastImageView.m b/ios/FastImage/FFFastImageView.m index 5fa8d91ee..b66eb7ba8 100644 --- a/ios/FastImage/FFFastImageView.m +++ b/ios/FastImage/FFFastImageView.m @@ -14,8 +14,11 @@ - (id) init { return self; } -- (void)setResizeMode:(RCTResizeMode)resizeMode -{ +- (void)dealloc { + [NSNotificationCenter.defaultCenter removeObserver:self]; +} + +- (void)setResizeMode:(RCTResizeMode)resizeMode { if (_resizeMode != resizeMode) { _resizeMode = resizeMode; self.contentMode = (UIViewContentMode)resizeMode; @@ -64,10 +67,20 @@ - (void)sendOnLoad:(UIImage *)image { } } +- (void)imageDidLoadObserver:(NSNotification *)notification { + FFFastImageSource *source = notification.object; + if (source != nil) { + [self sd_setImageWithURL:source.url]; + } +} + - (void)setSource:(FFFastImageSource *)source { if (_source != source) { _source = source; + // Attach a observer to refresh other FFFastImageView instance sharing the same source + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(imageDidLoadObserver:) name:source.url.absoluteString object:nil]; + // Load base64 images. NSString* url = [_source.url absoluteString]; if (url && [url hasPrefix:@"data:image"]) { @@ -165,6 +178,10 @@ - (void)setSource:(FFFastImageSource *)source { } else { hasCompleted = YES; [self sendOnLoad:image]; + + // Alert other FFFastImageView component sharing the same URL + [NSNotificationCenter.defaultCenter postNotificationName:source.url.absoluteString object:source]; + if (_onFastImageLoadEnd) { _onFastImageLoadEnd(@{}); } From 4a57513f0ad3ade05fbdc968d9b14cd17311f458 Mon Sep 17 00:00:00 2001 From: Steven Masini Date: Wed, 20 Mar 2019 16:15:05 +0800 Subject: [PATCH 2/7] chore: modernize the syntax and user weak self reference in block --- ios/FastImage/FFFastImageView.m | 64 ++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/ios/FastImage/FFFastImageView.m b/ios/FastImage/FFFastImageView.m index b66eb7ba8..28ba3a190 100644 --- a/ios/FastImage/FFFastImageView.m +++ b/ios/FastImage/FFFastImageView.m @@ -1,11 +1,17 @@ #import "FFFastImageView.h" -@implementation FFFastImageView { - BOOL hasSentOnLoadStart; - BOOL hasCompleted; - BOOL hasErrored; - NSDictionary* onLoadEvent; -} + +@interface FFFastImageView() + +@property (nonatomic, assign) BOOL hasSentOnLoadStart; +@property (nonatomic, assign) BOOL hasCompleted; +@property (nonatomic, assign) BOOL hasErrored; + +@property (nonatomic, strong) NSDictionary* onLoadEvent; + +@end + +@implementation FFFastImageView - (id) init { self = [super init]; @@ -27,43 +33,43 @@ - (void)setResizeMode:(RCTResizeMode)resizeMode { - (void)setOnFastImageLoadEnd:(RCTBubblingEventBlock)onFastImageLoadEnd { _onFastImageLoadEnd = onFastImageLoadEnd; - if (hasCompleted) { + if (self.hasCompleted) { _onFastImageLoadEnd(@{}); } } - (void)setOnFastImageLoad:(RCTBubblingEventBlock)onFastImageLoad { _onFastImageLoad = onFastImageLoad; - if (hasCompleted) { - _onFastImageLoad(onLoadEvent); + if (self.hasCompleted) { + _onFastImageLoad(self.onLoadEvent); } } - (void)setOnFastImageError:(RCTDirectEventBlock)onFastImageError { _onFastImageError = onFastImageError; - if (hasErrored) { + if (self.hasErrored) { _onFastImageError(@{}); } } - (void)setOnFastImageLoadStart:(RCTBubblingEventBlock)onFastImageLoadStart { - if (_source && !hasSentOnLoadStart) { + if (_source && !self.hasSentOnLoadStart) { _onFastImageLoadStart = onFastImageLoadStart; onFastImageLoadStart(@{}); - hasSentOnLoadStart = YES; + self.hasSentOnLoadStart = YES; } else { _onFastImageLoadStart = onFastImageLoadStart; - hasSentOnLoadStart = NO; + self.hasSentOnLoadStart = NO; } } - (void)sendOnLoad:(UIImage *)image { - onLoadEvent = @{ - @"width":[NSNumber numberWithDouble:image.size.width], - @"height":[NSNumber numberWithDouble:image.size.height] - }; + self.onLoadEvent = @{ + @"width":[NSNumber numberWithDouble:image.size.width], + @"height":[NSNumber numberWithDouble:image.size.height] + }; if (_onFastImageLoad) { - _onFastImageLoad(onLoadEvent); + _onFastImageLoad(self.onLoadEvent); } } @@ -86,9 +92,9 @@ - (void)setSource:(FFFastImageSource *)source { if (url && [url hasPrefix:@"data:image"]) { if (_onFastImageLoadStart) { _onFastImageLoadStart(@{}); - hasSentOnLoadStart = YES; + self.hasSentOnLoadStart = YES; } { - hasSentOnLoadStart = NO; + self.hasSentOnLoadStart = NO; } UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:_source.url]]; [self setImage:image]; @@ -98,7 +104,7 @@ - (void)setSource:(FFFastImageSource *)source { @"total": @(1) }); } - hasCompleted = YES; + self.hasCompleted = YES; [self sendOnLoad:image]; if (_onFastImageLoadEnd) { @@ -140,12 +146,12 @@ - (void)setSource:(FFFastImageSource *)source { if (_onFastImageLoadStart) { _onFastImageLoadStart(@{}); - hasSentOnLoadStart = YES; + self.hasSentOnLoadStart = YES; } { - hasSentOnLoadStart = NO; + self.hasSentOnLoadStart = NO; } - hasCompleted = NO; - hasErrored = NO; + self.hasCompleted = NO; + self.hasErrored = NO; // Load the new source. // This will work for: @@ -153,6 +159,8 @@ - (void)setSource:(FFFastImageSource *)source { // - file:///var/containers/Bundle/Application/50953EA3-CDA8-4367-A595-DE863A012336/ReactNativeFastImageExample.app/assets/src/images/fields.jpg // - file:///var/containers/Bundle/Application/545685CB-777E-4B07-A956-2D25043BC6EE/ReactNativeFastImageExample.app/assets/src/images/plankton.gif // - file:///Users/dylan/Library/Developer/CoreSimulator/Devices/61DC182B-3E72-4A18-8908-8A947A63A67F/data/Containers/Data/Application/AFC2A0D2-A1E5-48C1-8447-C42DA9E5299D/Documents/images/E1F1D5FC-88DB-492F-AD33-B35A045D626A.jpg" + + __weak typeof(self) weakSelf = self; // Always use a weak reference to self in blocks [self sd_setImageWithURL:_source.url placeholderImage:nil options:options @@ -168,7 +176,7 @@ - (void)setSource:(FFFastImageSource *)source { SDImageCacheType cacheType, NSURL * _Nullable imageURL) { if (error) { - hasErrored = YES; + weakSelf.hasErrored = YES; if (_onFastImageError) { _onFastImageError(@{}); } @@ -176,8 +184,8 @@ - (void)setSource:(FFFastImageSource *)source { _onFastImageLoadEnd(@{}); } } else { - hasCompleted = YES; - [self sendOnLoad:image]; + weakSelf.hasCompleted = YES; + [weakSelf sendOnLoad:image]; // Alert other FFFastImageView component sharing the same URL [NSNotificationCenter.defaultCenter postNotificationName:source.url.absoluteString object:source]; From 824eb435113d16b869b05958196888cdd4f7b0b2 Mon Sep 17 00:00:00 2001 From: Steven Masini Date: Wed, 20 Mar 2019 16:25:05 +0800 Subject: [PATCH 3/7] chore: no need to have two lines to set the default value of options to SDWebImageRetryFailed --- ios/FastImage/FFFastImageView.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ios/FastImage/FFFastImageView.m b/ios/FastImage/FFFastImageView.m index 28ba3a190..0564e6005 100644 --- a/ios/FastImage/FFFastImageView.m +++ b/ios/FastImage/FFFastImageView.m @@ -119,8 +119,7 @@ - (void)setSource:(FFFastImageSource *)source { }]; // Set priority. - SDWebImageOptions options = 0; - options |= SDWebImageRetryFailed; + SDWebImageOptions options = SDWebImageRetryFailed; // Auto-retry to download if failed switch (_source.priority) { case FFFPriorityLow: options |= SDWebImageLowPriority; From 721a24ff6d752708a860dbb19cebc9e9cf15dd30 Mon Sep 17 00:00:00 2001 From: Steven Masini Date: Wed, 20 Mar 2019 17:41:36 +0800 Subject: [PATCH 4/7] feat: implement auto-retry download after 1.5, 3 or 4.5 seconds before failing --- ios/FastImage/FFFastImageView.m | 106 +++++++++++++++++--------------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/ios/FastImage/FFFastImageView.m b/ios/FastImage/FFFastImageView.m index 0564e6005..97d517fa9 100644 --- a/ios/FastImage/FFFastImageView.m +++ b/ios/FastImage/FFFastImageView.m @@ -90,25 +90,25 @@ - (void)setSource:(FFFastImageSource *)source { // Load base64 images. NSString* url = [_source.url absoluteString]; if (url && [url hasPrefix:@"data:image"]) { - if (_onFastImageLoadStart) { - _onFastImageLoadStart(@{}); + if (self.onFastImageLoadStart) { + self.onFastImageLoadStart(@{}); self.hasSentOnLoadStart = YES; } { self.hasSentOnLoadStart = NO; } UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:_source.url]]; [self setImage:image]; - if (_onFastImageProgress) { - _onFastImageProgress(@{ - @"loaded": @(1), - @"total": @(1) - }); + if (self.onFastImageProgress) { + self.onFastImageProgress(@{ + @"loaded": @(1), + @"total": @(1) + }); } self.hasCompleted = YES; [self sendOnLoad:image]; - if (_onFastImageLoadEnd) { - _onFastImageLoadEnd(@{}); + if (self.onFastImageLoadEnd) { + self.onFastImageLoadEnd(@{}); } return; } @@ -119,7 +119,7 @@ - (void)setSource:(FFFastImageSource *)source { }]; // Set priority. - SDWebImageOptions options = SDWebImageRetryFailed; // Auto-retry to download if failed + SDWebImageOptions options = SDWebImageRetryFailed; switch (_source.priority) { case FFFPriorityLow: options |= SDWebImageLowPriority; @@ -143,8 +143,8 @@ - (void)setSource:(FFFastImageSource *)source { break; } - if (_onFastImageLoadStart) { - _onFastImageLoadStart(@{}); + if (self.onFastImageLoadStart) { + self.onFastImageLoadStart(@{}); self.hasSentOnLoadStart = YES; } { self.hasSentOnLoadStart = NO; @@ -152,49 +152,55 @@ - (void)setSource:(FFFastImageSource *)source { self.hasCompleted = NO; self.hasErrored = NO; - // Load the new source. - // This will work for: - // - https:// - // - file:///var/containers/Bundle/Application/50953EA3-CDA8-4367-A595-DE863A012336/ReactNativeFastImageExample.app/assets/src/images/fields.jpg - // - file:///var/containers/Bundle/Application/545685CB-777E-4B07-A956-2D25043BC6EE/ReactNativeFastImageExample.app/assets/src/images/plankton.gif - // - file:///Users/dylan/Library/Developer/CoreSimulator/Devices/61DC182B-3E72-4A18-8908-8A947A63A67F/data/Containers/Data/Application/AFC2A0D2-A1E5-48C1-8447-C42DA9E5299D/Documents/images/E1F1D5FC-88DB-492F-AD33-B35A045D626A.jpg" - - __weak typeof(self) weakSelf = self; // Always use a weak reference to self in blocks - [self sd_setImageWithURL:_source.url - placeholderImage:nil - options:options - progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) { - if (_onFastImageProgress) { - _onFastImageProgress(@{ - @"loaded": @(receivedSize), - @"total": @(expectedSize) - }); - } - } completed:^(UIImage * _Nullable image, - NSError * _Nullable error, - SDImageCacheType cacheType, - NSURL * _Nullable imageURL) { - if (error) { + [self downloadImage:_source options:options retry:0]; + } +} + +- (void)downloadImage:(FFFastImageSource *) source options:(SDWebImageOptions) options retry:(NSInteger) retry { + __weak typeof(self) weakSelf = self; // Always use a weak reference to self in blocks + [self sd_setImageWithURL:_source.url + placeholderImage:nil + options:options + progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) { + if (weakSelf.onFastImageProgress) { + weakSelf.onFastImageProgress(@{ + @"loaded": @(receivedSize), + @"total": @(expectedSize) + }); + } + } completed:^(UIImage * _Nullable image, + NSError * _Nullable error, + SDImageCacheType cacheType, + NSURL * _Nullable imageURL) { + if (error) { + if (retry >= 3) { weakSelf.hasErrored = YES; - if (_onFastImageError) { - _onFastImageError(@{}); + if (weakSelf.onFastImageError) { + weakSelf.onFastImageError(@{}); } - if (_onFastImageLoadEnd) { - _onFastImageLoadEnd(@{}); + if (weakSelf.onFastImageLoadEnd) { + weakSelf.onFastImageLoadEnd(@{}); } } else { - weakSelf.hasCompleted = YES; - [weakSelf sendOnLoad:image]; - - // Alert other FFFastImageView component sharing the same URL - [NSNotificationCenter.defaultCenter postNotificationName:source.url.absoluteString object:source]; - - if (_onFastImageLoadEnd) { - _onFastImageLoadEnd(@{}); - } + // Auto-retry to download if failed + NSTimeInterval delayInSeconds = (retry + 1) * 5.0; // will retry after 0.5, 1.0 or 1.5 seconds + dispatch_time_t trigger = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); + dispatch_after(trigger, dispatch_get_main_queue(), ^{ + [weakSelf downloadImage:_source options:options retry:retry + 1]; + }); } - }]; - } + } else { + weakSelf.hasCompleted = YES; + [weakSelf sendOnLoad:image]; + + // Alert other FFFastImageView component sharing the same URL + [NSNotificationCenter.defaultCenter postNotificationName:source.url.absoluteString object:source]; + + if (weakSelf.onFastImageLoadEnd) { + weakSelf.onFastImageLoadEnd(@{}); + } + } + }]; } @end From 86b8b16cb80fe85db7b75fe3079552b1e3d12cc3 Mon Sep 17 00:00:00 2001 From: Steven Masini Date: Wed, 20 Mar 2019 19:07:13 +0800 Subject: [PATCH 5/7] chore: replace all RCTBubblingEventBlock by RCTDirectEventBlock --- ios/FastImage/FFFastImageView.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/FastImage/FFFastImageView.m b/ios/FastImage/FFFastImageView.m index 97d517fa9..d4d8bbfec 100644 --- a/ios/FastImage/FFFastImageView.m +++ b/ios/FastImage/FFFastImageView.m @@ -31,14 +31,14 @@ - (void)setResizeMode:(RCTResizeMode)resizeMode { } } -- (void)setOnFastImageLoadEnd:(RCTBubblingEventBlock)onFastImageLoadEnd { +- (void)setOnFastImageLoadEnd:(RCTDirectEventBlock)onFastImageLoadEnd { _onFastImageLoadEnd = onFastImageLoadEnd; if (self.hasCompleted) { _onFastImageLoadEnd(@{}); } } -- (void)setOnFastImageLoad:(RCTBubblingEventBlock)onFastImageLoad { +- (void)setOnFastImageLoad:(RCTDirectEventBlock)onFastImageLoad { _onFastImageLoad = onFastImageLoad; if (self.hasCompleted) { _onFastImageLoad(self.onLoadEvent); @@ -52,7 +52,7 @@ - (void)setOnFastImageError:(RCTDirectEventBlock)onFastImageError { } } -- (void)setOnFastImageLoadStart:(RCTBubblingEventBlock)onFastImageLoadStart { +- (void)setOnFastImageLoadStart:(RCTDirectEventBlock)onFastImageLoadStart { if (_source && !self.hasSentOnLoadStart) { _onFastImageLoadStart = onFastImageLoadStart; onFastImageLoadStart(@{}); From 1f0102dbcdf5109428dc55c33109d0548494b568 Mon Sep 17 00:00:00 2001 From: Steven Masini Date: Wed, 20 Mar 2019 19:52:13 +0800 Subject: [PATCH 6/7] chore: correct syntax --- ios/FastImage/FFFastImageView.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/FastImage/FFFastImageView.m b/ios/FastImage/FFFastImageView.m index d4d8bbfec..491926860 100644 --- a/ios/FastImage/FFFastImageView.m +++ b/ios/FastImage/FFFastImageView.m @@ -68,8 +68,8 @@ - (void)sendOnLoad:(UIImage *)image { @"width":[NSNumber numberWithDouble:image.size.width], @"height":[NSNumber numberWithDouble:image.size.height] }; - if (_onFastImageLoad) { - _onFastImageLoad(self.onLoadEvent); + if (self.onFastImageLoad) { + self.onFastImageLoad(self.onLoadEvent); } } @@ -186,7 +186,7 @@ - (void)downloadImage:(FFFastImageSource *) source options:(SDWebImageOptions) o NSTimeInterval delayInSeconds = (retry + 1) * 5.0; // will retry after 0.5, 1.0 or 1.5 seconds dispatch_time_t trigger = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(trigger, dispatch_get_main_queue(), ^{ - [weakSelf downloadImage:_source options:options retry:retry + 1]; + [weakSelf downloadImage:source options:options retry:retry + 1]; }); } } else { From 1c19f96b8d2826fee9bd6abe7b15fdb434504e60 Mon Sep 17 00:00:00 2001 From: Steven Masini Date: Mon, 22 Apr 2019 15:27:38 +0800 Subject: [PATCH 7/7] feat: remove the auto-retry logic --- ios/FastImage/FFFastImageView.m | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/ios/FastImage/FFFastImageView.m b/ios/FastImage/FFFastImageView.m index 491926860..581794b7d 100644 --- a/ios/FastImage/FFFastImageView.m +++ b/ios/FastImage/FFFastImageView.m @@ -75,7 +75,7 @@ - (void)sendOnLoad:(UIImage *)image { - (void)imageDidLoadObserver:(NSNotification *)notification { FFFastImageSource *source = notification.object; - if (source != nil) { + if (source != nil && source.url != nil) { [self sd_setImageWithURL:source.url]; } } @@ -152,11 +152,11 @@ - (void)setSource:(FFFastImageSource *)source { self.hasCompleted = NO; self.hasErrored = NO; - [self downloadImage:_source options:options retry:0]; + [self downloadImage:_source options:options]; } } -- (void)downloadImage:(FFFastImageSource *) source options:(SDWebImageOptions) options retry:(NSInteger) retry { +- (void)downloadImage:(FFFastImageSource *) source options:(SDWebImageOptions) options { __weak typeof(self) weakSelf = self; // Always use a weak reference to self in blocks [self sd_setImageWithURL:_source.url placeholderImage:nil @@ -173,22 +173,13 @@ - (void)downloadImage:(FFFastImageSource *) source options:(SDWebImageOptions) o SDImageCacheType cacheType, NSURL * _Nullable imageURL) { if (error) { - if (retry >= 3) { - weakSelf.hasErrored = YES; + weakSelf.hasErrored = YES; if (weakSelf.onFastImageError) { weakSelf.onFastImageError(@{}); } if (weakSelf.onFastImageLoadEnd) { weakSelf.onFastImageLoadEnd(@{}); } - } else { - // Auto-retry to download if failed - NSTimeInterval delayInSeconds = (retry + 1) * 5.0; // will retry after 0.5, 1.0 or 1.5 seconds - dispatch_time_t trigger = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); - dispatch_after(trigger, dispatch_get_main_queue(), ^{ - [weakSelf downloadImage:source options:options retry:retry + 1]; - }); - } } else { weakSelf.hasCompleted = YES; [weakSelf sendOnLoad:image];