Skip to main content
This guide covers the setup process for integrating Open Mobile Maps into your SwiftUI application using the declarative MapView component.

Requirements

  • iOS 17.0+
  • Xcode 15.0+
  • Swift 5.9+
The SwiftUI MapView requires iOS 17.0 or later. For earlier iOS versions, use UIKit with MCMapView or wrap it using UIViewRepresentable.

Installation

Follow the same installation steps as the iOS setup guide using Swift Package Manager.

Basic setup

The SwiftUI MapView provides a declarative interface for displaying maps.
1

Import the framework

Import MapCore and SwiftUI in your view:
import MapCore
import SwiftUI
2

Create camera state

Define a @State property for the camera:
@State private var camera = MapView.Camera(
    latitude: 46.962592372639634,
    longitude: 8.378232525377973,
    zoom: 1000000
)
The camera binding is bidirectional - it updates as users interact with the map, and you can programmatically change it to update the map position.
3

Define layers

Create a state property for your map layers:
@State private var layers: [any Layer] = []
4

Add MapView to your view

Use the MapView in your SwiftUI body:
var body: some View {
    MapView(
        camera: $camera,
        layers: layers
    )
    .onAppear {
        setupLayers()
    }
}
5

Configure layers

Set up your layers in a separate method:
private func setupLayers() {
    layers = [
        TiledRasterLayer(
            "osm",
            webMercatorUrlFormat: "https://tiles.sample.org/{z}/{x}/{y}.png"
        )
    ]
}

Complete example

Here’s a complete working example:
ContentView.swift
import MapCore
import SwiftUI

struct ContentView: View {
    @State private var camera = MapView.Camera(
        latitude: 46.962592372639634,
        longitude: 8.378232525377973,
        zoom: 1000000
    )
    @State private var layers: [any Layer] = []
    
    var body: some View {
        MapView(
            camera: $camera,
            layers: layers
        )
        .onAppear {
            setupLayers()
        }
    }
    
    private func setupLayers() {
        layers = [
            TiledRasterLayer(
                "osm",
                webMercatorUrlFormat: "https://tiles.sample.org/{z}/{x}/{y}.png"
            )
        ]
    }
}

Multiple layers

Combine multiple layers by passing them in the layers array:
private func setupLayers() {
    layers = [
        TiledRasterLayer(
            "base",
            webMercatorUrlFormat: "https://tiles.sample.org/{z}/{x}/{y}.png"
        ),
        try! VectorLayer(
            "overlay",
            styleURL: "https://www.sample.org/overlay/style.json"
        )
    ]
}

Enable 3D mode

For 3D maps, use the is3D parameter:
MapView(
    camera: $camera,
    layers: layers,
    is3D: true
)

Custom map projection

Use a different coordinate system by providing a MapConfig:
MapView(
    camera: $camera,
    mapConfig: .init(
        mapCoordinateSystem: MCCoordinateSystemFactory.getEpsg2056System()
    ),
    layers: layers
)

Programmatic camera control

Update the camera binding to change the map position:
struct ContentView: View {
    @State private var camera = MapView.Camera(
        latitude: 46.962592372639634,
        longitude: 8.378232525377973,
        zoom: 1000000
    )
    @State private var layers: [any Layer] = []
    
    var body: some View {
        VStack {
            MapView(
                camera: $camera,
                layers: layers
            )
            .onAppear {
                setupLayers()
            }
            
            Button("Zoom In") {
                camera.zoom = (camera.zoom ?? 1000000) / 2
            }
            .padding()
        }
    }
    
    private func setupLayers() {
        layers = [
            TiledRasterLayer(
                "osm",
                webMercatorUrlFormat: "https://tiles.sample.org/{z}/{x}/{y}.png"
            )
        ]
    }
}

WMTS support

Parse WMTS capabilities to create layers:
private func setupWMTSLayer() {
    guard let resource = MCWmtsCapabilitiesResource.create(xml),
          let wmtsLayer = resource.createLayer(
              "identifier",
              tileLoader: MCTextureLoader()
          ) else {
        return
    }
    layers = [wmtsLayer]
}

Vector tiles

Add vector tile layers using the VectorLayer type:
private func setupLayers() {
    layers = [
        try! VectorLayer(
            "base-map",
            styleURL: "https://www.sample.org/base-map/style.json"
        )
    ]
}

Working with overlays

For overlay layers like polygons, icons, and lines, you’ll need to use UIViewRepresentable to wrap the MCMapView:

Polygon overlay

struct MapWithPolygonView: UIViewRepresentable {
    @Binding var camera: MapView.Camera
    let coords: [MCCoord]
    
