Skip to content

Commit

Permalink
Fixed offline images not appearing
Browse files Browse the repository at this point in the history
- Adjusted the timeout and caching of images.
- Cleans up cached image URLs if needed.
- When offline, now replaces an uncached image with a blank image, to avoid the broken image.
- When online, now loads the remote image if no cached image.
- Experimented with using cleaner cache filenames, but that wasn’t needed (it initially looked like the MD5 was changing between invocations).
- Now uses “jpeg” for the cache extension, since the image URL can have parameter junk that messes with caching.
  • Loading branch information
Dejal committed Sep 20, 2024
1 parent cdaec24 commit a4f827d
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 12 deletions.
2 changes: 1 addition & 1 deletion clients/ios/Classes/NewsBlurAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -4738,7 +4738,7 @@ - (void)prepareActiveCachedImages:(FMDatabase *)db {
feedIds = @[[storiesCollection.activeFeed objectForKey:@"id"]];
}
NSString *sql = [NSString stringWithFormat:@"SELECT c.image_url, c.story_hash FROM cached_images c "
"WHERE c.image_cached = 1 AND c.failed is null AND c.story_feed_id in (\"%@\")",
"WHERE c.image_cached = 1 AND c.story_feed_id in (\"%@\")",
[feedIds componentsJoinedByString:@"\",\""]];
FMResultSet *cursor = [db executeQuery:sql];

Expand Down
22 changes: 20 additions & 2 deletions clients/ios/Classes/StoryDetailObjCViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -515,20 +515,38 @@ - (void)drawStory:(BOOL)force withOrientation:(UIInterfaceOrientation)orientatio
contentWidthClass, (int)floorf(CGRectGetWidth(self.view.frame))];

// if (appDelegate.feedsViewController.isOffline) {
NSFileManager *manager = [NSFileManager defaultManager];
NSString *storyHash = [self.activeStory objectForKey:@"story_hash"];
NSArray *imageUrls = [appDelegate.activeCachedImages objectForKey:storyHash];
// NSLog(@"📚 imageUrls: %@", imageUrls);
NSLog(@"📚 %@ %@ imageUrls: %@", activeStory[@"story_title"], storyHash, imageUrls);
if (imageUrls) {
NSString *storyImagesDirectory = [appDelegate.documentsURL.path
stringByAppendingPathComponent:@"story_images"];
for (NSString *imageUrl in imageUrls) {
NSURL *cachedUrl = [NSURL fileURLWithPath:storyImagesDirectory];
// cachedUrl = [cachedUrl URLByAppendingPathComponent:[Utilities md5:imageUrl storyHash:storyHash]];
cachedUrl = [cachedUrl URLByAppendingPathComponent:[Utilities md5:imageUrl]];
cachedUrl = [cachedUrl URLByAppendingPathExtension:imageUrl.pathExtension];
cachedUrl = [cachedUrl URLByAppendingPathExtension:@"jpeg"];

if (![manager fileExistsAtPath:cachedUrl.path]) {
if (appDelegate.feedsViewController.isOffline) {
cachedUrl = [[NSBundle mainBundle] URLForResource:@"blank" withExtension:@"png"];
} else {
continue;
}
}

NSLog(@"📚 %@ %@ imageURL: %@ cachedURL: %@", activeStory[@"story_title"], storyHash, imageUrl, cachedUrl);

storyContent = [storyContent
stringByReplacingOccurrencesOfString:imageUrl
withString:cachedUrl.absoluteString];

NSString *escapedURL = [imageUrl stringByEncodingHTMLEntities];

storyContent = [storyContent
stringByReplacingOccurrencesOfString:escapedURL
withString:cachedUrl.absoluteString];
}
}
// }
Expand Down
1 change: 1 addition & 0 deletions clients/ios/Classes/Utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor
+ (UIImage *)templateImageNamed:(NSString *)imageName sized:(CGFloat)size;
+ (UIImage *)imageNamed:(NSString *)imageName sized:(CGFloat)size;
+ (UIImage *)imageWithImage:(UIImage *)image convertToSize:(CGSize)size;
//+ (NSString *)md5:(NSString *)string storyHash:(NSString *)storyHash;
+ (NSString *)md5:(NSString *)string;
+ (NSString *)formatLongDateFromTimestamp:(NSInteger)timestamp;
+ (NSString *)formatShortDateFromTimestamp:(NSInteger)timestamp;
Expand Down
26 changes: 26 additions & 0 deletions clients/ios/Classes/Utilities.m
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,32 @@ + (UIImage *)imageWithImage:(UIImage *)image convertToSize:(CGSize)size {
return destImage;
}

