Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[ios] Move cache.db to a subdirectory #5601

Merged
merged 9 commits into from
Jul 7, 2016
122 changes: 78 additions & 44 deletions platform/darwin/src/MGLOfflineStorage.mm
Original file line number Diff line number Diff line change
Expand Up @@ -40,58 +40,92 @@ + (instancetype)sharedOfflineStorage {
return sharedOfflineStorage;
}

/**
Returns the file URL to the offline cache, with the option to omit the private
subdirectory for legacy (v3.2.0 - v3.2.3) migration purposes.

The cache is located in a directory specific to the application, so that packs
downloaded by other applications don’t count toward this application’s limits.

The cache is located at:
~/Library/Application Support/tld.app.bundle.id/.mapbox/cache.db

The subdirectory-less cache was located at:
~/Library/Application Support/tld.app.bundle.id/cache.db
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also mention the new path. This comment is handy to link to in tickets.

*/
+ (NSURL *)cacheURLIncludingSubdirectory:(BOOL)useSubdirectory {
NSURL *cacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:nil];
NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
if (!bundleIdentifier) {
// There’s no main bundle identifier when running in a unit test bundle.
bundleIdentifier = [NSBundle bundleForClass:self].bundleIdentifier;
}
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
if (useSubdirectory) {
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"];
}
[[NSFileManager defaultManager] createDirectoryAtURL:cacheDirectoryURL
withIntermediateDirectories:YES
attributes:nil
error:nil];
if (useSubdirectory) {
// Avoid backing up the offline cache onto iCloud, because it can be
// redownloaded. Ideally, we’d even put the ambient cache in Caches, so
// it can be reclaimed by the system when disk space runs low. But
// unfortunately it has to live in the same file as offline resources.
[cacheDirectoryURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:NULL];
}
return [cacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName];
}

/**
Returns the absolute path to the location where v3.2.0-beta.1 placed the
offline cache.
*/
+ (NSString *)legacyCachePath {
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
// ~/Documents/offline.db
NSArray *legacyPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *legacyCachePath = [legacyPaths.firstObject stringByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1];
#elif TARGET_OS_MAC
// ~/Library/Caches/tld.app.bundle.id/offline.db
NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
NSURL *legacyCacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:nil];
legacyCacheDirectoryURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
NSURL *legacyCacheURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1];
NSString *legacyCachePath = legacyCacheURL ? legacyCacheURL.path : @"";
#endif
return legacyCachePath;
}

- (instancetype)init {
if (self = [super init]) {
// Place the cache in a location specific to the application, so that
// packs downloaded by other applications don’t count toward this
// application’s limits.
// ~/Library/Application Support/tld.app.bundle.id/cache.db
NSURL *cacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:nil];
NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
if (!bundleIdentifier) {
// There’s no main bundle identifier when running in a unit test bundle.
bundleIdentifier = [NSBundle bundleForClass:[self class]].bundleIdentifier;
}
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
[[NSFileManager defaultManager] createDirectoryAtURL:cacheDirectoryURL
withIntermediateDirectories:YES
attributes:nil
error:nil];
NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName];
NSString *cachePath = cacheURL ? cacheURL.path : @"";

NSURL *cacheURL = [[self class] cacheURLIncludingSubdirectory:YES];
NSString *cachePath = cacheURL.path ?: @"";

// Move the offline cache from v3.2.0-beta.1 to a location that can also
// be used for ambient caching.
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
// ~/Documents/offline.db
NSArray *legacyPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *legacyCachePath = [legacyPaths.firstObject stringByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1];
#elif TARGET_OS_MAC
// ~/Library/Caches/tld.app.bundle.id/offline.db
NSURL *legacyCacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:nil];
legacyCacheDirectoryURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
NSURL *legacyCacheURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1];
NSString *legacyCachePath = legacyCacheURL ? legacyCacheURL.path : @"";
#endif
if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath]) {
NSString *legacyCachePath = [[self class] legacyCachePath];
[[NSFileManager defaultManager] moveItemAtPath:legacyCachePath toPath:cachePath error:NULL];
}

_mbglFileSource = new mbgl::DefaultFileSource(cachePath.UTF8String, [NSBundle mainBundle].resourceURL.path.UTF8String);

// Avoid backing up the offline cache onto iCloud, because it can be
// redownloaded. Ideally, we’d even put the ambient cache in Caches, so
// it can be reclaimed by the system when disk space runs low. But
// unfortunately it has to live in the same file as offline resources.
[cacheURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:NULL];
// Move the offline file cache from v3.2.x path to a subdirectory that
// can be reliably excluded from backups.
if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath]) {
NSURL *subdirectorylessCacheURL = [[self class] cacheURLIncludingSubdirectory:NO];
[[NSFileManager defaultManager] moveItemAtPath:subdirectorylessCacheURL.path toPath:cachePath error:NULL];
}

_mbglFileSource = new mbgl::DefaultFileSource(cachePath.UTF8String, [NSBundle mainBundle].resourceURL.path.UTF8String);

// Observe for changes to the global access token (and find out the current one).
[[MGLAccountManager sharedManager] addObserver:self
Expand Down
11 changes: 6 additions & 5 deletions platform/darwin/test/MGLOfflineStorageTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -106,17 +106,18 @@ - (void)testBackupExclusion {
// Unit tests don't use the main bundle; use com.mapbox.ios.sdk instead.
NSString *bundleIdentifier = [NSBundle bundleForClass:[MGLMapView class]].bundleIdentifier;
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheDirectoryURL.path], @"Cache directory should exist.");
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"];
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheDirectoryURL.path], @"Cache subdirectory should exist.");

NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:@"cache.db"];
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheURL.path], @"Cache database should exist.");

NSError *error = nil;
NSNumber *exclusionFlag = nil;
[cacheURL getResourceValue:&exclusionFlag
forKey:NSURLIsExcludedFromBackupKey
error:&error];
XCTAssertTrue(exclusionFlag && [exclusionFlag boolValue], @"Backup exclusion flag should be set for cache database.");
[cacheDirectoryURL getResourceValue:&exclusionFlag
forKey:NSURLIsExcludedFromBackupKey
error:&error];
XCTAssertTrue(exclusionFlag && [exclusionFlag boolValue], @"Backup exclusion flag should be set for the directory containing the cache database.");
XCTAssertNil(error, @"No errors should be returned when checking backup exclusion flag.");
}

Expand Down
2 changes: 1 addition & 1 deletion platform/ios/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CON
### Offline maps

- `MGLOfflinePackProgress` now indicates how many tiles have been downloaded and how much space they take up. ([#4874](https://github.com/mapbox/mapbox-gl-native/pull/4874))
- Fixed an issue (speculatively) where the tile cache could be included in iCloud backups. ([#5124](https://github.com/mapbox/mapbox-gl-native/pull/5124))
- Fixed an issue where the tile cache could be included in iCloud backups on the first launch. ([#5124](https://github.com/mapbox/mapbox-gl-native/pull/5124), [#5601](https://github.com/mapbox/mapbox-gl-native/pull/5601))
- Suppressed “Unable to make space for entry” console spew. ([#4708](https://github.com/mapbox/mapbox-gl-native/pull/4708))
- Deprecated `-[MGLMapView emptyMemoryCache]`. ([#4725](https://github.com/mapbox/mapbox-gl-native/pull/4725))

Expand Down