From 44b6320dd925702f4e096658077e3e36e43c2edd Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 6 Jul 2016 20:30:42 -0400 Subject: [PATCH 1/9] [ios] Move cache.db to a subdirectory We cannot guarantee that cache.db will be created in a timely manner, so it's safer to set the backup exclusion key on its containing directory. But, because we placed cache.db in the base of the app's Application Support directory (where backups are expected to be allowed), we need to move cache.db into a directory that we control and can exclude from backups with impunity. --- platform/darwin/src/MGLOfflineStorage.mm | 130 +++++++++++++++-------- 1 file changed, 85 insertions(+), 45 deletions(-) diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index a59fb699439..f02196fb709 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -40,58 +40,98 @@ + (instancetype)sharedOfflineStorage { return sharedOfflineStorage; } +/** + Returns the file URL to the offline cache. + + 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 + */ ++ (NSURL *)cacheURL { + return [[self class] cacheURLIncludingSubdirectory:YES]; +} + +/** + Returns the file URL to the offline cache, with the option to omit the "mapbox" + subdirectory for legacy (v3.2.0 - v3.2.3) migration purposes. + + The subdirectory-less cache was located at: + ~/Library/Application Support/tld.app.bundle.id/cache.db + */ ++ (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 + 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] cacheURL]; + 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 *subdirectorylessCachePath = [[self class] cacheURLIncludingSubdirectory:NO]; + [[NSFileManager defaultManager] moveItemAtPath:subdirectorylessCachePath.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 @@ -262,4 +302,4 @@ - (void)offlinePack:(MGLOfflinePack *)pack didReceiveMaximumAllowedMapboxTiles:( }]; } -@end +@end \ No newline at end of file From 8c555c652f6be205a08bd6e024dbc24f2a7f7645 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 6 Jul 2016 20:44:00 -0400 Subject: [PATCH 2/9] Re-add bundle identifer string for macOS --- platform/darwin/src/MGLOfflineStorage.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index f02196fb709..928e127b6a7 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -100,6 +100,7 @@ + (NSString *)legacyCachePath { 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 From 5349c7e338e9317545b38345823aac5eacd9b62a Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 6 Jul 2016 21:02:50 -0400 Subject: [PATCH 3/9] Prefix mapbox subdirectory with dot, for extra safety --- platform/darwin/src/MGLOfflineStorage.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 928e127b6a7..2340ed0bad4 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -73,7 +73,7 @@ + (NSURL *)cacheURLIncludingSubdirectory:(BOOL)useSubdirectory { } cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier]; if (useSubdirectory) { - cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@"mapbox"]; + cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"]; } [[NSFileManager defaultManager] createDirectoryAtURL:cacheDirectoryURL withIntermediateDirectories:YES From 6a59addcafbf6dadc66ea8d6b8a248b6bb07c9f5 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 6 Jul 2016 21:04:03 -0400 Subject: [PATCH 4/9] Fix offline storage backup exclusion test --- platform/darwin/test/MGLOfflineStorageTests.m | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/darwin/test/MGLOfflineStorageTests.m b/platform/darwin/test/MGLOfflineStorageTests.m index 415039c527b..c4d1fe3243c 100644 --- a/platform/darwin/test/MGLOfflineStorageTests.m +++ b/platform/darwin/test/MGLOfflineStorageTests.m @@ -106,6 +106,7 @@ - (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]; + cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"]; XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheDirectoryURL.path], @"Cache directory should exist."); NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:@"cache.db"]; From 5b92cba186b2fd128114f78c9c7cd1aa2556cecf Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 6 Jul 2016 21:08:21 -0400 Subject: [PATCH 5/9] Consolidate cacheURL methods --- platform/darwin/src/MGLOfflineStorage.mm | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 2340ed0bad4..5215fdce73d 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -41,21 +41,14 @@ + (instancetype)sharedOfflineStorage { } /** - Returns the file URL to the offline cache. + 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 - */ -+ (NSURL *)cacheURL { - return [[self class] cacheURLIncludingSubdirectory:YES]; -} - -/** - Returns the file URL to the offline cache, with the option to omit the "mapbox" - subdirectory for legacy (v3.2.0 - v3.2.3) migration purposes. + ~/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 @@ -115,7 +108,7 @@ + (NSString *)legacyCachePath { - (instancetype)init { if (self = [super init]) { - NSURL *cacheURL = [[self class] cacheURL]; + 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 From 502381f74a9e325cfaa757f60a4ae6310737eed6 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 6 Jul 2016 21:34:17 -0400 Subject: [PATCH 6/9] Actually fix backup exclusion test ... it's late, I've been drinking. --- platform/darwin/test/MGLOfflineStorageTests.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/darwin/test/MGLOfflineStorageTests.m b/platform/darwin/test/MGLOfflineStorageTests.m index c4d1fe3243c..e2346c5f61b 100644 --- a/platform/darwin/test/MGLOfflineStorageTests.m +++ b/platform/darwin/test/MGLOfflineStorageTests.m @@ -107,17 +107,17 @@ - (void)testBackupExclusion { NSString *bundleIdentifier = [NSBundle bundleForClass:[MGLMapView class]].bundleIdentifier; cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier]; cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"]; - XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheDirectoryURL.path], @"Cache directory should exist."); + 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."); } From d94116440245b49bd97bf30876e53a3f291159c8 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 6 Jul 2016 21:51:56 -0400 Subject: [PATCH 7/9] Add newline --- platform/darwin/src/MGLOfflineStorage.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 5215fdce73d..6511bcd046d 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -296,4 +296,4 @@ - (void)offlinePack:(MGLOfflinePack *)pack didReceiveMaximumAllowedMapboxTiles:( }]; } -@end \ No newline at end of file +@end From 9ae6d83d0a2da78a3f638769c0afddf810e73083 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 7 Jul 2016 02:25:22 -0400 Subject: [PATCH 8/9] Rename variable to better match its type --- platform/darwin/src/MGLOfflineStorage.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 6511bcd046d..dd15920eab2 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -121,8 +121,8 @@ - (instancetype)init { // 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 *subdirectorylessCachePath = [[self class] cacheURLIncludingSubdirectory:NO]; - [[NSFileManager defaultManager] moveItemAtPath:subdirectorylessCachePath.path toPath:cachePath error:NULL]; + 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); From eaf2a7d402306a35af192140e1765babbd4f2c44 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 7 Jul 2016 11:24:43 -0400 Subject: [PATCH 9/9] Update changelog entry with new ticket, less speculation [skip ci] --- platform/ios/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 99d092dee96..3723fbd67b9 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -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))