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

Commit

Permalink
[ios, osx] Unified offline and ambient caches
Browse files Browse the repository at this point in the history
There is now only one instance of mbgl::OfflineFileSource, created when the shared MGLOfflineStorage object is initialized. Also create and use the shared MGLOfflineStorage object when initializing an MGLMapView object. The unified cache file is located in a subdirectory of Application Support, where the SDK has control over the file’s lifetime. The subdirectory is already named after the host application’s bundle identifier, ensuring that each Mapbox-powered application has an independent tile limit. If there’s an ambient cache in a subdirectory of Caches, delete it. If there’s an offline cache in a subdirectory of Documents on iOS or Caches on OS X, move it to the unified cache location in a subdirectory of Application Support.

Fixes the iOS/OS X side of #4338.
  • Loading branch information
1ec5 committed Mar 18, 2016
1 parent fff13d0 commit b493bdc
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 82 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,10 @@ Known issues:
- Black Screen On Ice Cream Sandwich and Jelly Bean devices ([#2802](https://github.com/mapbox/mapbox-gl-native/issues/2802))
- Resolved in 2.2.0

## iOS master
## iOS 3.2.0

- Offline packs can now be downloaded to allow users to view specific regions of the map offline. A new MGLOfflineStorage class provides APIs for managing MGLOfflinePacks. ([#4221](https://github.com/mapbox/mapbox-gl-native/pull/4221))
- Tiles and other resources are cached in the same file that holds offline resources. The combined cache file is located in a subdirectory of the user’s Application Support directory, which means iOS will not delete the file when disk space runs low. ([#4377](https://github.com/mapbox/mapbox-gl-native/pull/4377))
- The user dot no longer disappears after panning the map across the antimeridian at low zoom levels. ([#4275](https://github.com/mapbox/mapbox-gl-native/pull/4275))
- The map no longer recoils when panning quickly at low zoom levels. ([#4214](https://github.com/mapbox/mapbox-gl-native/pull/4214))
- An icon laid out along a line no longer appears if it would extend past the end of the line. Some one-way arrows no longer point the wrong way. ([#3839](https://github.com/mapbox/mapbox-gl-native/pull/3839))
Expand Down
41 changes: 35 additions & 6 deletions platform/darwin/src/MGLOfflineStorage.mm
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,20 @@ + (instancetype)sharedOfflineStorage {
static dispatch_once_t onceToken;
static MGLOfflineStorage *sharedOfflineStorage;
dispatch_once(&onceToken, ^{
sharedOfflineStorage = [[self alloc] initWithFileName:@"offline.db"];
sharedOfflineStorage = [[self alloc] initWithFileName:@"cache.db"];
});
return sharedOfflineStorage;
}

// This method can’t be called -init, because that selector has been marked
// unavailable in MGLOfflineStorage.h.
- (instancetype)initWithFileName:(NSString *)fileName {
if (self = [super init]) {
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths.firstObject stringByAppendingPathComponent:fileName];
#elif TARGET_OS_MAC
NSURL *cacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
// 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
Expand All @@ -46,7 +48,34 @@ - (instancetype)initWithFileName:(NSString *)fileName {
error:nil];
NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:fileName];
NSString *cachePath = cacheURL ? cacheURL.path : @"";

// 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];
#elif TARGET_OS_MAC
// ~/Library/Caches/tld.app.bundle.id/offline.db
NSURL *legacyCacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:nil];
legacyCacheDirectoryURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:
[NSBundle mainBundle].bundleIdentifier];
[[NSFileManager defaultManager] createDirectoryAtURL:legacyCacheDirectoryURL
withIntermediateDirectories:YES
attributes:nil
error:nil];
NSURL *legacyCacheURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:legacyCacheFileName];
NSString *legacyCachePath = legacyCacheURL ? legacyCacheURL.path : @"";
#endif
if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath]) {
[[NSFileManager defaultManager] moveItemAtPath:legacyCachePath 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).
Expand Down
42 changes: 8 additions & 34 deletions platform/ios/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@
#import "Mapbox.h"
#import "../../darwin/src/MGLGeometry_Private.h"
#import "../../darwin/src/MGLMultiPoint_Private.h"
#import "../../darwin/src/MGLOfflineStorage_Private.h"

#import "NSBundle+MGLAdditions.h"
#import "NSString+MGLAdditions.h"
#import "NSProcessInfo+MGLAdditions.h"
#import "NSException+MGLAdditions.h"
#import "MGLUserLocationAnnotationView.h"
#import "MGLUserLocation_Private.h"
#import "MGLAccountManager_Private.h"
#import "MGLAnnotationImage_Private.h"
#import "MGLMapboxEvents.h"
#import "MGLCompactCalloutView.h"
Expand Down Expand Up @@ -187,7 +187,6 @@ @implementation MGLMapView
{
mbgl::Map *_mbglMap;
MBGLView *_mbglView;
mbgl::DefaultFileSource *_mbglFileSource;

BOOL _opaque;

Expand Down Expand Up @@ -303,32 +302,22 @@ - (void)commonInit
// setup mbgl view
const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
_mbglView = new MBGLView(self, scaleFactor);

// setup mbgl cache & file source
NSString *fileCachePath = @"";

// Delete the pre-offline ambient cache at ~/Library/Caches/cache.db.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
if ([paths count] != 0) {
NSString *libraryDirectory = [paths objectAtIndex:0];
fileCachePath = [libraryDirectory stringByAppendingPathComponent:@"cache.db"];
}
_mbglFileSource = new mbgl::DefaultFileSource([fileCachePath UTF8String], [[[[NSBundle mainBundle] resourceURL] path] UTF8String]);
NSString *fileCachePath = [paths.firstObject stringByAppendingPathComponent:@"cache.db"];
[[NSFileManager defaultManager] removeItemAtPath:fileCachePath error:NULL];

// setup mbgl map
_mbglMap = new mbgl::Map(*_mbglView, *_mbglFileSource, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None);
mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
_mbglMap = new mbgl::Map(*_mbglView, *mbglFileSource, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None);

// start paused if in IB
if (_isTargetingInterfaceBuilder || background) {
self.dormant = YES;
_mbglMap->pause();
}

// 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];

// Notify map object when network reachability status changes.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reachabilityChanged:)
Expand Down Expand Up @@ -506,7 +495,6 @@ - (void)reachabilityChanged:(NSNotification *)notification
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[MGLAccountManager sharedManager] removeObserver:self forKeyPath:@"accessToken"];
[_attributionButton removeObserver:self forKeyPath:@"hidden"];

[self validateDisplayLink];
Expand All @@ -517,12 +505,6 @@ - (void)dealloc
_mbglMap = nullptr;
}

if (_mbglFileSource)
{
delete _mbglFileSource;
_mbglFileSource = nullptr;
}

if (_mbglView)
{
delete _mbglView;
Expand Down Expand Up @@ -1606,15 +1588,7 @@ - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(__unused void *)context
{
// Synchronize mbgl::Map’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]]) {
_mbglFileSource->setAccessToken((std::string)[accessToken UTF8String]);
}
}
else if ([keyPath isEqualToString:@"hidden"] && object == _attributionButton)
if ([keyPath isEqualToString:@"hidden"] && object == _attributionButton)
{
NSNumber *hiddenNumber = change[NSKeyValueChangeNewKey];
BOOL attributionButtonWasHidden = [hiddenNumber boolValue];
Expand Down
60 changes: 19 additions & 41 deletions platform/osx/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
#import "MGLOpenGLLayer.h"
#import "MGLStyle.h"

#import "../../darwin/src/MGLAccountManager_Private.h"
#import "../../darwin/src/MGLGeometry_Private.h"
#import "../../darwin/src/MGLMultiPoint_Private.h"
#import "../../darwin/src/MGLOfflineStorage_Private.h"

#import "MGLAccountManager.h"
#import "MGLMapCamera.h"
#import "MGLPolygon.h"
#import "MGLPolyline.h"
Expand Down Expand Up @@ -151,7 +152,6 @@ @implementation MGLMapView {
/// Cross-platform map view controller.
mbgl::Map *_mbglMap;
MGLMapViewImpl *_mbglView;
mbgl::DefaultFileSource *_mbglFileSource;

NSPanGestureRecognizer *_panGestureRecognizer;
NSMagnificationGestureRecognizer *_magnificationGestureRecognizer;
Expand Down Expand Up @@ -232,36 +232,25 @@ - (void)commonInit {
// Set up cross-platform controllers and resources.
_mbglView = new MGLMapViewImpl(self, [NSScreen mainScreen].backingScaleFactor);

// Place the cache in a location that can be shared among all the
// applications that embed the Mapbox OS X SDK.
NSURL *cacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:nil];
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:
[[NSBundle mgl_frameworkBundle] bundleIdentifier]];
[[NSFileManager defaultManager] createDirectoryAtURL:cacheDirectoryURL
withIntermediateDirectories:YES
attributes:nil
error:nil];
NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:@"cache.db"];
NSString *cachePath = cacheURL ? cacheURL.path : @"";
_mbglFileSource = new mbgl::DefaultFileSource(cachePath.UTF8String, [[[[NSBundle mainBundle] resourceURL] path] UTF8String]);

_mbglMap = new mbgl::Map(*_mbglView, *_mbglFileSource, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None);
// Delete the pre-offline ambient cache at
// ~/Library/Caches/com.mapbox.sdk.ios/cache.db.
NSURL *cachesDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:nil];
cachesDirectoryURL = [cachesDirectoryURL URLByAppendingPathComponent:
[NSBundle mgl_frameworkBundle].bundleIdentifier];
NSURL *legacyCacheURL = [cachesDirectoryURL URLByAppendingPathComponent:@"cache.db"];
[[NSFileManager defaultManager] removeItemAtURL:legacyCacheURL error:NULL];

mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
_mbglMap = new mbgl::Map(*_mbglView, *mbglFileSource, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None);

// Install the OpenGL layer. Interface Builder’s synchronous drawing means
// we can’t display a map, so don’t even bother to have a map layer.
self.layer = _isTargetingInterfaceBuilder ? [CALayer layer] : [MGLOpenGLLayer layer];

// 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];

// Notify map object when network reachability status changes.
MGLReachability *reachability = [MGLReachability reachabilityForInternetConnection];
reachability.reachableBlock = ^(MGLReachability *) {
Expand Down Expand Up @@ -443,7 +432,6 @@ - (void)updateAttributionView {
}

- (void)dealloc {
[[MGLAccountManager sharedManager] removeObserver:self forKeyPath:@"accessToken"];
[self.window removeObserver:self forKeyPath:@"contentLayoutRect"];
[self.window removeObserver:self forKeyPath:@"titlebarAppearsTransparent"];

Expand All @@ -455,25 +443,15 @@ - (void)dealloc {
delete _mbglMap;
_mbglMap = nullptr;
}
if (_mbglFileSource) {
delete _mbglFileSource;
_mbglFileSource = nullptr;
}
if (_mbglView) {
delete _mbglView;
_mbglView = nullptr;
}
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(__unused void *)context {
// Synchronize mbgl::Map’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]]) {
_mbglFileSource->setAccessToken((std::string)accessToken.UTF8String);
}
} else if ([keyPath isEqualToString:@"contentLayoutRect"] ||
[keyPath isEqualToString:@"titlebarAppearsTransparent"]) {
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(__unused id)object change:(__unused NSDictionary *)change context:(__unused void *)context {
if ([keyPath isEqualToString:@"contentLayoutRect"] ||
[keyPath isEqualToString:@"titlebarAppearsTransparent"]) {
[self adjustContentInsets];
}
}
Expand Down

0 comments on commit b493bdc

Please sign in to comment.