summaryrefslogtreecommitdiff
path: root/platform/darwin/src/MGLOfflineStorage.mm
diff options
context:
space:
mode:
Diffstat (limited to 'platform/darwin/src/MGLOfflineStorage.mm')
-rw-r--r--platform/darwin/src/MGLOfflineStorage.mm652
1 files changed, 0 insertions, 652 deletions
diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm
deleted file mode 100644
index 4c71286b79..0000000000
--- a/platform/darwin/src/MGLOfflineStorage.mm
+++ /dev/null
@@ -1,652 +0,0 @@
-#import "MGLOfflineStorage_Private.h"
-
-#import "MGLFoundation_Private.h"
-#import "MGLAccountManager_Private.h"
-#import "MGLGeometry_Private.h"
-#import "MGLOfflinePack_Private.h"
-#import "MGLOfflineRegion_Private.h"
-#import "MGLTilePyramidOfflineRegion.h"
-#import "MGLShapeOfflineRegion.h"
-#import "NSBundle+MGLAdditions.h"
-#import "NSValue+MGLAdditions.h"
-#import "NSDate+MGLAdditions.h"
-#import "MGLLoggingConfiguration_Private.h"
-
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
-#import "MMEConstants.h"
-#import "MGLMapboxEvents.h"
-#endif
-
-#include <mbgl/actor/actor.hpp>
-#include <mbgl/actor/scheduler.hpp>
-#include <mbgl/storage/file_source_manager.hpp>
-#include <mbgl/storage/resource_options.hpp>
-#include <mbgl/storage/resource_transform.hpp>
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/run_loop.hpp>
-#include <mbgl/util/string.hpp>
-
-#include <memory>
-
-static NSString * const MGLOfflineStorageFileName = @"cache.db";
-static NSString * const MGLOfflineStorageFileName3_2_0_beta_1 = @"offline.db";
-
-const NSNotificationName MGLOfflinePackProgressChangedNotification = @"MGLOfflinePackProgressChanged";
-const NSNotificationName MGLOfflinePackErrorNotification = @"MGLOfflinePackError";
-const NSNotificationName MGLOfflinePackMaximumMapboxTilesReachedNotification = @"MGLOfflinePackMaximumMapboxTilesReached";
-
-const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyState = @"State";
-const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyProgress = @"Progress";
-const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyError = @"Error";
-const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"MaximumCount";
-
-const MGLExceptionName MGLUnsupportedRegionTypeException = @"MGLUnsupportedRegionTypeException";
-
-@interface MGLOfflineStorage ()
-
-@property (nonatomic, strong, readwrite) NSMutableArray<MGLOfflinePack *> *packs;
-@property (nonatomic) std::shared_ptr<mbgl::DatabaseFileSource> mbglDatabaseFileSource;
-@property (nonatomic) std::shared_ptr<mbgl::FileSource> mbglOnlineFileSource;
-@property (nonatomic) std::shared_ptr<mbgl::FileSource> mbglFileSource;
-@property (nonatomic) std::string mbglCachePath;
-@property (nonatomic, getter=isPaused) BOOL paused;
-@end
-
-@implementation MGLOfflineStorage {
- std::unique_ptr<mbgl::Actor<mbgl::ResourceTransform::TransformCallback>> _mbglResourceTransform;
-}
-
-+ (instancetype)sharedOfflineStorage {
- static dispatch_once_t onceToken;
- static MGLOfflineStorage *sharedOfflineStorage;
- dispatch_once(&onceToken, ^{
- sharedOfflineStorage = [[self alloc] init];
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- [[NSNotificationCenter defaultCenter] addObserver:sharedOfflineStorage selector:@selector(unpauseFileSource:) name:UIApplicationWillEnterForegroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:sharedOfflineStorage selector:@selector(pauseFileSource:) name:UIApplicationDidEnterBackgroundNotification object:nil];
-#endif
- [sharedOfflineStorage reloadPacks];
- });
-
- return sharedOfflineStorage;
-}
-
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
-- (void)pauseFileSource:(__unused NSNotification *)notification {
- if (self.isPaused) {
- return;
- }
- _mbglFileSource->pause();
- self.paused = YES;
-}
-
-- (void)unpauseFileSource:(__unused NSNotification *)notification {
- if (!self.isPaused) {
- return;
- }
- _mbglFileSource->resume();
- self.paused = NO;
-}
-#endif
-
-- (void)setDelegate:(id<MGLOfflineStorageDelegate>)newValue {
- MGLLogDebug(@"Setting delegate: %@", newValue);
- _delegate = newValue;
- if ([self.delegate respondsToSelector:@selector(offlineStorage:URLForResourceOfKind:withURL:)]) {
- _mbglResourceTransform = std::make_unique<mbgl::Actor<mbgl::ResourceTransform::TransformCallback>>(*mbgl::Scheduler::GetCurrent(), [offlineStorage = self](auto kind_, const std::string& url_, mbgl::ResourceTransform::FinishedCallback cb) {
- NSURL* url =
- [NSURL URLWithString:[[NSString alloc] initWithBytes:url_.data()
- length:url_.length()
- encoding:NSUTF8StringEncoding]];
- MGLResourceKind kind = MGLResourceKindUnknown;
- switch (kind_) {
- case mbgl::Resource::Kind::Tile:
- kind = MGLResourceKindTile;
- break;
- case mbgl::Resource::Kind::Glyphs:
- kind = MGLResourceKindGlyphs;
- break;
- case mbgl::Resource::Kind::Style:
- kind = MGLResourceKindStyle;
- break;
- case mbgl::Resource::Kind::Source:
- kind = MGLResourceKindSource;
- break;
- case mbgl::Resource::Kind::SpriteImage:
- kind = MGLResourceKindSpriteImage;
- break;
- case mbgl::Resource::Kind::SpriteJSON:
- kind = MGLResourceKindSpriteJSON;
- break;
- case mbgl::Resource::Kind::Image:
- kind = MGLResourceKindImage;
- break;
- case mbgl::Resource::Kind::Unknown:
- kind = MGLResourceKindUnknown;
- break;
-
- }
- url = [offlineStorage.delegate offlineStorage:offlineStorage
- URLForResourceOfKind:kind
- withURL:url];
- cb(url.absoluteString.UTF8String);
- });
-
- _mbglOnlineFileSource->setResourceTransform({[actorRef = _mbglResourceTransform->self()](auto kind_, const std::string& url_, mbgl::ResourceTransform::FinishedCallback cb_){
- actorRef.invoke(&mbgl::ResourceTransform::TransformCallback::operator(), kind_, url_, std::move(cb_));
- }});
- } else {
- _mbglResourceTransform.reset();
- _mbglOnlineFileSource->setResourceTransform({});
- }
-}
-
-/**
- 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
- */
-+ (NSURL *)cacheURLIncludingSubdirectory:(BOOL)useSubdirectory {
- NSURL *cacheDirectoryURL = [[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];
- 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 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 : @"";
-#endif
- return legacyCachePath;
-}
-
-- (instancetype)init {
- MGLInitializeRunLoop();
-
- if (self = [super init]) {
- 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 (![[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];
- }
-
- _mbglCachePath = cachePath.UTF8String;
- mbgl::ResourceOptions options;
- options.withCachePath(_mbglCachePath)
- .withAssetPath([NSBundle mainBundle].resourceURL.path.UTF8String);
- _mbglFileSource = mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::ResourceLoader, options);
- _mbglOnlineFileSource = mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Network, options);
- _mbglDatabaseFileSource = std::static_pointer_cast<mbgl::DatabaseFileSource>(std::shared_ptr<mbgl::FileSource>(mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Database, options)));
-
- // Observe for changes to the API base URL (and find out the current one).
- [[MGLAccountManager sharedManager] addObserver:self
- forKeyPath:@"apiBaseURL"
- options:(NSKeyValueObservingOptionInitial |
- NSKeyValueObservingOptionNew)
- context:NULL];
-
- // Observe for changes to the global access token (and find out the current one).
- [[MGLAccountManager sharedManager] addObserver:self
- forKeyPath:@"accessToken"
- options:(NSKeyValueObservingOptionInitial |
- NSKeyValueObservingOptionNew)
- context:NULL];
- }
- return self;
-}
-
-- (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- [[MGLAccountManager sharedManager] removeObserver:self forKeyPath:@"apiBaseURL"];
- [[MGLAccountManager sharedManager] removeObserver:self forKeyPath:@"accessToken"];
-
- for (MGLOfflinePack *pack in self.packs) {
- [pack invalidate];
- }
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context {
- // Synchronize the file source’s access token with the global one in MGLAccountManager.
- if ([keyPath isEqualToString:@"accessToken"] && object == [MGLAccountManager sharedManager]) {
- NSString *accessToken = change[NSKeyValueChangeNewKey];
- if (![accessToken isKindOfClass:[NSNull class]]) {
- _mbglOnlineFileSource->setProperty(mbgl::ACCESS_TOKEN_KEY, accessToken.UTF8String);
- }
- } else if ([keyPath isEqualToString:@"apiBaseURL"] && object == [MGLAccountManager sharedManager]) {
- NSURL *apiBaseURL = change[NSKeyValueChangeNewKey];
- if ([apiBaseURL isKindOfClass:[NSNull class]]) {
- _mbglFileSource->setProperty(mbgl::API_BASE_URL_KEY, mbgl::util::API_BASE_URL);
- } else {
- _mbglFileSource->setProperty(mbgl::API_BASE_URL_KEY, apiBaseURL.absoluteString.UTF8String);
- }
- } else {
- [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
- }
-}
-
-#pragma mark Offline merge methods
-
-- (void)addContentsOfFile:(NSString *)filePath withCompletionHandler:(MGLBatchedOfflinePackAdditionCompletionHandler)completion {
- MGLLogDebug(@"Adding contentsOfFile: %@ completionHandler: %@", filePath, completion);
- NSURL *fileURL = [NSURL fileURLWithPath:filePath];
-
- [self addContentsOfURL:fileURL withCompletionHandler:completion];
-
-}
-
-- (void)addContentsOfURL:(NSURL *)fileURL withCompletionHandler:(MGLBatchedOfflinePackAdditionCompletionHandler)completion {
- MGLLogDebug(@"Adding contentsOfURL: %@ completionHandler: %@", fileURL, completion);
- NSFileManager *fileManager = [NSFileManager defaultManager];
-
- if (!fileURL.isFileURL) {
- [NSException raise:NSInvalidArgumentException format:@"%@ must be a valid file path", fileURL.absoluteString];
- }
- if (![fileManager isWritableFileAtPath:fileURL.path]) {
- [NSException raise:NSInvalidArgumentException format:@"The file path: %@ must be writable", fileURL.absoluteString];
- }
-
- __weak MGLOfflineStorage *weakSelf = self;
- [self _addContentsOfFile:fileURL.path withCompletionHandler:^(NSArray<MGLOfflinePack *> * _Nullable packs, NSError * _Nullable error) {
- if (packs) {
- NSMutableDictionary *packsByIdentifier = [NSMutableDictionary dictionary];
-
- MGLOfflineStorage *strongSelf = weakSelf;
- for (MGLOfflinePack *pack in packs) {
- [packsByIdentifier setObject:pack forKey:@(pack.mbglOfflineRegion->getID())];
- }
-
- id mutablePacks = [strongSelf mutableArrayValueForKey:@"packs"];
- NSMutableIndexSet *replaceIndexSet = [NSMutableIndexSet indexSet];
- NSMutableArray *replacePacksArray = [NSMutableArray array];
- [strongSelf.packs enumerateObjectsUsingBlock:^(MGLOfflinePack * _Nonnull pack, NSUInteger idx, BOOL * _Nonnull stop) {
- MGLOfflinePack *newPack = packsByIdentifier[@(pack.mbglOfflineRegion->getID())];
- if (newPack) {
- MGLOfflinePack *previousPack = [mutablePacks objectAtIndex:idx];
- [previousPack invalidate];
- [replaceIndexSet addIndex:idx];
- [replacePacksArray addObject:[packsByIdentifier objectForKey:@(newPack.mbglOfflineRegion->getID())]];
- [packsByIdentifier removeObjectForKey:@(newPack.mbglOfflineRegion->getID())];
- }
-
- }];
-
- if (replaceIndexSet.count > 0) {
- [mutablePacks replaceObjectsAtIndexes:replaceIndexSet withObjects:replacePacksArray];
- }
-
- [mutablePacks addObjectsFromArray:packsByIdentifier.allValues];
- }
- if (completion) {
- completion(fileURL, packs, error);
- }
- }];
-}
-
-- (void)_addContentsOfFile:(NSString *)filePath withCompletionHandler:(void (^)(NSArray<MGLOfflinePack *> * _Nullable packs, NSError * _Nullable error))completion {
- _mbglDatabaseFileSource->mergeOfflineRegions(std::string(static_cast<const char *>([filePath UTF8String])), [&, completion, filePath](mbgl::expected<mbgl::OfflineRegions, std::exception_ptr> result) {
- NSError *error;
- NSMutableArray *packs;
- if (!result) {
- NSString *description = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"ADD_FILE_CONTENTS_FAILED_DESC", @"Foundation", nil, @"Unable to add offline packs from the file at %@.", @"User-friendly error description"), filePath];
- error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeModifyingOfflineStorageFailed
- userInfo:@{
- NSLocalizedDescriptionKey: description,
- NSLocalizedFailureReasonErrorKey: @(mbgl::util::toString(result.error()).c_str())
- }];
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- [[MMEEventsManager sharedManager] reportError:error];
-#endif
- } else {
- auto& regions = result.value();
- packs = [NSMutableArray arrayWithCapacity:regions.size()];
- for (auto &region : regions) {
- MGLOfflinePack *pack = [[MGLOfflinePack alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(region))];
- [packs addObject:pack];
- }
- }
- if (completion) {
- dispatch_async(dispatch_get_main_queue(), [&, completion, error, packs](void) {
- completion(packs, error);
- });
- }
- });
-}
-
-#pragma mark Pack management methods
-
-- (void)addPackForRegion:(id <MGLOfflineRegion>)region withContext:(NSData *)context completionHandler:(MGLOfflinePackAdditionCompletionHandler)completion {
- MGLLogDebug(@"Adding packForRegion: %@ contextLength: %lu completionHandler: %@", region, (unsigned long)context.length, completion);
- __weak MGLOfflineStorage *weakSelf = self;
- [self _addPackForRegion:region withContext:context completionHandler:^(MGLOfflinePack * _Nullable pack, NSError * _Nullable error) {
- pack.state = MGLOfflinePackStateInactive;
- MGLOfflineStorage *strongSelf = weakSelf;
- [[strongSelf mutableArrayValueForKey:@"packs"] addObject:pack];
- if (completion) {
- completion(pack, error);
- }
-
- #if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- NSMutableDictionary *offlineDownloadStartEventAttributes = [NSMutableDictionary dictionaryWithObject:MMEventTypeOfflineDownloadStart forKey:MMEEventKeyEvent];
-
- if ([region conformsToProtocol:@protocol(MGLOfflineRegion_Private)]) {
- NSDictionary *regionAttributes = ((id<MGLOfflineRegion_Private>)region).offlineStartEventAttributes;
- [offlineDownloadStartEventAttributes addEntriesFromDictionary:regionAttributes];
- }
-
- [MGLMapboxEvents pushEvent:MMEventTypeOfflineDownloadStart withAttributes:offlineDownloadStartEventAttributes];
- #endif
- }];
-}
-
-- (void)_addPackForRegion:(id <MGLOfflineRegion>)region withContext:(NSData *)context completionHandler:(MGLOfflinePackAdditionCompletionHandler)completion {
- if (![region conformsToProtocol:@protocol(MGLOfflineRegion_Private)]) {
- [NSException raise:MGLUnsupportedRegionTypeException
- format:@"Regions of type %@ are unsupported.", NSStringFromClass([region class])];
- return;
- }
-
- const mbgl::OfflineRegionDefinition regionDefinition = [(id <MGLOfflineRegion_Private>)region offlineRegionDefinition];
- mbgl::OfflineRegionMetadata metadata(context.length);
- [context getBytes:&metadata[0] length:metadata.size()];
- _mbglDatabaseFileSource->createOfflineRegion(regionDefinition, metadata, [&, completion](mbgl::expected<mbgl::OfflineRegion, std::exception_ptr> mbglOfflineRegion) {
- NSError *error;
- if (!mbglOfflineRegion) {
- NSString *errorDescription = @(mbgl::util::toString(mbglOfflineRegion.error()).c_str());
- error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeModifyingOfflineStorageFailed userInfo:errorDescription ? @{
- NSLocalizedDescriptionKey: errorDescription,
- } : nil];
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- [[MMEEventsManager sharedManager] reportError:error];
-#endif
- }
- if (completion) {
- MGLOfflinePack *pack = mbglOfflineRegion ? [[MGLOfflinePack alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(mbglOfflineRegion.value()))] : nil;
- dispatch_async(dispatch_get_main_queue(), [&, completion, error, pack](void) {
- completion(pack, error);
- });
- }
- });
-}
-
-- (void)removePack:(MGLOfflinePack *)pack withCompletionHandler:(MGLOfflinePackRemovalCompletionHandler)completion {
- MGLLogDebug(@"Removing pack: %@ completionHandler: %@", pack, completion);
- [[self mutableArrayValueForKey:@"packs"] removeObject:pack];
- [self _removePack:pack withCompletionHandler:^(NSError * _Nullable error) {
- if (completion) {
- completion(error);
- }
- }];
-}
-
-- (void)_removePack:(MGLOfflinePack *)pack withCompletionHandler:(MGLOfflinePackRemovalCompletionHandler)completion {
- mbgl::OfflineRegion *mbglOfflineRegion = pack.mbglOfflineRegion;
-
- [pack invalidate];
-
- if (!mbglOfflineRegion) {
- MGLAssert(pack.state == MGLOfflinePackStateInvalid, @"State should be invalid");
- completion(nil);
- return;
- }
-
- _mbglDatabaseFileSource->deleteOfflineRegion(std::move(*mbglOfflineRegion), [&, completion](std::exception_ptr exception) {
- NSError *error;
- if (exception) {
- error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeModifyingOfflineStorageFailed userInfo:@{
- NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
- }];
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- [[MMEEventsManager sharedManager] reportError:error];
-#endif
- }
- if (completion) {
- dispatch_async(dispatch_get_main_queue(), [&, completion, error](void) {
- completion(error);
- });
- }
- });
-
-}
-
-- (void)invalidatePack:(MGLOfflinePack *)pack withCompletionHandler:(void (^)(NSError * _Nullable))completion {
- mbgl::OfflineRegion& region = *pack.mbglOfflineRegion;
- NSError *error;
- if (!pack.mbglOfflineRegion) {
- completion(nil);
- return;
- }
-
- _mbglDatabaseFileSource->invalidateOfflineRegion(region, [&](std::exception_ptr exception) {
- if (exception) {
- error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeModifyingOfflineStorageFailed userInfo:@{
- NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
- }];
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- [[MMEEventsManager sharedManager] reportError:error];
-#endif
- }
- });
- if (completion) {
- dispatch_async(dispatch_get_main_queue(), [&, completion, error](void) {
- completion(error);
- });
- }
-}
-
-- (void)reloadPacks {
- MGLLogInfo(@"Reloading packs.");
- [self getPacksWithCompletionHandler:^(NSArray<MGLOfflinePack *> *packs, __unused NSError * _Nullable error) {
- for (MGLOfflinePack *pack in self.packs) {
- [pack invalidate];
- }
- self.packs = [packs mutableCopy];
- }];
-}
-
-- (void)getPacksWithCompletionHandler:(void (^)(NSArray<MGLOfflinePack *> *packs, NSError * _Nullable error))completion {
- _mbglDatabaseFileSource->listOfflineRegions([&, completion](mbgl::expected<mbgl::OfflineRegions, std::exception_ptr> result) {
- NSError *error;
- NSMutableArray *packs;
- if (!result) {
- error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeUnknown userInfo:@{
- NSLocalizedDescriptionKey: @(mbgl::util::toString(result.error()).c_str()),
- }];
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- [[MMEEventsManager sharedManager] reportError:error];
-#endif
- } else {
- auto& regions = result.value();
- packs = [NSMutableArray arrayWithCapacity:regions.size()];
- for (auto &region : regions) {
- MGLOfflinePack *pack = [[MGLOfflinePack alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(region))];
- [packs addObject:pack];
- }
- }
- if (completion) {
- dispatch_async(dispatch_get_main_queue(), [&, completion, error, packs](void) {
- completion(packs, error);
- });
- }
- });
-}
-
-- (void)setMaximumAllowedMapboxTiles:(uint64_t)maximumCount {
- MGLLogDebug(@"Setting maximumAllowedMapboxTiles: %lu", (unsigned long)maximumCount);
- _mbglDatabaseFileSource->setOfflineMapboxTileCountLimit(maximumCount);
-}
-
-#pragma mark - Ambient Cache management
-
-- (void)setMaximumAmbientCacheSize:(NSUInteger)cacheSize withCompletionHandler:(void (^)(NSError * _Nullable))completion {
- _mbglDatabaseFileSource->setMaximumAmbientCacheSize(cacheSize, [&, completion](std::exception_ptr exception) {
- NSError *error;
- if (completion) {
- if (exception) {
- error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeModifyingOfflineStorageFailed userInfo:@{
- NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
- }];
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- [[MMEEventsManager sharedManager] reportError:error];
-#endif
- }
- dispatch_sync(dispatch_get_main_queue(), ^ {
- completion(error);
- });
- }
- });
-}
-
-- (void)invalidateAmbientCacheWithCompletionHandler:(void (^)(NSError *_Nullable))completion {
- _mbglDatabaseFileSource->invalidateAmbientCache([&, completion](std::exception_ptr exception){
- NSError *error;
- if (completion) {
- if (exception) {
- // Convert std::exception_ptr to an NSError.
- error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeModifyingOfflineStorageFailed userInfo:@{
- NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
- }];
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- [[MMEEventsManager sharedManager] reportError:error];
-#endif
- }
- dispatch_async(dispatch_get_main_queue(), ^ {
- completion(error);
- });
- }
- });
-}
-
-- (void)clearAmbientCacheWithCompletionHandler:(void (^)(NSError *_Nullable error))completion {
- _mbglDatabaseFileSource->clearAmbientCache([&, completion](std::exception_ptr exception){
- NSError *error;
- if (completion) {
- if (exception) {
- error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeModifyingOfflineStorageFailed userInfo:@{
- NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
- }];
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- [[MMEEventsManager sharedManager] reportError:error];
-#endif
- }
- dispatch_async(dispatch_get_main_queue(), [&, completion, error](void) {
- completion(error);
- });
- }
- });
-}
-
-- (void)resetDatabaseWithCompletionHandler:(void (^)(NSError *_Nullable error))completion {
- _mbglDatabaseFileSource->resetDatabase([&, completion](std::exception_ptr exception) {
- NSError *error;
- if (completion) {
- if (exception) {
- error = [NSError errorWithDomain:MGLErrorDomain code:MGLErrorCodeUnknown userInfo:@{
- NSLocalizedDescriptionKey: @(mbgl::util::toString(exception).c_str()),
- }];
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- [[MMEEventsManager sharedManager] reportError:error];
-#endif
- }
- dispatch_async(dispatch_get_main_queue(), ^{
- completion(error);
- });
- }
- });
-}
-#pragma mark -
-
-- (unsigned long long)countOfBytesCompleted {
- NSURL *cacheURL = [[self class] cacheURLIncludingSubdirectory:YES];
- NSString *cachePath = cacheURL.path;
- if (!cachePath) {
- return 0;
- }
-
- NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:cachePath error:NULL];
- return attributes.fileSize;
-}
-
-- (void)preloadData:(NSData *)data forURL:(NSURL *)url modificationDate:(nullable NSDate *)modified expirationDate:(nullable NSDate *)expires eTag:(nullable NSString *)eTag mustRevalidate:(BOOL)mustRevalidate {
- mbgl::Resource resource(mbgl::Resource::Kind::Unknown, url.absoluteString.UTF8String);
- mbgl::Response response;
- response.data = std::make_shared<std::string>(static_cast<const char*>(data.bytes), data.length);
- response.mustRevalidate = mustRevalidate;
-
- if (eTag) {
- response.etag = std::string(eTag.UTF8String);
- }
-
- if (modified) {
- response.modified = mbgl::Timestamp() + std::chrono::duration_cast<mbgl::Seconds>(MGLDurationFromTimeInterval(modified.timeIntervalSince1970));
- }
-
- if (expires) {
- response.expires = mbgl::Timestamp() + std::chrono::duration_cast<mbgl::Seconds>(MGLDurationFromTimeInterval(expires.timeIntervalSince1970));
- }
-
- _mbglDatabaseFileSource->put(resource, response);
-}
-
-- (void)putResourceWithUrl:(NSURL *)url data:(NSData *)data modified:(nullable NSDate *)modified expires:(nullable NSDate *)expires etag:(nullable NSString *)etag mustRevalidate:(BOOL)mustRevalidate {
- [self preloadData:data forURL:url modificationDate:modified expirationDate:expires eTag:etag mustRevalidate:mustRevalidate];
-}
-
-@end