# Map Download lifecycle

In this guide we are going to cover the offline maps download life-cycle and how to monitor statuses. The SDK prepares the shared Preview Bundle and regional packages in parallel. Call `beginDownloadPage(withOwner:)` when the download screen appears and `endDownloadPage()` when it closes. This lets the SDK prepare shared preview resources early while keeping screen ownership bound to the active UI.

```swift
final class RegionalOfflineDownloadViewController: UIViewController {
 
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NGLRegionalOffline.beginDownloadPage(withOwner: self)
    }
 
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
 
        if isMovingFromParent || navigationController?.isBeingDismissed == true {
            NGLRegionalOffline.endDownloadPage()
        }
    }
}
```

> _The first use requires an internet connection. After the shared resources have been cached successfully, the SDK can load them from local storage._

A task can remain in `.preparingOfflineDisplay` at up to `99%` while the SDK prepares preview resources. The task reaches `100%` only after the preview style, TileJSON/source, glyphs, sprites, and preview database have been validated and applied to the map engine.

## Observe shared preview resource status

```swift
private var importToken: NGLRegionalOfflineListenerToken?
 
func observePreviewResources() {
    importToken = NGLRegionalOffline.addPreviewBundleImportStatusListener {
        [weak self] status in
        guard let self else { return }
 
        if status.previewBundleReady {
            statusLabel.text = "Offline resources are ready"
        } else if status.waitingForNetwork {
            statusLabel.text = "Waiting for a network connection"
        } else if status.ensuring {
            statusLabel.text = "Preparing offline resources"
        } else if let message = status.lastErrorMessage {
            statusLabel.text = message
        }
    }
}
```

Read the current status synchronously when needed:

```swift
let status = NGLRegionalOffline.previewBundleImportStatusSync()
let previewResourcesAreReady = status.previewBundleReady
```

Remove the listener when it is no longer needed:

```swift
func removePreviewResourceObserver() {
    if let importToken {
        NGLRegionalOffline.removePreviewBundleImportStatusListener(importToken)
        self.importToken = nil
    }
}
```
