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

# SwiftUI

The Mapbox Maps SDK has a complete support of SwiftUI. This guide demonstrates how to integrate Mapbox Maps into your SwiftUI application.

You can find working [SwiftUI examples](https://github.com/mapbox/mapbox-maps-ios/tree/main/Sources/Examples/SwiftUI%20Examples//) in the [Examples](https://github.com/mapbox/mapbox-maps-ios/tree/main/Sources/Examples/) application.

### Feature support

The SwiftUI [`Map`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/map/) wraps the existing [`MapView`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapview/), giving SwiftUI apps full access to the power of the Mapbox Maps SDK.

Note that not every single API is exposed in SwiftUI, you can track the progress in the table below.

| Feature | Status | Note |
| --- | --- | --- |
| Viewport & Camera | ✅ |  |
| View Annotations | ✅ |  |
| Layer Annotations | ✅ | `isDraggable`, `isSelected` are not supported |
| Annotations Clustering | ✅ |  |
| View Annotations | ✅ |  |
| Puck 2D/3D | ✅ |  |
| Map Events | ✅ |  |
| Gesture Configuration | ✅ |  |
| Ornaments Configuration | ✅ |  |
| Style API | ✅ | Explore [Declarative Map Styling](https://docs.mapbox.com/ios/ja/maps/guides/styles/declarative-map-styling/) user guide. |
| Custom Camera Animations | 🚧 |  |

### Getting started

To start using Mapbox Map in SwiftUI you need to import `SwiftUI` and `MapboxMaps`.

```swift
import SwiftUI
import MapboxMaps
```

Then you can use [`Map`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/map/) to display map content.

```swift
struct ContentView: View {
    init() {
        MapboxOptions.accessToken = "pk..."
    }
    var body: some View {
        Map()
          .ignoresSafeArea()
    }
}
```

Note that you have to set the Mapbox Access Token at any time before using the [`Map`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/map/). You can do it either by setting `MapboxOptions.accessToken` or by putting it into your application `Info.plist` as described in the [Get Started](https://docs.mapbox.com/ios/ja/maps/guides/install#step-2-configure-your-public-token) guide.

## Concepts

### Setting Map style

By default the map uses the new [`standard`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapstyle/standard/) style which brings rich 3D visualization. But you can use [`mapStyle(_:)`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/map/mapstyle(_:)/) to set any other style.

```swift
Map()
  .mapStyle(.standard) // Sets Mapbox Standard Style.
```

With the Standard style you can set the `lightPresets` of the style according to your application's `colorScheme`. Light presents are 4 time-of-day states (`dawn`, `day`, `dusk`, `night`) that set the lighting and shadows of the map to represent changes in daylight.

```swift
struct ContentView: View {
    @Environment(\.colorScheme) var colorScheme
    var body: some View {
        Map()
            .mapStyle(.standard(lightPreset: colorScheme == .light ? .day : .dusk))
    }
}
```

Also, you always can use your custom Mapbox Styles built with [Mapbox Studio](https://studio.mapbox.com/).

```swift
Map()
    .mapStyle(.myCustomStyle)

extension MapStyle {
  static let myCustomStyle = MapStyle(uri: StyleURI(rawValue: "mapbox://...")!)
}
```

Consult the [`MapStyle`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapstyle/) documentation to find more information about style loading.

### Declarative Map Styling

With the advent of Declarative Map Styling, it's now possible to reuse [`MapStyleContent`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapstylecontent/) components within SwiftUI, offering a robust and exhaustive method to delineate map content comprehensively in one place.

The following example illustrates the how to use [`MapStyleContent`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapstylecontent/), which can also be utilized outside of SwiftUI, and SwiftUI-specific [`MapContent`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapcontent/) within a singular declarative [`Map`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/map/) description:

```swift
Map(initialViewport: .camera(center: .init(latitude: 27.2, longitude: -26.9), zoom: 1.53, bearing: 0, pitch: 0)) {
    MapViewAnnotation(coordinate: .apple) {
        Circle()
            .fill(.purple)
            .frame(width: 40, height: 40)
    }

     PolygonAnnotation(polygon: Polygon(center: .apple, radius: 8 * 100, vertices: 60))
        .fillColor(StyleColor(.yellow))


    GeoJSONSource(id: "source")
        .data(.geometry(.polygon(Polygon(center: .apple, radius: 4 * 100, vertices: 60))))

    FillLayer(id: "fill-id", source: "source")
        .fillColor(.green)
        .fillOpacity(0.7)
}
```

Within SwiftUI, all [`MapStyleContent`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapstylecontent/) elements will be retained during style reloads and appropriately re-added. This ensures that the declaration itself remains the single source of truth for map content. SwiftUI's [`MapContent`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapcontent/) serves as an extension of the Declarative Map Styling approach introduced for the UIKit API. Thus, it's advisable to peruse the [Declarative Map Styling](https://docs.mapbox.com/ios/ja/maps/guides/styles/declarative-map-styling/) guide to become acquainted with the underlying concepts of this declarative styling paradigm.

### Displaying a user's location

To display a user's location, you will need to access the device's location and then render a location puck at its coordinates.

To request access to the user's location data, you will need to add the following location permission to your `info.plist`.

```Info.plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your precise location is used to calculate turn-by-turn directions, show your location on the map, and help improve the map.</string>
```

Once you set up location permissions, you can add the code below to your `ContentView.swift` file. By default, Mapbox creates an instance of [`AppleLocationProvider`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/applelocationprovider/) to receive location data from the user's device. Creating a location puck will access and render the device's location data on your map.

```swift
import SwiftUI
import MapboxMaps

struct ContentView: View {
    
    @State var viewport: Viewport = .followPuck(zoom: 13, bearing: .heading)
    var body: some View {
            Map(viewport: $viewport) {
                Puck2D(bearing: .heading)
            }
            .ignoresSafeArea()
        }
}
```

> **Note (warning): Troubleshooting with the iOS simulator**
> 
> If you cannot see the location puck while testing in the iOS simulator, try the following:
> 
> -   Simulate a location in Xcode by going to **Debug > Simulate Location** and select a location. Then, return to the Simulator and the location puck should appear over the selected location.
> -   If you already declined location permissions for your application you can update permission in Settings. Go to **Settings > Privacy & Security > Location Services**. Then find your application and update the location access to "Always" or "While Using the App".

> **Related content (guide): [User Location Guide](https://docs.mapbox.com/ios/ja/maps/guides/user-location/)**
> 
> To learn more about user location data, see the User Location guide.

### Using Viewport to manage camera

[`Viewport`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/viewport/) is a powerful abstraction that manages the camera in SwiftUI. It supports multiple modes, such as `camera`, `overview`, `followPuck`, and others.

For example, with [`camera(center:anchor:zoom:bearing:pitch:)`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/viewport/camera(center:anchor:zoom:bearing:pitch:)/) you can set the camera parameters directly to the map.

```swift
let london = CLLocationCoordinate2D(latitude: 51.5073219, longitude: -0.1276474)
// Sets camera centered to London
Map(initialViewport: .camera(center: london, zoom: 12, bearing: 0, pitch: 0)
```

In the example above, the map uses the `initialViewport` only during initialization. After the user drags the map, you can't update the viewport again. In contrast, the example below uses a `@State` variable with two-way data binding, allowing your code to update the viewport whenever needed. Choose the approach that aligns with your use case.

```swift
struct ContentView: View {
    // Initializes viewport state as styleDefault,
    // which will use the default camera for the current style.
    @State var viewport: Viewport = .styleDefault

    var body: some View {
        VStack {
            // Passes the viewport binding to the map.
            Map(viewport: $viewport)
            Button("Overview route") {
                // Sets the viewport to overview (fit) the route, or any other geometry.
                viewport = .overview(geometry: LineString(...))
            }
            Button("Locate the user") {
                // Sets viewport to follow the user location.
                viewport = .followPuck(zoom: 16, pitch: 60)
            }
        }
    }
}
```

When the user drags the map, the viewport always resets to [`idle`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/viewport/idle/) state. You can't read the actual current camera state from that viewport, but you can observe it via [`onCameraChanged(action:)`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/map/oncamerachanged(action:)/).

It's not recommended to store the camera values received from [`onCameraChanged(action:)`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/map/oncamerachanged(action:)/) in `@State` property. They come with high frequency, which may lead to unwanted `body` re-execution and high CPU consumption. It's better to store them in model, or throttle before setting them to @State.

### Viewport animations

The viewport changes can be animated using the [`withViewportAnimation(_:body:completion:)`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/withviewportanimation(_:body:completion:)/) function.

```swift
struct ContentView: View {
    @State var viewport: Viewport = .styleDefault


    var body: some View {
        VStack {
            Map(viewport: $viewport)
            Button("Animate viewport") {
                // Changes viewport with default animation
                withViewportAnimation {
                    viewport = .followPuck
                }
            }
            Button("Animate viewport (ease-in)") {
                // Changes viewport with ease-in animation
                withViewportAnimation(.easeIn(duration: 1)) {
                    viewport = .followPuck
                }
            }
        }
    }
}
```

Consult the [`ViewportAnimation`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/viewportanimation/) documentation to learn more about supported animations.

It's recommended to use [`default(maxDuration:)`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/viewportanimation/default(maxduration:)/) animation when transitioning to [`followPuck(zoom:bearing:pitch:)`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/viewport/followpuck(zoom:bearing:pitch:)/) state. With other animation types, there might be a jump when animation finishes. It may happen because they're designed to finish at the static target.

### Annotations

There are two kinds of annotations in Maps SDK - View Annotations ([`MapViewAnnotation`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapviewannotation/)) and Layer Annotations (a.k.a [`PointAnnotation`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/pointannotation/), [`CircleAnnotation`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/circleannotation/), etc).

#### View Annotations

View annotation allow you to display any SwiftUI view on top of the map. They give you endless possibility for customization, but may be less performant. Also, they are always displayed above all map content, you cannot place them between map layers.

The example below displays multiple view annotations.

```swift
struct ContentView: View {
    struct Item: Identifiable {...}
    @state var items = [Item]()

    var body: some View {
        Map {
            // Displays a single view annotation at specified coordinate.
            MapViewAnnotation(coordinate: CLLocationCoordinate(...))
                Text("🚀")
                    .frame(width: 20, height: 20)
                    .background(Circle().fill(.red))
            }

            // Displays multiple data-driven view annotations.
            ForEvery(items) { item in
                MapViewAnnotation(coordinate: item.coordinate) {
                    ItemContentView(item)
                }
            }

            // Displays annotation on the layer feature.
            // The annotation will be dynamically positioned along the route line
            // that is displayed by "route" layer.
            MapViewAnnotation(layerId: "route") {
                ETAView(text: "55 min")
            }
        }
    }
}
```

The [`ForEvery`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/forevery/) in the above example is like `ForEach` in SwiftUI, but works with Map content.

All View annotations may be configured via modifier functions (see [`MapViewAnnotation`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapviewannotation/) for the full list):

```swift
MapViewAnnotation(coordinate: CLLocationCoordinate(...))
    Text("🚀")
        .frame(width: 20, height: 20)
        .background(Circle().fill(.red))
}
.allowOverlap(true) // will overlap with outer annotations
.variableAnchors([
    ViewAnnotationAnchorConfig(anchor: .bottom) // Anchor will be at the bottom
])
```

#### Layer Annotations

Layer annotations are rendered natively in the map using layers. They can be placed between map layers, support clustering (for [`PointAnnotation`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/pointannotation/)s only) and are usually more performant.

The example below displays different types of layer annotations.

```swift
struct ContentView: View {
    struct Item {...}
    @state var items = [Item]()

    var body: some View {
        Map {
            /// Displays a polygon annotation
            let polygon = Polygon(...)
            PolygonAnnotation(polygon: polygon)
                .fillColor(StyleColor(.systemBlue))
                .fillOpacity(0.5)
                .fillOutlineColor(StyleColor(.black))
                .onTapGesture {
                    print("Polygon is tapped")
                }

            /// Displays a single point annotation
            PointAnnotation(...)

            /// Displays data-driven group of point annotations.
            PointAnnotationGroup(items, id: \.id) { item in
                PointAnnotation(coordinate: item.coordinate)
                    .image(named: "dest-pin")
                    .iconAnchor(.bottom)
            }
            .clusterOptions(ClusterOptions(...))
        }
    }
```

In example above you can see that `PointAnnotation` (and other types of layer annotations) can be placed alone, or by using an annotation group, such as [`PointAnnotationGroup`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/pointannotationgroup/).

The first method is a handy way to place only one annotation of its kind. The second is better for multiple annotations and gives more configuration options such as clustering, layer position, and more. Annotation groups also behave like [`ForEvery`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/forevery/) for layer annotations.

### Displaying user position

The Puck allows you to display the user position on the map. The puck can be 2D or 3D.

The example below displays the user position using 2D puck.

```swift
Map {
    Puck2D(bearing: .heading)
        .showsAccuracyRing(true)
}
```

The example below displays the user position using custom 3D model.

```swift
Map {
    let duck = Model(
        uri: URL(string: "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Embedded/Duck.gltf")!,
        orientation: [0, 0, -90])
    Puck3D(model: duck, bearing: .heading)
}
```

If you add multiple pucks into one map, only the last one will be displayed.

### Direct access to the underlying map implementation.

If some API is not yet exposed in SwiftUI, you can use `MapReader` to access the underlying map implementation.

```swift
var body: some View {
    MapReader { proxy in
        Map()
            .onAppear {
                configureUnderlyingMap(proxy.map)
            }
    }
}
```

We welcome your feedback on the SwiftUI support. If you have any questions or comments, open an [issue in the Mapbox Maps SDK repository](https://github.com/mapbox/mapbox-maps-ios/issues/) and add the `SwiftUI` label.