// These methods were an experiment in replacing the offline image filenames while tracing the images not appearing; an improvement, but skip for now; keep for future consideration.

//+ (NSString *)removeIllegalCharactersForFilename:(NSString *)filename {
// NSCharacterSet *illegalCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"\\/:"];
// NSString *cleanedFilename = [[filename componentsSeparatedByCharactersInSet:illegalCharacterSet] componentsJoinedByString:@"-"];
//
// return cleanedFilename;
//}
//
//+ (NSUInteger)checksum:(NSString *)string {
// NSUInteger base = string.length;
// NSUInteger result = base * base;
//
// for (NSUInteger i = 0; i < string.length; i++) {
// result = (result + ([string characterAtIndex:i] * (i + 34)) + (732 * i) + (base * (i + 83))) % 999999999;
// }
//
// return result;
//}
//
//+ (NSString *)md5:(NSString *)string storyHash:(NSString *)storyHash {
// NSUInteger checksum = [self checksum:string];
// NSString *cleanedStoryHash = [self removeIllegalCharactersForFilename:storyHash];
// return [NSString stringWithFormat:@"%@-%@-%@", cleanedStoryHash, @(checksum), [self md5:string]];
//}

+ (NSString *)md5:(NSString *)string {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Expand Down
21 changes: 13 additions & 8 deletions clients/ios/Classes/offline/OfflineFetchImages.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,34 +58,36 @@ - (BOOL)fetchImages {
});
return NO;
}


NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
[manager.requestSerializer setTimeoutInterval:5];
[manager.requestSerializer setTimeoutInterval:10];
manager.responseSerializer = [AFImageResponseSerializer serializer];
manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

dispatch_group_t group = dispatch_group_create();

for (NSArray *urlArray in urls) {
NSString *urlString = [[urlArray objectAtIndex:0] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSString *urlString = [urlArray objectAtIndex:0];
NSString *storyHash = [urlArray objectAtIndex:1];
NSInteger storyTimestamp = [[urlArray objectAtIndex:2] integerValue];
dispatch_group_enter(group);
// NSLog(@" ---> Fetching offline image: %@", urlString);
if (![NSURL URLWithString:urlString]) {
urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
}
NSLog(@"📚 Fetching offline %@ image: %@", storyHash, urlString);
[manager GET:urlString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
// NSLog(@" ---> Fetched %@: %@", storyHash, urlString);
NSLog(@"📚 Fetched %@: %@", storyHash, urlString);
UIImage *image = (UIImage *)responseObject;
[self storeCachedImage:urlString withImage:image storyHash:storyHash storyTimestamp:storyTimestamp];
dispatch_group_leave(group);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
// NSLog(@" ---> Failed to fetch image %@: %@", storyHash, urlString);
NSLog(@"📚 Failed to fetch image %@: %@ %@", storyHash, urlString, error);
[self storeFailedImage:storyHash];
dispatch_group_leave(group);
}];
}