    func makeUIView(context: Context) -> MCMapView {
        let mapView = MCMapView()
        
        // Add base layer
        mapView.add(layer: TiledRasterLayer(
            "osm",
            webMercatorUrlFormat: "https://tiles.sample.org/{z}/{x}/{y}.png"
        ))
        
        // Add polygon layer
        let polygonLayer = MCPolygonLayerInterface.create()
        let polygonInfo = MCPolygonInfo(
            identifier: "switzerland",
            coordinates: MCPolygonCoord(positions: coords, holes: []),
            color: UIColor.red.mapCoreColor,
            highlight: UIColor.red.withAlphaComponent(0.2).mapCoreColor
        )
        
        polygonLayer?.add(polygonInfo)
        mapView.add(layer: polygonLayer?.asLayerInterface())
        
        // Set initial camera position
        if let center = camera.center.value, let zoom = camera.zoom.value {
            mapView.camera.move(
                toCenterPositionZoom: center,
                zoom: zoom,
                animated: false
            )
        }
        
        return mapView
    }
    
    func updateUIView(_ uiView: MCMapView, context: Context) {
        // Handle camera updates if needed
    }
}

struct ContentView: View {
    @State private var camera = MapView.Camera(
        latitude: 46.962592372639634,
        longitude: 8.378232525377973,
        zoom: 1000000
    )
    
    var body: some View {
        MapWithPolygonView(
            camera: $camera,
            coords: [
                // your coordinates here
            ]
        )
    }
}

Icon overlay

struct MapWithIconView: UIViewRepresentable {
    @Binding var camera: MapView.Camera
    let coordinate: MCCoord
    let imageName: String
    
    func makeUIView(context: Context) -> MCMapView {
        let mapView = MCMapView()
        
        // Add base layer
        mapView.add(layer: TiledRasterLayer(
            "osm",
            webMercatorUrlFormat: "https://tiles.sample.org/{z}/{x}/{y}.png"
        ))
        
        // Add icon layer
        let iconLayer = MCIconLayerInterface.create()
        let image = UIImage(named: imageName)
        let texture = try! TextureHolder(image!.cgImage!)
        let icon = MCIconFactory.createIcon(
            "icon",
            coordinate: coordinate,
            texture: texture,
            iconSize: .init(
                x: Float(texture.getImageWidth()),
                y: Float(texture.getImageHeight())
            ),
            scale: .FIXED,
            blendMode: .NORMAL
        )
        iconLayer?.add(icon)
        mapView.add(layer: iconLayer?.asLayerInterface())
        
        // Set initial camera position
        if let center = camera.center.value, let zoom = camera.zoom.value {
            mapView.camera.move(
                toCenterPositionZoom: center,
                zoom: zoom,
                animated: false
            )
        }
        
        return mapView
    }
    
    func updateUIView(_ uiView: MCMapView, context: Context) {
        // Handle updates if needed
    }
}

Line overlay

struct MapWithLineView: UIViewRepresentable {
    @Binding var camera: MapView.Camera
    let coords: [MCCoord]
    
    func makeUIView(context: Context) -> MCMapView {
        let mapView = MCMapView()
        
        // Add base layer
        mapView.add(layer: TiledRasterLayer(
            "osm",
            webMercatorUrlFormat: "https://tiles.sample.org/{z}/{x}/{y}.png"
        ))
        
        // Add line layer
        let lineLayer = MCLineLayerInterface.create()
        lineLayer?.add(MCLineFactory.createLine(
            "lineIdentifier",
            coordinates: coords,
            style: MCLineStyle(
                color: MCColorStateList(
                    normal: UIColor.systemPink.withAlphaComponent(0.5).mapCoreColor,
                    highlighted: UIColor.blue.withAlphaComponent(0.5).mapCoreColor
                ),
                gapColor: MCColorStateList(
                    normal: UIColor.red.withAlphaComponent(0.5).mapCoreColor,
                    highlighted: UIColor.gray.withAlphaComponent(0.5).mapCoreColor
                ),
                opacity: 1.0,
                widthType: .SCREEN_PIXEL,
                width: 50,
                dashArray: [1, 1],
                dashFade: 0.0,
                dashAnimationSpeed: 0.0,
                lineCap: .BUTT,
                lineJoin: .ROUND,
                offset: 0.0,
                dotted: false,
                dottedSkew: 0.0
            )
        ))
        mapView.add(layer: lineLayer?.asLayerInterface())
        
        // Set initial camera position
        if let center = camera.center.value, let zoom = camera.zoom.value {
            mapView.camera.move(
                toCenterPositionZoom: center,
                zoom: zoom,
                animated: false
            )
        }
        
        return mapView
    }
    
    func updateUIView(_ uiView: MCMapView, context: Context) {
        // Handle updates if needed
    }
}

Next steps

UIKit setup

Learn how to use Open Mobile Maps with UIKit

Add layers

Learn how to add and customize map layers

Build docs developers (and LLMs) love