summaryrefslogtreecommitdiff
path: root/platform/darwin/src/MGLOfflineStorage.mm
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2016-03-25 17:02:08 -0700
committerMinh Nguyễn <mxn@1ec5.org>2016-03-29 11:46:25 -0700
commita1e5701fa3fa7041cbbdd3edabd1c6a8006ce68b (patch)
tree4ad07a8c03277f4f9d4700ad27fd351ded166139 /platform/darwin/src/MGLOfflineStorage.mm
parentbbab5a5b5e9b3cd2461a1c088f8610e6f1938686 (diff)
downloadqtlocation-mapboxgl-a1e5701fa3fa7041cbbdd3edabd1c6a8006ce68b.tar.gz
[ios, osx] Centralized offline pack management
MGLOfflineStorage now maintains a centralized, strongly held, KVO-compliant collection of all extant offline packs. MGLOfflineStorage issues notifications for progress updates. Client code can now react to the successful addition of an offline pack either via the completion block or via a KVO insertion. Fixes #4287.
Diffstat (limited to 'platform/darwin/src/MGLOfflineStorage.mm')
-rw-r--r--platform/darwin/src/MGLOfflineStorage.mm110
1 files changed, 98 insertions, 12 deletions
diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm
index 4dbfb05801..2add5e1051 100644
--- a/platform/darwin/src/MGLOfflineStorage.mm
+++ b/platform/darwin/src/MGLOfflineStorage.mm
@@ -8,11 +8,30 @@
#include <mbgl/util/string.hpp>
-@interface MGLOfflineStorage ()
+static NSString * const MGLOfflineStorageFileName = @"cache.db";
+static NSString * const MGLOfflineStorageFileName3_2_0_beta_1 = @"offline.db";
-@property (nonatomic) mbgl::DefaultFileSource *mbglFileSource;
+NSString * const MGLOfflinePackProgressChangedNotification = @"MGLOfflinePackProgressChanged";
+NSString * const MGLOfflinePackErrorNotification = @"MGLOfflinePackError";
+NSString * const MGLOfflinePackMaximumMapboxTilesReachedNotification = @"MGLOfflinePackMaximumMapboxTilesReached";
+
+NSString * const MGLOfflinePackErrorUserInfoKey = @"Error";
+NSString * const MGLOfflinePackMaximumCountUserInfoKey = @"MaximumCount";
+
+/**
+ A block to be called with a complete list of offline packs.
+
+ @param pack Contains a pointer an array of packs, or `nil` if there was an
+ error obtaining the packs.
+ @param error Contains a pointer to an error object (if any) indicating why the
+ list of packs could not be obtained.
+ */
+typedef void (^MGLOfflinePackListingCompletionHandler)(NS_ARRAY_OF(MGLOfflinePack *) *packs, NSError * _Nullable error);
+
+@interface MGLOfflineStorage () <MGLOfflinePackDelegate>
-- (instancetype)initWithFileName:(NSString *)fileName NS_DESIGNATED_INITIALIZER;
+@property (nonatomic, copy, readwrite) NS_MUTABLE_ARRAY_OF(MGLOfflinePack *) *packs;
+@property (nonatomic) mbgl::DefaultFileSource *mbglFileSource;
@end
@@ -22,14 +41,20 @@
static dispatch_once_t onceToken;
static MGLOfflineStorage *sharedOfflineStorage;
dispatch_once(&onceToken, ^{
- sharedOfflineStorage = [[self alloc] initWithFileName:@"cache.db"];
+ sharedOfflineStorage = [[self alloc] init];
+ [sharedOfflineStorage getPacksWithCompletionHandler:^(NS_ARRAY_OF(MGLOfflinePack *) *packs, __unused NSError *error) {
+ sharedOfflineStorage.packs = [packs mutableCopy];
+
+ for (MGLOfflinePack *pack in packs) {
+ pack.delegate = sharedOfflineStorage;
+ [pack requestProgress];
+ }
+ }];
});
return sharedOfflineStorage;
}
-// This method can’t be called -init, because that selector has been marked
-// unavailable in MGLOfflineStorage.h.
-- (instancetype)initWithFileName:(NSString *)fileName {
+- (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
@@ -46,7 +71,7 @@
withIntermediateDirectories:YES
attributes:nil
error:nil];
- NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:fileName];
+ NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName];
NSString *cachePath = cacheURL ? cacheURL.path : @"";
// Avoid backing up the offline cache onto iCloud, because it can be
@@ -57,11 +82,10 @@
// Move the offline cache from v3.2.0-beta.1 to a location that can also
// be used for ambient caching.
- NSString *legacyCacheFileName = @"offline.db";
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
// ~/Documents/offline.db
NSArray *legacyPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
- NSString *legacyCachePath = [legacyPaths.firstObject stringByAppendingPathComponent:legacyCacheFileName];
+ 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
@@ -75,7 +99,7 @@
withIntermediateDirectories:YES
attributes:nil
error:nil];
- NSURL *legacyCacheURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:legacyCacheFileName];
+ NSURL *legacyCacheURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageLegacyFileName];
NSString *legacyCachePath = legacyCacheURL ? legacyCacheURL.path : @"";
#endif
if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath]) {
@@ -101,17 +125,52 @@
_mbglFileSource = nullptr;
}
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NS_DICTIONARY_OF(NSString *, id) *)change context:(__unused void *)context {
+#pragma mark KVO methods
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NS_DICTIONARY_OF(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]]) {
self.mbglFileSource->setAccessToken(accessToken.UTF8String);
}
+ } else {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
+- (void)insertPacks:(NSArray *)packs atIndexes:(NSIndexSet *)indices {
+ [(NSMutableArray *)self.packs insertObjects:packs atIndexes:indices];
+}
+
+- (void)addPacksObject:(MGLOfflinePack *)pack {
+ [(NSMutableArray *)self.packs addObject:pack];
+}
+
+- (void)removePacksAtIndexes:(NSIndexSet *)indices {
+ [(NSMutableArray *)self.packs removeObjectsAtIndexes:indices];
+}
+
+- (void)removePacksObject:(MGLOfflinePack *)pack {
+ [(NSMutableArray *)self.packs removeObject:pack];
+}
+
+#pragma mark Pack management methods
+
- (void)addPackForRegion:(id <MGLOfflineRegion>)region withContext:(NSData *)context completionHandler:(MGLOfflinePackAdditionCompletionHandler)completion {
+ __weak MGLOfflineStorage *weakSelf = self;
+ [self _addPackForRegion:region withContext:context completionHandler:^(MGLOfflinePack * _Nullable pack, NSError * _Nullable error) {
+ MGLOfflineStorage *strongSelf = weakSelf;
+ [strongSelf addPacksObject:pack];
+ pack.delegate = strongSelf;
+ [pack requestProgress];
+ if (completion) {
+ completion(pack, error);
+ }
+ }];
+}
+
+- (void)_addPackForRegion:(id <MGLOfflineRegion>)region withContext:(NSData *)context completionHandler:(MGLOfflinePackAdditionCompletionHandler)completion {
if (![region conformsToProtocol:@protocol(MGLOfflineRegion_Private)]) {
[NSException raise:@"Unsupported region type" format:
@"Regions of type %@ are unsupported.", NSStringFromClass([region class])];
@@ -139,6 +198,15 @@
}
- (void)removePack:(MGLOfflinePack *)pack withCompletionHandler:(MGLOfflinePackRemovalCompletionHandler)completion {
+ [self removePacksObject: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];
self.mbglFileSource->deleteOfflineRegion(std::move(*mbglOfflineRegion), [&, completion](std::exception_ptr exception) {
@@ -184,4 +252,22 @@
_mbglFileSource->setOfflineMapboxTileCountLimit(maximumCount);
}
+#pragma mark MGLOfflinePackDelegate methods
+
+- (void)offlinePack:(MGLOfflinePack *)pack progressDidChange:(__unused MGLOfflinePackProgress)progress {
+ [[NSNotificationCenter defaultCenter] postNotificationName:MGLOfflinePackProgressChangedNotification object:pack];
+}
+
+- (void)offlinePack:(MGLOfflinePack *)pack didReceiveError:(NSError *)error {
+ [[NSNotificationCenter defaultCenter] postNotificationName:MGLOfflinePackErrorNotification object:pack userInfo:@{
+ MGLOfflinePackErrorUserInfoKey: error,
+ }];
+}
+
+- (void)offlinePack:(MGLOfflinePack *)pack didReceiveMaximumAllowedMapboxTiles:(uint64_t)maximumCount {
+ [[NSNotificationCenter defaultCenter] postNotificationName:MGLOfflinePackMaximumMapboxTilesReachedNotification object:pack userInfo:@{
+ MGLOfflinePackMaximumCountUserInfoKey: @(maximumCount),
+ }];
+}
+
@end