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

Commit

Permalink
[ios, macos] Added Info.plist key to customize offline database path
Browse files Browse the repository at this point in the history
  • Loading branch information
1ec5 committed May 6, 2020
1 parent ecfe9bd commit 9e53135
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 52 deletions.
8 changes: 8 additions & 0 deletions platform/darwin/src/MGLOfflineStorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,19 @@ MGL_EXPORT

/**
The file path at which offline packs and the ambient cache are stored.
To customize this path, specify the
[`MGLOfflineStorageDatabasePath`](../infoplist-keys.html#mglofflinestoragedatabasepath)
key in Info.plist.
*/
@property (nonatomic, readonly, copy) NSString *databasePath;

/**
The file URL at which offline packs and the ambient cache are stored.
To customize this path, specify the
[`MGLOfflineStorageDatabasePath`](../infoplist-keys.html#mglofflinestoragedatabasepath)
key in Info.plist.
*/
@property (nonatomic, readonly, copy) NSURL *databaseURL;

Expand Down
119 changes: 69 additions & 50 deletions platform/darwin/src/MGLOfflineStorage.mm
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include <memory>

static NSString * const MGLOfflineStorageDatabasePathInfoDictionaryKey = @"MGLOfflineStorageDatabasePath";
static NSString * const MGLOfflineStorageFileName = @"cache.db";
static NSString * const MGLOfflineStorageFileName3_2_0_beta_1 = @"offline.db";

Expand All @@ -53,6 +54,7 @@ @interface MGLOfflineStorage ()
@end

@implementation MGLOfflineStorage {
NSURL *_databaseURL;
std::unique_ptr<mbgl::Actor<mbgl::ResourceTransform::TransformCallback>> _mbglResourceTransform;
}

Expand Down Expand Up @@ -161,26 +163,8 @@ - (instancetype)init {
MGLInitializeRunLoop();

if (self = [super init]) {
_databaseURL = [[self class] cacheURLIncludingSubdirectory:YES];
NSString *cachePath = self.databasePath;
NSAssert(cachePath, @"Offline pack database URL “%@” is not a valid file URL.", _databaseURL);

// Move the offline cache from v3.2.0-beta.1 to a location that can also
// be used for ambient caching.
if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath]) {
NSString *legacyCachePath = [[self class] legacyCachePath];
[[NSFileManager defaultManager] moveItemAtPath:legacyCachePath toPath:cachePath 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];
}

mbgl::ResourceOptions options;
options.withCachePath(cachePath.UTF8String)
options.withCachePath(self.databasePath.UTF8String)
.withAssetPath([NSBundle mainBundle].resourceURL.path.UTF8String);
_mbglFileSource = mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::ResourceLoader, options);
_mbglOnlineFileSource = mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Network, options);
Expand Down Expand Up @@ -238,70 +222,105 @@ - (NSString *)databasePath {
return self.databaseURL.path;
}

- (NSURL *)databaseURL {
if (!_databaseURL) {
NSString *customPath = [NSBundle.mainBundle objectForInfoDictionaryKey:MGLOfflineStorageDatabasePathInfoDictionaryKey];
if ([customPath isKindOfClass:[NSString class]]) {
_databaseURL = [NSURL fileURLWithPath:customPath.stringByStandardizingPath isDirectory:NO relativeToURL:NSBundle.mainBundle.resourceURL];

NSURL *directoryURL = _databaseURL.URLByDeletingLastPathComponent;
[NSFileManager.defaultManager createDirectoryAtURL:directoryURL
withIntermediateDirectories:YES
attributes:nil
error:nil];
} else {
_databaseURL = [[self class] defaultDatabaseURLIncludingSubdirectory:YES];
}
NSString *databasePath = self.databasePath;
NSAssert(databasePath, @"Offline pack database URL “%@” is not a valid file URL.", _databaseURL);

// Move the offline database from v3.2.0-beta.1 to a location that can
// also be used for ambient caching.
if (![[NSFileManager defaultManager] fileExistsAtPath:databasePath]) {
NSString *legacyDatabasePath = [[self class] legacyDatabasePath];
[[NSFileManager defaultManager] moveItemAtPath:legacyDatabasePath toPath:databasePath error:NULL];
}

// Move the offline database from v3.2.x path to a subdirectory that can
// be reliably excluded from backups.
if (![[NSFileManager defaultManager] fileExistsAtPath:databasePath]) {
NSURL *subdirectorylessDatabaseURL = [[self class] defaultDatabaseURLIncludingSubdirectory:NO];
[[NSFileManager defaultManager] moveItemAtPath:subdirectorylessDatabaseURL.path toPath:databasePath error:NULL];
}
}
return _databaseURL;
}

