Description
Talking about ch24/iHotelApp/iHotelApp/ directory.
AppCache.m:
+(BOOL) isMenuItemsStale
{
...
NSString *archivePath = [[AppCache cacheDirectory] stringByAppendingPathComponent:@"MenuItems.archive"];
NSTimeInterval stalenessLevel = [[[[NSFileManager defaultManager] attributesOfItemAtPath:archivePath error:nil] fileModificationDate] timeIntervalSinceNow];
...
}
stalenessLevel
is computed from the time of the last modification of the corresponding data file. Thus, in order to have the correct time value, we need that every new data are written to local file as soon as we receive it from the remote engine in iHotelAppMenuViewController viewWillAppear
method. But this is not done sooner than in viewWillDisappear
method.
This "post-write" strategy is taken also in AppCache cacheData:toFile: method, where (see [leastRecentlyUsedCacheData writeToFile:archivePath atomically:YES];
) the data gets written to disk only after it can't remain in memory any more.
When combined with "pre-write" stalenessLevel
concept, some stale data are declared as not stale.
Fix is rather simple provided one caching strategy will be chosen as preferred.
Code excerpts follow.
iHotelAppMenuViewController:
-(void) viewWillAppear:(BOOL)animated {
self.menuItems = [AppCache getCachedMenuItems];
[self.tableView reloadData];
if([AppCache isMenuItemsStale] || !self.menuItems) {
[AppDelegate.engine fetchMenuItemsOnSucceeded:^(NSMutableArray *listOfModelBaseObjects) {
self.menuItems = listOfModelBaseObjects;
[self.tableView reloadData];
} onError:^(NSError *engineError) {
[UIAlertView showWithError:engineError];
}];
}
[super viewWillAppear:animated];
}
-(void) viewWillDisappear:(BOOL)animated {
[AppCache cacheMenuItems:self.menuItems];
[super viewWillDisappear:animated];
}
AppCache.m:
+(void) cacheData:(NSData*) data toFile:(NSString*) fileName
{
[memoryCache setObject:data forKey:fileName];
if([recentlyAccessedKeys containsObject:fileName])
{
[recentlyAccessedKeys removeObject:fileName];
}
[recentlyAccessedKeys insertObject:fileName atIndex:0];
if([recentlyAccessedKeys count] > kCacheMemoryLimit)
{
NSString *leastRecentlyUsedDataFilename = [recentlyAccessedKeys lastObject];
NSData *leastRecentlyUsedCacheData = [memoryCache objectForKey:leastRecentlyUsedDataFilename];
NSString *archivePath = [[AppCache cacheDirectory] stringByAppendingPathComponent:fileName];
[leastRecentlyUsedCacheData writeToFile:archivePath atomically:YES];
[recentlyAccessedKeys removeLastObject];
[memoryCache removeObjectForKey:leastRecentlyUsedDataFilename];
}
}
+(BOOL) isMenuItemsStale
{
// if it is in memory cache, it is not stale
if([recentlyAccessedKeys containsObject:@"MenuItems.archive"])
return NO;
NSString *archivePath = [[AppCache cacheDirectory] stringByAppendingPathComponent:@"MenuItems.archive"];
NSTimeInterval stalenessLevel = [[[[NSFileManager defaultManager] attributesOfItemAtPath:archivePath error:nil] fileModificationDate] timeIntervalSinceNow];
return stalenessLevel > kMenuStaleSeconds;
}