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

# Add custom raster source

![Load a custom raster source to Style and display it on a map as animated weather data using RasterLayer.](https://docs.mapbox.com/ios/assets/ideal-img/maps-examples-custom-raster-source.c9cf68f.480.png)

This example demonstrates how to add a [CustomRasterSource](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/customrastersource/) to a map using the **Mapbox Maps SDK for iOS**. A series of images are added to a layer and are store in a `CustomRasterSource` to then be added to the [`MapView`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapview/). We identify and then display the required tiles. Lastly the layer is styled with different color properties based on specified expressions.

In the code below, to create a `CustomRasterSource`, we start by defining a [`CustomRasterSourceClient`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/customrastersourceclient/) to detect once the `CustomRasterSource` is added. Next a [`CustomRasterSourceOptions`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/customrastersourceoptions/) struct is created and parameters are passed to adjust the zoom levels and tile sizes. Lastly, a [`RasterLayer`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/rasterlayer/) is added to the map with the custom data.

> **Note (warning): Image Dependencies**
> 
> This example uses 4 custom images:
> 
> -   [`wind_0.png`](https://github.com/mapbox/mapbox-maps-ios/tree/main/Sources/Examples/Assets.xcassets/RasterSource/wind_0.imageset/wind_0.png)
> -   [`wind_1.png`](https://github.com/mapbox/mapbox-maps-ios/tree/main/Sources/Examples/Assets.xcassets/RasterSource/wind_1.imageset/wind_1.png)
> -   [`wind_2.png`](https://github.com/mapbox/mapbox-maps-ios/tree/main/Sources/Examples/Assets.xcassets/RasterSource/wind_2.imageset/wind_2.png)
> -   [`wind_3.png`](https://github.com/mapbox/mapbox-maps-ios/tree/main/Sources/Examples/Assets.xcassets/RasterSource/wind_3.imageset/wind_3.png)
> 
> These images are all located in the [Examples repository in the RasterSource example assets folder](https://github.com/mapbox/mapbox-maps-ios/tree/11.29.0/Sources/Examples/Assets.xcassets/dataDrivenSymbolsExample).

> **Note: iOS Examples App Available**
> 
> This example code is part of the **Maps SDK for iOS Examples App**, a working iOS project [available on Github](https://github.com/mapbox/mapbox-maps-ios/tree/11.29.0/Sources/Examples). iOS developers are encouraged to run the examples app locally to interact with this example in an emulator and explore other features of the Maps SDK.
> 
> See our [Run the Maps SDK for iOS Examples App](https://docs.mapbox.com/help/tutorials/maps-sdk-ios-examples-app/) tutorial for step-by-step instructions.

**Swift**

Title: `CustomRasterSourceExample.swift`

[View on GitHub](https://github.com/mapbox/mapbox-maps-ios/blob/main/Sources/Examples/All%20Examples/CustomRasterSourceExample.swift)

```swift
import UIKit
import os
@_spi(Experimental) import MapboxMaps

final class ViewController: UIViewController {
    private var mapView: MapView!
    private var cancelables: Set<AnyCancelable> = []
    private var requiredTiles: [CanonicalTileID] = []

    private enum ID {
        static let customRasterSource = "custom-raster-source"
        static let rasterLayer = "customRaster"
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        mapView = MapView(frame: view.bounds, mapInitOptions: .init(cameraOptions: CameraOptions(center: .helsinki, zoom: 2)))
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(mapView)

        mapView.mapboxMap.onStyleLoaded.observeNext { [weak self] _ in
            self?.setupExample()
            
            
        }
        .store(in: &cancelables)
    }

    private func setupExample() {
        let rasterSourceClient = CustomRasterSourceClient.fromCustomRasterSourceTileStatusChangedCallback { [weak self] (tileID, status) in
            os_log(.info, "Tile status changed: tileId={%@}, status=%@", tileID.log, status.log)
            guard let self else { return }

            switch status {
            case .required:
                if !self.requiredTiles.contains(where: { $0 == tileID }) {
                    self.requiredTiles.append(tileID)
                }
                self.refreshTiles()
            case .notNeeded, .optional:
                if let index = self.requiredTiles.firstIndex(of: tileID) {
                    self.requiredTiles.remove(at: index)
                }
                try! self.mapView.mapboxMap.setCustomRasterSourceTileData(
                    forSourceId: ID.customRasterSource,
                    tiles: [CustomRasterSourceTileData(tileId: tileID, image: nil)])
            default: break
            }
        }
        let rasterSourceOptions = CustomRasterSourceOptions(clientCallback: rasterSourceClient, minZoom: 0, maxZoom: 0, tileSize: 256)
        let customRasterSource = CustomRasterSource(id: ID.customRasterSource, options: rasterSourceOptions)

        do {
            try mapView.mapboxMap.addSource(customRasterSource)

            var rasterLayer = RasterLayer(id: ID.rasterLayer, source: ID.customRasterSource)
            rasterLayer.rasterColorMix = .constant([1, 0, 0, 0])
            rasterLayer.rasterColor = .expression(
                Exp(.interpolate) {
                    Exp(.linear)
                    Exp(.lineProgress)
                    0
                    "rgba(0.0, 0.0, 0.0, 0.0)"
                    0.3
                    "rgba(7, 238, 251, 0.4)"
                    0.5
                    "rgba(0, 255, 42, 0.5)"
                    0.7
                    "rgba(255, 255, 0, 0.7)"
                    1
                    "rgba(255, 30, 0, 0.9)"
                }
            )
            try mapView.mapboxMap.addLayer(rasterLayer)
            refreshTiles()
        } catch {
            print("[Example/ViewController] Error:\(error)")
        }
    }

    private func refreshTiles() {
        let rasterImage = nextImage()
        let tiles = requiredTiles
            .map { CustomRasterSourceTileData(tileId: $0, image: rasterImage) }
        try! mapView.mapboxMap.setCustomRasterSourceTileData(forSourceId: ID.customRasterSource, tiles: tiles)
    }

    // MARK: Raster Images

    private var currentImageIndex = 0
    private let rasterImages: [UIImage] = [
        UIImage(named: "RasterSource/wind_0")!,
        UIImage(named: "RasterSource/wind_1")!,
        UIImage(named: "RasterSource/wind_2")!,
        UIImage(named: "RasterSource/wind_3")!,
    ]

    private func nextImage() -> UIImage {
        var currentImageIndex = self.currentImageIndex + 1
        if currentImageIndex >= self.rasterImages.endIndex {
            currentImageIndex = 0
        }
        return rasterImages[currentImageIndex]
    }
}
```