/**
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.
Returns the default file URL to the offline pack database, with the option to
omit the private subdirectory for legacy (v3.2.0v3.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 database 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:
The database is located at:
~/Library/Application Support/tld.app.bundle.id/.mapbox/cache.db
The subdirectory-less cache was located at:
The subdirectory-less database 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];
+ (NSURL *)defaultDatabaseURLIncludingSubdirectory:(BOOL)useSubdirectory {
NSURL *databaseDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:nil];
NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier];
if (!bundleIdentifier) {
// There’s no main bundle identifier when running in a unit test bundle.
bundleIdentifier = [[NSUUID UUID] UUIDString];
}
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
databaseDirectoryURL = [databaseDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
if (useSubdirectory) {
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"];
databaseDirectoryURL = [databaseDirectoryURL URLByAppendingPathComponent:@".mapbox"];
}
[[NSFileManager defaultManager] createDirectoryAtURL:cacheDirectoryURL
[[NSFileManager defaultManager] createDirectoryAtURL:databaseDirectoryURL
withIntermediateDirectories:YES
attributes:nil
error:nil];
if (useSubdirectory) {
// Avoid backing up the offline cache onto iCloud, because it can be
// Avoid backing up the database 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];
[databaseDirectoryURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:NULL];
}
return [cacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName];
return [databaseDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName];
}

/**
Returns the absolute path to the location where v3.2.0-beta.1 placed the
offline cache.
offline pack database.
*/
+ (NSString *)legacyCachePath {
+ (NSString *)legacyDatabasePath {
#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];
NSString *legacyDatabasePath = [legacyPaths.firstObject stringByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1];
#elif TARGET_OS_MAC
// ~/Library/Caches/tld.app.bundle.id/offline.db
NSString *bundleIdentifier = [NSBundle mgl_applicationBundleIdentifier];
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 : @"";
NSURL *legacyDatabaseDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:nil];
legacyDatabaseDirectoryURL = [legacyDatabaseDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
NSURL *legacyDatabaseURL = [legacyDatabaseDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1];
NSString *legacyDatabasePath = legacyDatabaseURL ? legacyDatabaseURL.path : @"";
#endif
return legacyCachePath;
return legacyDatabasePath;
}

- (void)addContentsOfFile:(NSString *)filePath withCompletionHandler:(MGLBatchedOfflinePackAdditionCompletionHandler)completion {
Expand Down Expand Up @@ -553,7 +572,7 @@ - (void)setMaximumAllowedMapboxTiles:(uint64_t)maximumCount {
_mbglDatabaseFileSource->setOfflineMapboxTileCountLimit(maximumCount);
}

#pragma mark - Ambient Cache management
#pragma mark - Ambient cache management

- (void)setMaximumAmbientCacheSize:(NSUInteger)cacheSize withCompletionHandler:(void (^)(NSError * _Nullable))completion {
_mbglDatabaseFileSource->setMaximumAmbientCacheSize(cacheSize, [&, completion](std::exception_ptr exception) {
Expand Down
2 changes: 1 addition & 1 deletion platform/ios/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
### Offline maps

* Added the `-[MGLOfflinePack setContext:completionHandler:]` method for replacing the data associated with an offline pack, such as a name. ([#288](https://github.com/mapbox/mapbox-gl-native-ios/pull/288))
* Added the `MGLOfflineStorage.databasePath` and `MGLOfflineStorage.databaseURL` properties. ([#298](https://github.com/mapbox/mapbox-gl-native-ios/pull/298))
* Added the `MGLOfflineStorage.databasePath` and `MGLOfflineStorage.databaseURL` properties to obtain the path of the database that contains offline packs and the ambient cache. To customize this path, set the `MGLOfflineStorageDatabasePath` in Info.plist. ([#298](https://github.com/mapbox/mapbox-gl-native-ios/pull/298))
* Fixed an error that occurred if your implementation of the `-[MGLOfflineStorageDelegate offlineStorage:URLForResourceOfKind:]` method returned a local file URL. ([mapbox/mapbox-gl-native#16428](https://github.com/mapbox/mapbox-gl-native/pull/16428))

### Other changes
Expand Down
10 changes: 10 additions & 0 deletions platform/ios/docs/guides/Info.plist Keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ This key can either be set to a single string or an array of strings, which the

To disable client-side rendering of CJK characters in favor of [server-side rendering](customizing-fonts.html#server-side-fonts), set this key to the Boolean value `NO`.

## MGLOfflineStorageDatabasePath

This key customizes the file path at which `MGLOfflineStorage` keeps the offline map database, which contains any offline packs as well as the ambient cache. Most applications should not need to customize this path; however, you could customize it to implement a migration path between different versions of your application.

The key is interpreted as either an absolute file path or a file path relative to the main bundle’s resource folder, resolving any tilde or symbolic link. The path must be writable. If a database does not exist at the path you specify, one will be created automatically.

An offline map database can consume a significant amount of the user’s bandwidth and iCloud storage due to iCloud backups. To exclude the database from backups, set the containing directory’s `NSURLIsExcludedFromBackupKey` resource property to the Boolean value `YES` using the `-[NSURL setResourceValue:forKey:error:]` method. The entire directory will be affected, not just the database file. If the user restores the application from a backup, your application will need to restore any offline packs that had been previously downloaded.

At runtime, you can obtain the value of this key using the `MGLOfflineStorage.databasePath` and `MGLOfflineStorage.databaseURL` properties.

## MGLCollisionBehaviorPre4_0

If this key is set to YES (`true`), collision detection is performed only between symbol style layers based on the same source, as in versions 2.0–3.7 of the Mapbox Maps SDK for iOS. In other words, symbols in an `MGLSymbolStyleLayer` based on one source (for example, an `MGLShapeSource`) may overlap with symbols in another layer that is based on a different source (such as the Mapbox Streets source). This is the case regardless of the `MGLSymbolStyleLayer.iconAllowsOverlap`, `MGLSymbolStyleLayer.iconIgnoresPlacement`, `MGLSymbolStyleLayer.textAllowsOverlap`, and `MGLSymbolStyleLayer.textIgnoresPlacement` properties.
Expand Down
2 changes: 1 addition & 1 deletion platform/macos/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
### Offline maps

* Added the `-[MGLOfflinePack setContext:completionHandler:]` method for replacing the data associated with an offline pack, such as a name. ([#288](https://github.com/mapbox/mapbox-gl-native-ios/pull/288))
* Added the `MGLOfflineStorage.databasePath` and `MGLOfflineStorage.databaseURL` properties. ([#298](https://github.com/mapbox/mapbox-gl-native-ios/pull/298))
* Added the `MGLOfflineStorage.databasePath` and `MGLOfflineStorage.databaseURL` properties to obtain the path of the database that contains offline packs and the ambient cache. To customize this path, set the `MGLOfflineStorageDatabasePath` in Info.plist. ([#298](https://github.com/mapbox/mapbox-gl-native-ios/pull/298))
* Fixed an error that occurred if your implementation of the `-[MGLOfflineStorageDelegate offlineStorage:URLForResourceOfKind:]` method returned a local file URL. ([mapbox/mapbox-gl-native#16428](https://github.com/mapbox/mapbox-gl-native/pull/16428))

### Other changes
Expand Down
10 changes: 10 additions & 0 deletions platform/macos/docs/guides/Info.plist Keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ This key can either be set to a single string or an array of strings, which the

To disable client-side rendering of CJK characters in favor of [server-side rendering](customizing-fonts.html#server-side-fonts), set this key to the Boolean value `NO`.

## MGLOfflineStorageDatabasePath

This key customizes the file path at which `MGLOfflineStorage` keeps the offline map database, which contains any offline packs as well as the ambient cache. Most applications should not need to customize this path; however, you could customize it to implement a migration path between different versions of your application.

The key is interpreted as either an absolute file path or a file path relative to the main bundle’s resource folder, resolving any tilde or symbolic link. The path must be writable. If a database does not exist at the path you specify, one will be created automatically.

An offline map database can consume a significant amount of the user’s bandwidth and iCloud storage due to iCloud backups. To exclude the database from backups, set the containing directory’s `NSURLIsExcludedFromBackupKey` resource property to the Boolean value `YES` using the `-[NSURL setResourceValue:forKey:error:]` method. The entire directory will be affected, not just the database file. If the user restores the application from a backup, your application will need to restore any offline packs that had been previously downloaded.

At runtime, you can obtain the value of this key using the `MGLOfflineStorage.databasePath` and `MGLOfflineStorage.databaseURL` properties.

## MGLCollisionBehaviorPre4_0

If this key is set to YES (`true`), collision detection is performed only between symbol style layers based on the same source, as in versions 0.1–0.7 of the Mapbox Maps SDK for iOS. In other words, symbols in an `MGLSymbolStyleLayer` based on one source (for example, an `MGLShapeSource`) may overlap with symbols in another layer that is based on a different source (such as the Mapbox Streets source). This is the case regardless of the `MGLSymbolStyleLayer.iconAllowsOverlap`, `MGLSymbolStyleLayer.iconIgnoresPlacement`, `MGLSymbolStyleLayer.textAllowsOverlap`, and `MGLSymbolStyleLayer.textIgnoresPlacement` properties.
Expand Down

0 comments on commit 9e53135

Please sign in to comment.