// dispatch_sync(dispatch_get_main_queue(), ^{
// [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
// });
Expand Down Expand Up @@ -167,16 +169,19 @@ - (void)storeCachedImage:(NSString *)imageUrl withImage:(UIImage *)image storyHa
(unsigned long)NULL), ^{

NSData *responseData = UIImageJPEGRepresentation(image, 0.6);
// NSString *md5Url = [Utilities md5:imageUrl storyHash:storyHash];
NSString *md5Url = [Utilities md5:imageUrl];
// NSLog(@"Storing image: %@ (%d bytes - %d in queue)", storyHash, [responseData length], [imageDownloadOperationQueue requestsCount]);

NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"story_images"];
NSString *fullPath = [cacheDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", md5Url, [imageUrl pathExtension]]];
NSString *fullPath = [[cacheDirectory stringByAppendingPathComponent:md5Url] stringByAppendingPathExtension:@"jpeg"];

[fileManager createFileAtPath:fullPath contents:responseData attributes:nil];

NSLog(@"📚 stored storyHash: %@ imageURL: %@ cachedURL: %@", storyHash, imageUrl, fullPath);

[self.appDelegate.database inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"UPDATE cached_images SET "
"image_cached = 1 WHERE story_hash = ?",
Expand Down
6 changes: 6 additions & 0 deletions clients/ios/NewsBlur.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,8 @@
177551DF238E228A00E27818 /* Old NewsBlur Latest.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 177551D3238E228A00E27818 /* Old NewsBlur Latest.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
177D017D28B05D9500F2F2DB /* StoryPagesCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 177D017C28B05D9500F2F2DB /* StoryPagesCollectionCell.swift */; };
17813FB723AC6E450057FB16 /* WidgetErrorTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17CE3F0523AC529B003152EF /* WidgetErrorTableViewCell.xib */; };
1781B97F2C9D332B00C8F344 /* blank.png in Resources */ = {isa = PBXBuildFile; fileRef = 1781B97E2C9D332B00C8F344 /* blank.png */; };
1781B9802C9D332B00C8F344 /* blank.png in Resources */ = {isa = PBXBuildFile; fileRef = 1781B97E2C9D332B00C8F344 /* blank.png */; };
178552492A1F115800A8CD92 /* FeedDetailTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 178552472A1F115800A8CD92 /* FeedDetailTableCell.m */; };
1785524A2A1F115800A8CD92 /* FeedDetailTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 178552472A1F115800A8CD92 /* FeedDetailTableCell.m */; };
1785524C2A21693300A8CD92 /* FeedDetailLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1785524B2A21693300A8CD92 /* FeedDetailLoadingView.swift */; };
Expand Down Expand Up @@ -1547,6 +1549,7 @@
177551DC238E228A00E27818 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
177551E3238E26BF00E27818 /* Widget Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Widget Extension.entitlements"; sourceTree = "<group>"; };
177D017C28B05D9500F2F2DB /* StoryPagesCollectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryPagesCollectionCell.swift; sourceTree = "<group>"; };
1781B97E2C9D332B00C8F344 /* blank.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = blank.png; sourceTree = "<group>"; };
178552472A1F115800A8CD92 /* FeedDetailTableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FeedDetailTableCell.m; sourceTree = "<group>"; };
178552482A1F115800A8CD92 /* FeedDetailTableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FeedDetailTableCell.h; sourceTree = "<group>"; };
1785524B2A21693300A8CD92 /* FeedDetailLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedDetailLoadingView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2878,6 +2881,7 @@
FFB1F69C1FBD1B2D001DA171 /* white_fade.png */,
FF9D59331FBC08DB00823C10 /* Shiloh.jpg */,
17E2AEB22863FECF000FAB5B /* Lyric.jpg */,
1781B97E2C9D332B00C8F344 /* blank.png */,
FF3FA8941BB26B6A001F7C32 /* [email protected] */,
FF83FF121FB54692008DAC0F /* g_icn_buffer.png */,
FF83FF0E1FB54691008DAC0F /* g_icn_eating.png */,
Expand Down Expand Up @@ -4441,6 +4445,7 @@
175792042930605500490924 /* theme_color_dark-sel.png in Resources */,
175792052930605500490924 /* [email protected] in Resources */,
175792062930605500490924 /* g_icn_offline.png in Resources */,
1781B9802C9D332B00C8F344 /* blank.png in Resources */,
175792072930605500490924 /* triangle.png in Resources */,
175792082930605500490924 /* [email protected] in Resources */,
175792092930605500490924 /* [email protected] in Resources */,
Expand Down Expand Up @@ -4867,6 +4872,7 @@
FF83FF1A1FB54693008DAC0F /* g_icn_buffer.png in Resources */,
17E2AEDD286556CC000FAB5B /* icons8-security-wi-fi-100.png in Resources */,
17432C7A1C533DCC003F8FD6 /* FeedChooserViewController.xib in Resources */,
1781B97F2C9D332B00C8F344 /* blank.png in Resources */,
FFDD847F16E887D3000AA0A2 /* [email protected] in Resources */,
1740C6881C10FD75005EA453 /* theme_color_dark.png in Resources */,
FFDD848016E887D3000AA0A2 /* g_icn_hidden.png in Resources */,
Expand Down
2 changes: 1 addition & 1 deletion clients/ios/Other Sources/NBURLCache.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ - (NSString *)substitutePath:(NSString *)pathString {
NSString *storyImagesDirectory = [[paths objectAtIndex:0]
stringByAppendingPathComponent:@"story_images"];
NSString *cachedImage = [[storyImagesDirectory
stringByAppendingPathComponent:[Utilities md5:pathString]] stringByAppendingPathExtension:[pathString pathExtension]];
stringByAppendingPathComponent:[Utilities md5:pathString]] stringByAppendingPathExtension:@"jpeg"];
return cachedImage;
}

Expand Down
Binary file added clients/ios/Resources/blank.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a4f827d

Please sign in to comment.