> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/ios/navigation/llms.txt)

# Offline

There are two main kinds of offline functionality: predictive caching and offline regions. Predictive caching is fully automatic and based on the driver's behavior. It allows offline navigation around the user's current location, the destination, and the route itself. Offline regions need to be created and loaded ahead of time, enabling routing functionality in non-connected environments.

## Predictive caching

> **Note (warning): Map style support for predictive caching**
> 
> Predictive caching only supports map styles that do not use [source compositing](https://docs.mapbox.com/console-tools/studio/studio-interface/#source-compositing). You may need to check your style in Mapbox Studio to make sure source compositing is turned off. Mapbox styles like Mapbox Streets and Mapbox Outdoors have compositing turned on and **will not work with predictive caching by default**.
> 
> To use a Default Mapbox style with predictive caching:
> 
> 1.  [Create a new style](https://docs.mapbox.com/console-tools/studio/#getting-started).
> 2.  Turn off compositing in your style's [settings](https://docs.mapbox.com/console-tools/studio/studio-interface/#settings).
> 3.  [Publish](https://docs.mapbox.com/console-tools/studio/studio-interface/#publish) your style.
> 4.  Reference your style in your application. Predictive caching only supports sources hosted with Mapbox. These sources will have a URL starts with `mapbox://`.

### Configuration

Predictive caching is disabled by default in the SDK. To enable it you need to set a non-nil value to [`predictiveCacheConfig`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationcore/coreconfig/predictivecacheconfig/):

```swift
let coreConfig = CoreConfig(predictiveCacheConfig: .init())
let mapboxNavigationProvider = MapboxNavigationProvider(coreConfig: coreConfig)
```

### Customization

When predictive caching is enabled, the Navigation SDK will create a cache of data within three configurable boundaries, each with its own default value:

1.  **Radius around the user's location**. Defaults to 2,000 meters. Use [`PredictiveCacheLocationConfig.currentLocationRadius`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationcore/predictivecachelocationconfig/currentlocationradius/) to configure this value.
2.  **Buffer around the route**. Defaults to 500 meters. Use [`PredictiveCacheLocationConfig.routeBufferRadius`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationcore/predictivecachelocationconfig/routebufferradius/) to configure this value.
3.  **Radius around the destination**. Defaults to 5,000 meters. Use [`PredictiveCacheLocationConfig.destinationLocationRadius`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationcore/predictivecachelocationconfig/destinationlocationradius/) to configure this value.

Options for navigation and for maps data caching are configured separately. To configure predictive caching, pass values, in meters, to [`PredictiveCacheConfig`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationcore/predictivecacheconfig/):

```swift
var coreConfig = CoreConfig()
coreConfig.predictiveCacheConfig = PredictiveCacheConfig(
    predictiveCacheNavigationConfig: .init(locationConfig: .init(
        currentLocationRadius: 2000.0,
        routeBufferRadius: 300.0,
        destinationLocationRadius: 3000.0
    )),
    predictiveCacheMapsConfig: .init(
        locationConfig: .init(currentLocationRadius: 2000.0),
        zoomRange: 5...12
    ),
    predictiveCacheSearchConfig: .init(
        locationConfig: .init(currentLocationRadius: 2000.0),
        searchTilesetDescriptor: MapboxSearch.SearchOfflineManager.createDefaultTilesetDescriptor()
    )
)
let mapboxNavigationProvider = MapboxNavigationProvider(coreConfig: coreConfig)
```

### Predictive caching with `NavigationMapView`

To add predictive caching to your map view, pass [`PredictiveCacheManager`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationcore/predictivecachemanager/) instance into the [`NavigationMapView`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationcore/navigationmapview/init(location:routeprogress:navigationcameratype:heading:predictivecachemanager:)/) initializer.

```swift
let navigationMapView = NavigationMapView(
    location: mapboxNavigation.navigation()
        .locationMatching.map(\.mapMatchingResult.enhancedLocation)
        .eraseToAnyPublisher(),
    routeProgress: mapboxNavigation.navigation()
        .routeProgress.map(\.?.routeProgress)
        .eraseToAnyPublisher(),
    predictiveCacheManager: mapboxNavigationProvider.predictiveCacheManager
)
```

### Predictive caching with `NavigationViewController`

To add predictive caching to your application, pass [`PredictiveCacheManager`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationcore/predictivecachemanager/) instance into the [`NavigationOptions`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationuikit/navigationoptions/) initializer as you configure a [`NavigationViewController`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationuikit/navigationviewcontroller/).

```swift
let navigationOptions = NavigationOptions(
    mapboxNavigation: mapboxNavigation,
    voiceController: mapboxNavigationProvider.routeVoiceController,
    eventsManager: mapboxNavigationProvider.eventsManager(),
    predictiveCacheManager: mapboxNavigationProvider.predictiveCacheManager
)
let navigationViewController = NavigationViewController(
    navigationRoutes: navigationRoutes,
    navigationOptions: navigationOptions
)
```

### Estimate predictive caching data use

Data usage depends on the configured radius and the dataset being used. For default Mapbox data, example usage is:

-   Berlin, 15km radius: 136 MB maps, 42 MB navigation
-   Chicago, 15km radius: 74 MB maps, 43 MB navigation
-   London, 15km radius: 145 MB maps, 90 MB navigation
-   Atlanta, 100 miles radius: 659 MB maps, 156 MB navigation

## Offline regions

[`TileStore`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxcommon/tilestore/) allows users to create and download offline regions ahead of time, enabling routing functionality in non-connected environments. In areas with no cellular connectivity, or on a device with no SIM card, users can take advantage of turn-by-turn navigation and request new routes. If they go off-route, the Mapbox Navigation SDK can reroute and keep them headed to their destination without requiring network connectivity. `TileStore` copies the routing data onto the user's device, so there's no need to make HTTP API calls for routing information.

### Customization

> **Note: TileStore and the Maps SDK**
> 
> `TileStore` handles offline region data for both navigation data for generating routes and map tiles for displaying a map in your application.
> 
> Find more details on how to use `TileStore` for map data in the [Maps SDK documentation](https://docs.mapbox.com/ios/maps/guides/offline/)

Though not recommended, the tile store can also be configured with a custom storage location in [`TileStoreConfiguration`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationcore/tilestoreconfiguration/):

```swift
let mapboxNavigationProvider = MapboxNavigationProvider(
    coreConfig: .init(
        tilestoreConfig: .custom(URL(filePath: "path/to/cache/directory/"))
    )
)
```

### Access `TileStore` instance

You don't need to initialize a `TileStore` instance. Instead, access the [`tileStore`](https://docs.mapbox.com/ios/navigation/api/3.28.3/navigation/documentation/mapboxnavigationcore/tilestoreconfiguration/location/tilestore/) instance created by the Navigation SDK.

```swift
let tilestoreConfig = mapboxNavigationProvider.coreConfig.tilestoreConfig
let tileStore = tilestoreConfig.navigatorLocation.tileStore
```

### Download a tile region

To download a new tile region or update an existing one, create an asynchronous tile region download by calling the tile store's [`loadTileRegion`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxcommon/tilestore/loadtileregion(forid:loadoptions:progress:completion:)/) method and passing in the [`TileRegionLoadOptions`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxcommon/tileregionloadoptions/) and tile region ID.

To create a new tile region, start by defining [`TileRegionLoadOptions`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxcommon/tileregionloadoptions/). You must provide at least two pieces of information to `TileRegionLoadOptions`:

-   `geometry`: The tile region's associated geometry (for example `Polygon` or `MultiPolygon`). Developer may need to import [`Turf`](https://github.com/mapbox/turf-swift) to create one of these types.
    
-   `descriptors`: [`TilesetDescriptor`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxcommon/tilesetdescriptor/) object is a bundle that encapsulates tilesets creation for the tile store implementation. Maps tileset descriptor contains metadata about the tilesets, zoom ranges, and pixel ratio that cached tile packs should include.
    

```swift
let tilesetDescriptorOptions = TilesetDescriptorOptions(styleURI: styleURI,
                                                        zoomRange: zoomRange,
                                                        tilesets: tilesets)
let tilesetDescriptor: TilesetDescriptor = offlineManager.createTilesetDescriptor(for: tilesetDescriptorOptions)
let tileRegionLoadOptions = TileRegionLoadOptions(geometry: geometry,
                                                  descriptors: [tilesetDescriptor],
                                                  acceptExpired: true)!
tileStore.loadTileRegion(forId: tileRegionId,
                         loadOptions: tileRegionLoadOptions) { result in
    switch result {
    case let .success(region):
        print("\(region.id) downloaded!")
    case let .failure(error):
        print("Error while downloading region: \(error)")
    }
}
```

### List tile regions

Call the tile store's [`allTileRegions`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxcommon/tilestore/alltileregions(completion:)/) method to get a list of available tile regions. This will return either an object that includes a list of available tile regions or an error.

```swift
tileStore.allTileRegions { result in
    switch result {
    case .success(let tileStoreRegions):
        handleAllTileRegionsSuccess(tileStoreRegions)
    case .failure(let error):
        handleAllTileRegionsFailure(error)
    }
}
```

### Delete tile regions

You can delete a tile region with the tile store's [`removeTileRegion(forId:)`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxcommon/tilestore/removetileregion(forid:)/) method.

```swift
tileStore.removeTileRegion(forId: regionId)
```

### Observe tile regions state

You can add [`TileStoreObserver`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/tilestoreobserver/) to be notified when the state of any tile region changes.

```swift
class CustomTileStoreObserver: TileStoreObserver {
    func onRegionLoadProgress(forId id: String, progress: TileRegionLoadProgress) {
        // Called whenever the load progress of a TileRegion changes.
    }

    func onRegionLoadFinished(forId id: String, region: Result<TileRegion, Error>) {
        // Called once a TileRegion load completes successfully, or is aborted due to cancellation or errors.
    }

    func onRegionRemoved(forId id: String) {
        // Called when a TileRegion is removed
    }

    func onRegionGeometryChanged(forId id: String, geometry: Geometry?) {
        // Called when the geometry of a TileRegion is modified.
    }

    func onRegionMetadataChanged(forId id: String, value: Any) {
        // Called when the user-provided metadata associated with a TileRegion is changed.
    }
}

let tileStoreObserver = CustomTileStoreObserver()
subscription = tileStore.subscribe(tileStoreObserver)
```