Offline

The Maps SDK enables you to download and store preselected regions for when the device goes offline. The resulting map is fully functional and includes the styles, tiles, and other resources required to draw a complete map.

Limitations

An app can download multiple regions for offline use, but the total offline download is capped at a maximum tile count “ceiling” across all downloaded regions. The SDK sets the tile ceiling to 6,000 tiles. Use our Tile Count Estimator to calculate the number of tiles required for your offline use case. Six thousand tiles cover a region roughly the size of Greater London within the M25 at zoom levels 0–15 or the contiguous United States at zoom levels 0–9. The size of these tiles on disk will vary according to the selected style.

The Maps SDK places no limit on the number of offline regions that may be created, as long as the cumulative number of tiles used does not exceed 6,000. Your Mapbox-powered application will reuse tiles and resources that are required by multiple regions, conserving network traffic and disk space. If you would like to increase your offline tile limit, you can do so by calling -[MGLOfflineStorage setMaximumAllowedMapboxTiles:]. Please note that you will be billed for those tiles should you reach this limit.

Additionally, the SDK places no limit on the download speed of offline regions, nor to disk space used by offline resources. The limits will depend on the storage capacity of the mobile device and the speed of the network to which it is connected.

Our terms of service do not allow you or an end user to redistribute offline maps downloaded from Mapbox servers.

Automatic tile updates

The Maps SDK downloads tiles when any connection is available, including over regular mobile data (2G, 3G, 4G, etc.). Because only individual highly-optimized tiles download, there is no risk of the user incurring an unexpected 100 MB download by opening the map in a region that's already downloaded. Unless the user is browsing 100 MB worth of tiles.

When the SDK automatically updates offline map tiles, such as when the cache expires, the offline region is not re-download from scratch. The offline tile update process is the same process as with regular map tiles: The map tile is only downloaded if there is a new version of that tile.

Downloading an offline map

Defining a region

Before a region can be used offline, the resources necessary for that region must be downloaded. Based on the parameters you specify when creating the region, the SDK calculates all resource requirements such as fonts, styles, and vector tiles covering the region. If more than one region in the offline database contains the fonts and styles, these will not be re-downloaded when another region with the same map style is downloaded. First, you'll need to define the pack to download by defining the region to save offline. You may also associate optional metadata with the pack if desired.

    let region = MGLTilePyramidOfflineRegion(styleURL: mapView.styleURL, bounds: mapView.visibleCoordinateBounds, fromZoomLevel: mapView.zoomLevel, toZoomLevel: mapView.zoomLevel + 2)

    // Store some data for identification purposes alongside the downloaded resources.

    let userInfo = ["name": "\(region.bounds)"]
    let context = NSKeyedArchiver.archivedData(withRootObject: userInfo)

Create offline pack

Next, you will need to register the region with the offline storage manager, and begin downloading the pack. You can also store any arbitrary metadata you would like to associate with the pack.

    // Create and register an offline pack with the shared offline storage object.

     MGLOfflineStorage.shared.addPack(for: region, withContext: context) { [self] (pack, error) in
         guard error == nil else {
             // The pack couldn’t be created for some reason.
             print("Error: \(error?.localizedDescription ?? "unknown error")")
             return
         }
         // Start downloading.
         pack!.resume()
     }

Besides creating the offline pack, you can also update the information stored, allowing your users, for example, to update the region name. The MGLOfflineStorage object provides a method called addPack which takes in both the updated offline pack and a callback to be notified when the update is completed, or an error occurs.

Start download

To make this one seamless operation, we can place the definition of our region and context, as well as the addition of our offline pack to MGLOfflineStorage, within one function startOfflinePackDownload.

Now that the region has been created, you can call startOfflinePackDownload to begin an asynchronous download.

extension UIViewController: MGLMapViewDelegate {
    func mapViewDidFinishLoadingMap(_ mapView: MGLMapView) {
       // put all the code here to create and download the pack instead of startOfflinePackDownload()
    }
}

Managing downloaded regions

Once you or your user has downloaded a region, the Maps SDK provides a few options to handle gathering a list, positioning the camera inside the downloaded region, and a method for deleting a region.

List offline regions

The listing of regions is useful for presenting downloaded information to your user. For example, displaying all downloaded packs in a UITableView could be useful to users who want to manage their downloaded offline maps. Accessing these packs can be done by way of the MGLOfflinePacks.shared.packs property, as outlined in our manage offline regions example.

Delete a region

To remove an offline region from the database, call MGLOfflineStorage.shared.removePack:withCompletionHandler:. Similarly to addPack, this method can be called with optional completion handler that returns an error if the pack can't be removed.

Deleting a region will result in also removing the least recently requested resources not required by other regions until the database shrinks below a certain size.

guard let lastPack = MGLOfflineStorage.shared.packs?.last else { return }

MGLOfflineStorage.shared.removePack(lastPack) { (error) in
    // Handle error if pack can't be removed.
    if let error = error {
        print(error)
    } else {
        // Perform some action upon successful pack removal.
    }
}