diff options
author | Minh Nguyễn <mxn@1ec5.org> | 2016-03-07 03:25:21 -0800 |
---|---|---|
committer | Minh Nguyễn <mxn@1ec5.org> | 2016-03-10 17:08:58 -0800 |
commit | 8824237e2df9d9bfb9cd618e762e74d8ab08c099 (patch) | |
tree | 58cc45ca983cb487606b185d99a2de2b6a089703 /platform | |
parent | 0348c1ee967cf3e19d9dc91918d28e42b40b5b19 (diff) | |
download | qtlocation-mapboxgl-8824237e2df9d9bfb9cd618e762e74d8ab08c099.tar.gz |
[ios, osx] Documented offline APIs
Also categorized offline symbols for jazzy and moved styleURL from MGLOfflineRegion to MGLTilePyramidOfflineRegion, since you never know if there will be a region type not constrained by a style in the future.
Diffstat (limited to 'platform')
-rw-r--r-- | platform/darwin/include/MGLOfflineRegion.h | 8 | ||||
-rw-r--r-- | platform/darwin/include/MGLOfflineStorage.h | 87 | ||||
-rw-r--r-- | platform/darwin/include/MGLOfflineTask.h | 141 | ||||
-rw-r--r-- | platform/darwin/include/MGLTilePyramidOfflineRegion.h | 58 | ||||
-rw-r--r-- | platform/darwin/src/MGLOfflineStorage.mm | 11 | ||||
-rw-r--r-- | platform/darwin/src/MGLOfflineTask.mm | 25 | ||||
-rw-r--r-- | platform/darwin/src/MGLOfflineTask_Private.h | 2 | ||||
-rw-r--r-- | platform/ios/app/MBXDownloadsTableViewController.m | 8 |
8 files changed, 328 insertions, 12 deletions
diff --git a/platform/darwin/include/MGLOfflineRegion.h b/platform/darwin/include/MGLOfflineRegion.h index 6958e3bffe..04d2327293 100644 --- a/platform/darwin/include/MGLOfflineRegion.h +++ b/platform/darwin/include/MGLOfflineRegion.h @@ -4,10 +4,14 @@ NS_ASSUME_NONNULL_BEGIN +/** + An object conforming to the `MGLOfflineRegion` protocol determines which + resources are required by an `MGLOfflineTask` object. At present, only + instances of `MGLTilePyramidOfflineRegion` may be used as `MGLOfflineTask` + regions, but additional conforming implementations may be added in the future. + */ @protocol MGLOfflineRegion <NSObject> -@property (nonatomic, readonly) NSURL *styleURL; - @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/include/MGLOfflineStorage.h b/platform/darwin/include/MGLOfflineStorage.h index 2d33f3ba69..459371d9f7 100644 --- a/platform/darwin/include/MGLOfflineStorage.h +++ b/platform/darwin/include/MGLOfflineStorage.h @@ -7,22 +7,99 @@ NS_ASSUME_NONNULL_BEGIN @class MGLOfflineTask; @protocol MGLOfflineRegion; -typedef void (^MGLOfflineTaskRegistrationCompletionHandler)(MGLOfflineTask *task, NSError *error); -typedef void (^MGLOfflineTaskRemovalCompletionHandler)(NSError *error); -typedef void (^MGLOfflineTasksRetrievalCompletionHandler)(NS_ARRAY_OF(MGLOfflineTask *) *tasks, NSError *error); +/** + A block to be called once an offline task has been completely created and + added. + + @param task Contains a pointer to the newly added task, or `nil` if there was + an error creating or adding the task. + @param error Contains a pointer to an error object (if any) indicating why the + task could not be created or added. For a list of possible error codes, see + `MGLErrorCode`. + */ +typedef void (^MGLOfflineTaskAdditionCompletionHandler)(MGLOfflineTask * _Nullable task, NSError * _Nullable error); +/** + A block to be called once an offline task has been completely invalidated and + removed. + + @param error Contains a pointer to an error object (if any) indicating why the + task could not be invalidated or removed. + */ +typedef void (^MGLOfflineTaskRemovalCompletionHandler)(NSError * _Nullable error); + +/** + A block to be called with a complete list of offline tasks. + + @param task Contains a pointer an array of tasks, or `nil` if there was an + error obtaining the tasks. + @param error Contains a pointer to an error object (if any) indicating why the + list of tasks could not be obtained. + */ +typedef void (^MGLOfflineTaskListingCompletionHandler)(NS_ARRAY_OF(MGLOfflineTask *) *tasks, NSError * _Nullable error); + +/** + MGLOfflineStorage implements a singleton (shared object) that manages offline + tasks. All of this class’s instance methods are asynchronous, reflecting the + fact that offline resources are stored in a database. + */ @interface MGLOfflineStorage : NSObject +/** + Returns the shared offline storage object. + */ + (instancetype)sharedOfflineStorage; - (instancetype)init NS_UNAVAILABLE; -- (void)addTaskForRegion:(id <MGLOfflineRegion>)region withContext:(NSData *)context completionHandler:(MGLOfflineTaskRegistrationCompletionHandler)completion; +/** + Creates and registers an offline task that downloads the resources needed to + use the given region offline. + + The resulting task starts out with a state of `MGLOfflineTaskStateInactive`. To + begin downloading resources, call `-[MGLOfflineTask resume]`. To monitor + download progress, set the task’s `delegate` property to an object that + conforms to the `MGLOfflineTaskDelegate` protocol. + + @param region A region to download. + @param context Arbitrary data to store alongside the downloaded resources. + @param completion The completion handler to call once the task has been added. + This handler is executed asynchronously on the main queue. + */ +- (void)addTaskForRegion:(id <MGLOfflineRegion>)region withContext:(NSData *)context completionHandler:(MGLOfflineTaskAdditionCompletionHandler)completion; +/** + Unregisters the given offline task and frees any resources that are no longer + required by any remaining tasks. After this task is removed, it is invalid; any + attempt to interact with it will result in an exception being thrown. + + @param task The offline task to remove. + @param completion The completion handler to call once the task has been + removed. This handler is executed asynchronously on the main queue. + */ - (void)removeTask:(MGLOfflineTask *)task withCompletionHandler:(MGLOfflineTaskRemovalCompletionHandler)completion; -- (void)getTasksWithCompletionHandler:(MGLOfflineTasksRetrievalCompletionHandler)completion; +/** + Asynchronously calls a completion callback with all existing offline tasks. + + @param completion The completion handler to call with the list of tasks. This + handler is executed asynchronously on the main queue. + */ +- (void)getTasksWithCompletionHandler:(MGLOfflineTaskListingCompletionHandler)completion; +/** + Sets the maximum number of Mapbox-hosted tiles that may be downloaded and + stored on the current device. + + Once this limit is reached, + `-[MGLOfflineTaskDelegate offlineTask:didReceiveMaximumAllowedMapboxTiles:]` is + called on every delegate of `MGLOfflineTask` until already downloaded tiles are + removed by calling the `-removeTask:withCompletionHandler:` method. + + @note The [Mapbox Terms of Service](https://www.mapbox.com/tos/) prohibits + changing or bypassing this limit without permission from Mapbox. Contact + your Mapbox sales representative to have the limit raised. + */ - (void)setMaximumAllowedMapboxTiles:(uint64_t)maximumCount; @end diff --git a/platform/darwin/include/MGLOfflineTask.h b/platform/darwin/include/MGLOfflineTask.h index 72e66a50f5..504416aeb9 100644 --- a/platform/darwin/include/MGLOfflineTask.h +++ b/platform/darwin/include/MGLOfflineTask.h @@ -6,42 +6,183 @@ NS_ASSUME_NONNULL_BEGIN @protocol MGLOfflineTaskDelegate; +/** + The state an offline task is currently in. + */ typedef NS_ENUM (NSInteger, MGLOfflineTaskState) { + /** + The task is incomplete and is not currently downloading. This is the + initial state of a task that is added using the + `-[MGLOfflineStorage addTaskForRegion:withContext:completionHandler:]` + method. + */ MGLOfflineTaskStateInactive = 0, + /** + The task is incomplete and is currently downloading. This is the state of a + task after the `-[MGLOfflineTask resume]` method is called. + */ MGLOfflineTaskStateActive = 1, + /** + The task has downloaded to completion. + */ MGLOfflineTaskStateComplete = 2, + /** + The task has been removed using the + `-[MGLOfflineStorage removeTask:withCompletionHandler:]` method. Any + messages sent to the task will raise an exception. + */ + MGLOfflineTaskStateInvalid = 3, }; +/** + A structure containing information about an offline task’s current download + progress. + */ typedef struct MGLOfflineTaskProgress { + /** + The number of resources that have been completely downloaded and are ready + to use offline. + */ uint64_t countOfResourcesCompleted; + /** + The cumulative size of the downloaded resources on disk, measured in bytes. + */ uint64_t countOfBytesCompleted; + /** + The minimum number of resources that must be downloaded in order to view + the task’s full region without any omissions. + + At the beginning of a download, this count is a lower bound; the number of + expected resources may increase as the download progresses. + */ uint64_t countOfResourcesExpected; + /** + The maximum number of resources that must be downloaded in order to view + the task’s full region without any omissions. + + At the beginning of a download, when the exact number of required resources + is unknown, this field is set to `UINT64_MAX`. Thus this count is always an + upper bound. + */ uint64_t maximumResourcesExpected; } MGLOfflineTaskProgress; +/** + An `MGLOfflineTask` writes the resources necessary for viewing a region offline + to a local database, providing an optional `MGLOfflineTaskDelegate` object with + progress updates as data or errors arrive from the server. + */ @interface MGLOfflineTask : NSObject +/** + The region for which the task manages resources. + */ @property (nonatomic, readonly) id <MGLOfflineRegion> region; + +/** + Arbitrary data stored alongside the downloaded resources. + + The context typically holds application-specific information for identifying + the task, such as a user-selected name. + */ @property (nonatomic, readonly) NSData *context; + +/** + The task’s current state. + + The state of an inactive or completed task is computed lazily and is set to + `MGLOfflineTaskStateInactive` by default. If you need the state of a task + inside an `MGLOfflineTaskListingCompletionHandler`, set the `delegate` property + then call the `-requestProgress` method. + */ @property (nonatomic, readonly) MGLOfflineTaskState state; + +/** + The task’s current progress. + + The progress of an inactive or completed task is computed lazily, and all its + fields are set to 0 by default. If you need the progress of a task inside an + `MGLOfflineTaskListingCompletionHandler`, set the `delegate` property then call + the `-requestProgress` method. + */ @property (nonatomic, readonly) MGLOfflineTaskProgress progress; + +/** + The task’s delegate. + + You can use the offline task delegate to be notified of any changes in the + task’s progress and of any errors while downloading. For more information, see + the `MGLOfflineTaskDelegate` documentation. + */ @property (nonatomic, weak, nullable) id <MGLOfflineTaskDelegate> delegate; - (instancetype)init NS_UNAVAILABLE; +/** + Resumes downloading if the task is inactive. + */ - (void)resume; + +/** + Temporarily stops downloading if the task is active. + + To resume downloading, call the `-resume` method. + */ - (void)suspend; +/** + Request an asynchronous update to the task’s `state` and `progress` properties. + + The state and progress of an inactive or completed task are computed lazily. If + you need the state or progress of a task inside an + `MGLOfflineTaskListingCompletionHandler`, set the `delegate` property then call + this method. + */ - (void)requestProgress; @end +/** + The `MGLOfflineTaskDelegate` protocol defines methods that a delegate of an + `MGLOfflineTask` object can optionally implement to be notified of any changes + in the task’s download progress and of any errors while downloading. + */ @protocol MGLOfflineTaskDelegate <NSObject> @optional +/** + Sent whenever the task’s state or download progress changes. Every change to a + field in the `progress` property corresponds to an invocation of this method. + + @param task The task whose state of progress changed. + @param progress The updated progress. To get the updated state, refer to the + `state` property. + */ - (void)offlineTask:(MGLOfflineTask *)task progressDidChange:(MGLOfflineTaskProgress)progress; + +/** + Sent whenever the task encounters an error while downloading. + + Download errors may be recoverable. For example, this task’s implementation may + attempt to re-request failed resources based on an exponential backoff + strategy or upon the restoration of network access. + + @param task The task that encountered an error. + @param error A download error. For a list of possible error codes, see + `MGLErrorCode`. + */ - (void)offlineTask:(MGLOfflineTask *)task didReceiveError:(NSError *)error; + +/** + Sent when the maximum number of Mapbox-hosted tiles has been downloaded and + stored on the current device. + + Once this limit is reached, no instance of `MGLOfflineTask` can download + additional tiles from Mapbox APIs until already downloaded tiles are removed by + calling the `-[MGLOfflineStorage removeTask:withCompletionHandler:]` method. + Contact your Mapbox sales representative to have the limit raised. + */ - (void)offlineTask:(MGLOfflineTask *)task didReceiveMaximumAllowedMapboxTiles:(uint64_t)maximumCount; @end diff --git a/platform/darwin/include/MGLTilePyramidOfflineRegion.h b/platform/darwin/include/MGLTilePyramidOfflineRegion.h index 7beca3390f..89920adcc5 100644 --- a/platform/darwin/include/MGLTilePyramidOfflineRegion.h +++ b/platform/darwin/include/MGLTilePyramidOfflineRegion.h @@ -5,13 +5,71 @@ NS_ASSUME_NONNULL_BEGIN +/** + An offline region defined by a style URL, geographic coordinate bounds, and + range of zoom levels. + */ @interface MGLTilePyramidOfflineRegion : NSObject <MGLOfflineRegion> +/** + URL of the style whose resources are required for offline viewing. + + In addition to the JSON stylesheet, different styles may require different font + glyphs, sprite sheets, and other resources. + + The URL may be a full HTTP or HTTPS URL, a Mapbox URL indicating the style’s + map ID (`mapbox://styles/{user}/{style}`), or a path to a local file + relative to the application’s resource path. + */ +@property (nonatomic, readonly) NSURL *styleURL; + +/** + The coordinate bounds for the geographic region covered by the downloaded + tiles. + */ @property (nonatomic, readonly) MGLCoordinateBounds bounds; + +/** + The minimum zoom level for which to download tiles and other resources. + + For more information about zoom levels, `-[MGLMapView zoomLevel]`. + */ @property (nonatomic, readonly) double minimumZoomLevel; + +/** + The maximum zoom level for which to download tiles and other resources. + + For more information about zoom levels, `-[MGLMapView zoomLevel]`. + */ @property (nonatomic, readonly) double maximumZoomLevel; - (instancetype)init NS_UNAVAILABLE; + +/** + Initializes a newly created offline region with the given style URL, geographic + coordinate bounds, and range of zoom levels. + + This is the designated initializer for `MGLTilePyramidOfflineRegion`. + + @param styleURL URL of the map style for which to download resources. The URL + may be a full HTTP or HTTPS URL, a Mapbox URL indicating the style’s map ID + (`mapbox://styles/{user}/{style}`), or a path to a local file relative to + the application’s resource path. Specify `nil` for the default style. + @param bounds The coordinate bounds for the geographic region to be covered by + the downloaded tiles. + @param minimumZoomLevel The minimum zoom level to be covered by the downloaded + tiles. This parameter should be set to at least 0 but no greater than the + value of the `maximumZoomLevel` parameter. For each required tile source, if + this parameter is set to a value less than the tile source’s minimum zoom + level, the download covers zoom levels down to the tile source’s minimum + zoom level. + @param maximumZoomLevel The maximum zoom level to be covered by the downloaded + tiles. This parameter should be set to at least the value of the + `minimumZoomLevel` parameter. For each required tile source, if this + parameter is set to a value greater than the tile source’s minimum zoom + level, the download covers zoom levels up to the tile source’s maximum zoom + level. + */ - (instancetype)initWithStyleURL:(nullable NSURL *)styleURL bounds:(MGLCoordinateBounds)bounds fromZoomLevel:(double)minimumZoomLevel toZoomLevel:(double)maximumZoomLevel NS_DESIGNATED_INITIALIZER; @end diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 17537452a3..f2061b4a3f 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -76,7 +76,7 @@ } } -- (void)addTaskForRegion:(id <MGLOfflineRegion>)region withContext:(NSData *)context completionHandler:(MGLOfflineTaskRegistrationCompletionHandler)completion { +- (void)addTaskForRegion:(id <MGLOfflineRegion>)region withContext:(NSData *)context completionHandler:(MGLOfflineTaskAdditionCompletionHandler)completion { if (![region conformsToProtocol:@protocol(MGLOfflineRegion_Private)]) { [NSException raise:@"Unsupported region type" format: @"Regions of type %@ are unsupported.", NSStringFromClass([region class])]; @@ -95,7 +95,7 @@ } : nil]; } if (completion) { - MGLOfflineTask *task = [[MGLOfflineTask alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(*mbglOfflineRegion))]; + MGLOfflineTask *task = mbglOfflineRegion ? [[MGLOfflineTask alloc] initWithMBGLRegion:new mbgl::OfflineRegion(std::move(*mbglOfflineRegion))] : nil; dispatch_async(dispatch_get_main_queue(), [&, completion, error, task](void) { completion(task, error); }); @@ -104,7 +104,7 @@ } - (void)removeTask:(MGLOfflineTask *)task withCompletionHandler:(MGLOfflineTaskRemovalCompletionHandler)completion { - self.mbglFileSource->deleteOfflineRegion(std::move(*task.mbglOfflineRegion), [&, completion](std::exception_ptr exception) { + self.mbglFileSource->deleteOfflineRegion(std::move(*task.mbglOfflineRegion), [&, task, completion](std::exception_ptr exception) { NSError *error; if (exception) { error = [NSError errorWithDomain:MGLErrorDomain code:-1 userInfo:@{ @@ -112,14 +112,15 @@ }]; } if (completion) { - dispatch_async(dispatch_get_main_queue(), [&, completion, error](void) { + dispatch_async(dispatch_get_main_queue(), [&, task, completion, error](void) { + [task invalidate]; completion(error); }); } }); } -- (void)getTasksWithCompletionHandler:(MGLOfflineTasksRetrievalCompletionHandler)completion { +- (void)getTasksWithCompletionHandler:(MGLOfflineTaskListingCompletionHandler)completion { self.mbglFileSource->listOfflineRegions([&, completion](std::exception_ptr exception, mbgl::optional<std::vector<mbgl::OfflineRegion>> regions) { NSError *error; if (exception) { diff --git a/platform/darwin/src/MGLOfflineTask.mm b/platform/darwin/src/MGLOfflineTask.mm index a5b6e66031..f1e8bdb566 100644 --- a/platform/darwin/src/MGLOfflineTask.mm +++ b/platform/darwin/src/MGLOfflineTask.mm @@ -7,6 +7,13 @@ #include <mbgl/storage/default_file_source.hpp> #include <mbgl/util/string.hpp> +#define MGLAssertOfflineTaskIsValid() \ + [NSException raise:@"Invalid offline task" \ + format: \ + @"-[MGLOfflineStorage removeTask:withCompletionHandler:] has been called " \ + @"on this instance of MGLOfflineTask, rendering it invalid. It is an " \ + @"error to send any message to this task."] + class MBGLOfflineRegionObserver; @interface MGLOfflineTask () @@ -46,29 +53,45 @@ class MBGLOfflineRegionObserver; } - (id <MGLOfflineRegion>)region { + MGLAssertOfflineTaskIsValid(); + const mbgl::OfflineRegionDefinition ®ionDefinition = _mbglOfflineRegion->getDefinition(); NSAssert([MGLTilePyramidOfflineRegion conformsToProtocol:@protocol(MGLOfflineRegion_Private)], @"MGLTilePyramidOfflineRegion should conform to MGLOfflineRegion_Private."); return [(id <MGLOfflineRegion_Private>)[MGLTilePyramidOfflineRegion alloc] initWithOfflineRegionDefinition:regionDefinition]; } - (NSData *)context { + MGLAssertOfflineTaskIsValid(); + const mbgl::OfflineRegionMetadata &metadata = _mbglOfflineRegion->getMetadata(); return [NSData dataWithBytes:&metadata[0] length:metadata.size()]; } - (void)resume { + MGLAssertOfflineTaskIsValid(); + mbgl::DefaultFileSource *mbglFileSource = [[MGLOfflineStorage sharedOfflineStorage] mbglFileSource]; mbglFileSource->setOfflineRegionDownloadState(*_mbglOfflineRegion, mbgl::OfflineRegionDownloadState::Active); self.state = MGLOfflineTaskStateActive; } - (void)suspend { + MGLAssertOfflineTaskIsValid(); + mbgl::DefaultFileSource *mbglFileSource = [[MGLOfflineStorage sharedOfflineStorage] mbglFileSource]; mbglFileSource->setOfflineRegionDownloadState(*_mbglOfflineRegion, mbgl::OfflineRegionDownloadState::Inactive); self.state = MGLOfflineTaskStateInactive; } +- (void)invalidate { + MGLAssertOfflineTaskIsValid(); + + self.state = MGLOfflineTaskStateInvalid; +} + - (void)requestProgress { + MGLAssertOfflineTaskIsValid(); + mbgl::DefaultFileSource *mbglFileSource = [[MGLOfflineStorage sharedOfflineStorage] mbglFileSource]; __weak MGLOfflineTask *weakSelf = self; @@ -84,6 +107,8 @@ class MBGLOfflineRegionObserver; } - (void)offlineRegionStatusDidChange:(mbgl::OfflineRegionStatus)status { + MGLAssertOfflineTaskIsValid(); + switch (status.downloadState) { case mbgl::OfflineRegionDownloadState::Inactive: self.state = status.complete() ? MGLOfflineTaskStateComplete : MGLOfflineTaskStateInactive; diff --git a/platform/darwin/src/MGLOfflineTask_Private.h b/platform/darwin/src/MGLOfflineTask_Private.h index ad0e597a8d..0b630ff6ff 100644 --- a/platform/darwin/src/MGLOfflineTask_Private.h +++ b/platform/darwin/src/MGLOfflineTask_Private.h @@ -11,6 +11,8 @@ class MBGLOfflineRegionObserver; - (instancetype)initWithMBGLRegion:(mbgl::OfflineRegion *)region; +- (void)invalidate; + @end class MBGLOfflineRegionObserver : public mbgl::OfflineRegionObserver { diff --git a/platform/ios/app/MBXDownloadsTableViewController.m b/platform/ios/app/MBXDownloadsTableViewController.m index 1748310be6..e5edc336a4 100644 --- a/platform/ios/app/MBXDownloadsTableViewController.m +++ b/platform/ios/app/MBXDownloadsTableViewController.m @@ -147,6 +147,10 @@ static NSString * const MBXDownloadsTableViewActiveCellReuseIdentifier = @"Activ statusString = [NSString stringWithFormat:@"Downloading %@ of %@ resources (%@ so far)…", completedString, expectedString, byteCountString]; break; + + case MGLOfflineTaskStateInvalid: + NSAssert(NO, @"Invalid offline task at index path %@", indexPath); + break; } cell.detailTextLabel.text = statusString; @@ -188,6 +192,10 @@ static NSString * const MBXDownloadsTableViewActiveCellReuseIdentifier = @"Activ [task suspend]; [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; break; + + case MGLOfflineTaskStateInvalid: + NSAssert(NO, @"Invalid offline task at index path %@", indexPath); + break; } } |