
Mapbox Ios Patterns
- 1k installs
- 71 repo stars
- Updated August 4, 2026
- mapbox/mapbox-agent-skills
mapbox-ios-patterns is a Mapbox reference skill that delivers correct SwiftUI and UIKit integration patterns for developers adding Mapbox Maps SDK v11 maps to iOS applications.
About
mapbox-ios-patterns is a quick-reference skill from mapbox/mapbox-agent-skills for Mapbox Maps SDK v11 on iOS with Swift, SwiftUI, and UIKit. It documents Swift Package Manager installation from github.com/mapbox/mapbox-maps-ios.git at version 11.0.0+, Info.plist MBXAccessToken setup, and SwiftUI Map views with Viewport camera bindings. Developers reach for it when implementing interactive maps, style layers, or location features and need copy-paste-correct SDK v11 APIs instead of outdated Mapbox examples. The skill accelerates iOS map feature work by centralizing token config, imports, and viewport patterns in one agent-accessible reference.
- Complete Mapbox Maps SDK v11 iOS quick reference
- Side-by-side SwiftUI and UIKit code examples
- SPM installation and Info.plist token setup
- Viewport, annotations, and mapStyle patterns
- Ready-to-copy snippets for common map tasks
Mapbox Ios Patterns by the numbers
- 1,007 all-time installs (skills.sh)
- +36 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #383 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mapbox/mapbox-agent-skills --skill mapbox-ios-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1k |
|---|---|
| repo stars | ★ 71 |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | mapbox/mapbox-agent-skills ↗ |
How do you add Mapbox maps to a SwiftUI iOS app?
Get instant, correct SwiftUI and UIKit patterns when adding Mapbox maps to an iOS app.
Who is it for?
iOS developers integrating Mapbox Maps SDK v11 who need verified SwiftUI and UIKit snippets during active feature development.
Skip if: Android or web Mapbox projects, or teams already standardized on MapKit without Mapbox SDK requirements.
When should I use this skill?
The user adds Mapbox to an iOS app, configures MBXAccessToken, or needs SwiftUI Map viewport patterns for SDK v11.
What you get
Working iOS Mapbox integration with SPM dependency, access token config, and SwiftUI/UIKit map view code.
- SPM dependency config
- Info.plist token entry
- SwiftUI map view code
By the numbers
- Targets Mapbox Maps SDK v11 (11.0.0+) via Swift Package Manager
Files
Mapbox iOS Integration Patterns
Official patterns for integrating Mapbox Maps SDK v11 on iOS with Swift, SwiftUI, and UIKit.
Use this skill when:
- Installing and configuring Mapbox Maps SDK for iOS
- Adding markers and annotations to maps
- Showing user location and tracking with camera
- Adding custom data (GeoJSON) to maps
- Working with map styles, camera, or user interaction
- Handling feature interactions and taps
Official Resources:
---
Installation & Setup
Requirements
- iOS 14+
- Xcode 15+
- Swift 5.9+
- Free Mapbox account
Step 1: Configure Access Token
Add your public token to Info.plist:
<key>MBXAccessToken</key>
<string>pk.your_mapbox_token_here</string>Get your token: Sign in at mapbox.com
Step 2: Add Swift Package Dependency
1. File → Add Package Dependencies 2. Enter URL: https://github.com/mapbox/mapbox-maps-ios.git 3. Version: "Up to Next Major" from 11.0.0 4. Verify four dependencies appear: MapboxCommon, MapboxCoreMaps, MapboxMaps, Turf
Alternative: CocoaPods or direct download (install guide)
---
Map Initialization
SwiftUI Pattern
Basic map:
import SwiftUI
import MapboxMaps
struct ContentView: View {
@State private var viewport: Viewport = .camera(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
zoom: 12
)
var body: some View {
Map(viewport: $viewport)
.mapStyle(.standard)
}
}With ornaments:
Map(viewport: $viewport)
.mapStyle(.standard)
.ornamentOptions(OrnamentOptions(
scaleBar: .init(visibility: .visible),
compass: .init(visibility: .adaptive),
logo: .init(position: .bottomLeading)
))UIKit Pattern
import UIKit
import MapboxMaps
class MapViewController: UIViewController {
private var mapView: MapView!
override func viewDidLoad() {
super.viewDidLoad()
let options = MapInitOptions(
cameraOptions: CameraOptions(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
zoom: 12
)
)
mapView = MapView(frame: view.bounds, mapInitOptions: options)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)
mapView.mapboxMap.loadStyle(.standard)
}
}---
Add Markers
The SDK offers three ways to place a point on the map. Pick the simplest one that fits.
Which API should I use?
| API | Use it when | Platforms | Notes |
|---|---|---|---|
Marker (Markers API) | You need a default pin and don't have a custom image asset | SwiftUI only | No image assets required. Experimental SPI — needs @_spi(Experimental) import MapboxMaps. Best < 100 markers. |
PointAnnotation | You have a custom image and want layer-level placement | SwiftUI + UIKit | Backed by a symbol layer, so it scales well to hundreds of markers. Accepts any UIImage that UIKit can render. |
View annotations (ViewAnnotation / MapViewAnnotation) | You want to render a full native view (card, badge, animated content) anchored to a coordinate | SwiftUI + UIKit | SwiftUI uses MapViewAnnotation; UIKit uses mapView.viewAnnotations with a ViewAnnotation. Each annotation is a real view — costs more than PointAnnotation at scale. |
For hundreds or thousands of features, use a style layer (SymbolLayer on a GeoJSONSource) instead of annotations.
Markers API (recommended for simple cases, SwiftUI)
import SwiftUI
@_spi(Experimental) import MapboxMaps
struct ContentView: View {
var body: some View {
Map {
Marker(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
.color(.red)
.text("San Francisco")
}
}
}Multiple markers from a collection:
Map {
ForEvery(locations, id: \.id) { location in
Marker(coordinate: location.coordinate)
.color(.red)
.text(location.name)
}
}Scaling note.MarkerandPointAnnotationeach create their own view or symbol entry per pin — fine up to about 100 markers. For larger datasets (hundreds or thousands of features — common with open-ended GeoJSON feeds), load the data into aGeoJSONSourceand render it with aSymbolLayerinstead. That scales to thousands of features and enables clustering.
PointAnnotation (custom image)
SwiftUI:
Map(viewport: $viewport) {
PointAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
.image(.init(image: UIImage(named: "marker")!, name: "marker"))
}UIKit:
// Create annotation manager (once, reuse for updates)
var pointAnnotationManager = mapView.annotations.makePointAnnotationManager()
// Create marker
var annotation = PointAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
annotation.image = .init(image: UIImage(named: "marker")!, name: "marker")
annotation.iconAnchor = .bottom
// Add to map
pointAnnotationManager.annotations = [annotation]Multiple markers:
let annotations = locations.map { coordinate in
var annotation = PointAnnotation(coordinate: coordinate)
annotation.image = .init(image: UIImage(named: "marker")!, name: "marker")
return annotation
}
pointAnnotationManager.annotations = annotations---
Show User Location
Step 1: Add location permission to Info.plist:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Show your location on the map</string>Step 2: Request permissions and show location:
import CoreLocation
// Request permissions
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
// Show user location puck
mapView.location.options.puckType = .puck2D()
mapView.location.options.puckBearingEnabled = true---
Performance Best Practices
Reuse Annotation Managers
// ❌ Don't create new managers repeatedly
func updateMarkers() {
let manager = mapView.annotations.makePointAnnotationManager()
manager.annotations = markers
}
// ✅ Create once, reuse
let pointAnnotationManager: PointAnnotationManager
init() {
pointAnnotationManager = mapView.annotations.makePointAnnotationManager()
}
func updateMarkers() {
pointAnnotationManager.annotations = markers
}Batch Annotation Updates
// ✅ Update all at once
pointAnnotationManager.annotations = newAnnotations
// ❌ Don't update one by one
for annotation in newAnnotations {
pointAnnotationManager.annotations.append(annotation)
}Memory Management
// Use weak self in closures
mapView.gestures.onMapTap.observe { [weak self] context in
self?.handleTap(context.coordinate)
}.store(in: &cancelables)
// Clean up on deinit
deinit {
cancelables.forEach { $0.cancel() }
}Use Standard Style
// ✅ Standard style is optimized and recommended
.mapStyle(.standard)
// Use other styles only when needed for specific use cases
.mapStyle(.standardSatellite) // Satellite imagery---
Troubleshooting
Map Not Displaying
Check:
1. ✅ MBXAccessToken in Info.plist 2. ✅ Token is valid (test at mapbox.com) 3. ✅ MapboxMaps framework imported 4. ✅ MapView added to view hierarchy 5. ✅ Correct frame/constraints set
Style Not Loading
mapView.mapboxMap.onStyleLoaded.observe { [weak self] _ in
print("Style loaded successfully")
// Add layers and sources here
}.store(in: &cancelables)Performance Issues
- Use
.standardstyle (recommended and optimized) - Limit visible annotations to viewport
- Reuse annotation managers
- Avoid frequent style reloads
- Batch annotation updates
---
Reference Files
Load these references when the task requires deeper patterns:
- `references/annotations.md` — Circle, Polyline, Polygon Annotations
- `references/location-tracking.md` — Camera Follow User + Get Current Location
- `references/custom-data.md` — GeoJSON: Lines, Polygons, Points, Update/Remove
- `references/camera-styles.md` — Camera Control + Map Styles
- `references/interactions.md` — Featureset Interactions, Custom Layer Taps, Long Press, Gestures
---
Additional Resources
Mapbox iOS Quick Reference
Fast reference for Mapbox Maps SDK v11 on iOS with Swift, SwiftUI, and UIKit.
Setup
Installation (SPM)
// File → Add Package Dependencies
https://github.com/mapbox/mapbox-maps-ios.git
// Version: 11.0.0+Access Token
<!-- Info.plist -->
<key>MBXAccessToken</key>
<string>pk.your_token_here</string>SwiftUI
Basic Map
import SwiftUI
import MapboxMaps
struct MapView: View {
@State private var viewport: Viewport = .camera(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
zoom: 12
)
var body: some View {
Map(viewport: $viewport)
.mapStyle(.standard)
}
}With Annotation
Map(viewport: $viewport) {
PointAnnotation(coordinate: CLLocationCoordinate2D(
latitude: 37.7749,
longitude: -122.4194
))
// Register and use the image in one call — raster UIImage only.
.image(.init(image: UIImage(named: "marker")!, name: "marker"))
}
.mapStyle(.standard)UIKit
Basic Map
import UIKit
import MapboxMaps
class MapViewController: UIViewController {
private var mapView: MapView!
override func viewDidLoad() {
super.viewDidLoad()
let options = MapInitOptions(
cameraOptions: CameraOptions(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
zoom: 12
)
)
mapView = MapView(frame: view.bounds, mapInitOptions: options)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)
mapView.mapboxMap.loadStyle(.standard)
}
}Common Patterns
1. Add Markers
Three options — pick the simplest:
Marker(SwiftUI, experimental SPI) — default pin, no image assets.PointAnnotation(SwiftUI + UIKit) — custom image, scales to hundreds via the underlying symbol layer.- View annotations (SwiftUI + UIKit) — arbitrary native view at a coordinate.
// Markers API — SwiftUI, simplest
import SwiftUI
@_spi(Experimental) import MapboxMaps
Map {
Marker(coordinate: coord).color(.red).text("Coffee")
}
// PointAnnotation — UIKit, custom image
var manager = mapView.annotations.makePointAnnotationManager()
var annotation = PointAnnotation(coordinate: coordinate)
annotation.image = .init(image: UIImage(named: "marker")!, name: "marker")
manager.annotations = [annotation]2. User Location with Camera Follow
import Combine
var cancelables = Set<AnyCancellable>()
// Request permission (add to Info.plist)
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
// Show user location
mapView.location.options.puckType = .puck2D()
mapView.location.options.puckBearingEnabled = true
// Follow user location
mapView.location.onLocationChange.observe { [weak self] locations in
guard let self = self, let location = locations.last else { return }
self.mapView.camera.ease(to: CameraOptions(
center: location.coordinate,
zoom: 15,
bearing: location.course >= 0 ? location.course : nil
), duration: 1.0)
}.store(in: &cancelables)3. Add Custom Data (GeoJSON)
var source = GeoJSONSource(id: "route-source")
source.data = .geometry(.lineString(LineString(coordinates)))
try? mapView.mapboxMap.addSource(source)
var layer = LineLayer(id: "route-layer", source: "route-source")
layer.lineColor = .constant(StyleColor(.blue))
layer.lineWidth = .constant(4)
try? mapView.mapboxMap.addLayer(layer)4. Camera Control
// Fly animation
mapView.camera.fly(to: CameraOptions(
center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060),
zoom: 14
), duration: 2.0)
// Ease animation
mapView.camera.ease(to: CameraOptions(
center: coordinate,
zoom: 15
), duration: 1.0)5. Featureset Interactions
// Tap on POI features
let token = mapView.mapboxMap.addInteraction(
TapInteraction(.standardPoi) { poi, context in
print("Tapped POI: \(poi.name ?? "Unknown")")
return true
}
)
// Tap on buildings
let buildingToken = mapView.mapboxMap.addInteraction(
TapInteraction(.standardBuildings) { building, context in
// Highlight the building using feature state
self.mapView.mapboxMap.setFeatureState(
building,
state: ["select": true]
)
return true
}
)6. Map Tap Handling
mapView.gestures.onMapTap.observe { [weak self] context in
let coordinate = context.coordinate
print("Tapped at: \(coordinate)")
}.store(in: &cancelables)7. Styles
// SwiftUI
.mapStyle(.standard) // Recommended
.mapStyle(.streets)
.mapStyle(.dark)
.mapStyle(.standardSatellite)
// UIKit
mapView.mapboxMap.loadStyle(.standard)
mapView.mapboxMap.loadStyle(.dark)Performance Tips
Reuse Managers
// ✅ Create once
let annotationManager = mapView.annotations.makePointAnnotationManager()
// ✅ Update many times
func updateMarkers() {
annotationManager.annotations = newMarkers
}Batch Updates
// ✅ Update all at once
manager.annotations = allAnnotations
// ❌ Don't update one by one
allAnnotations.forEach { manager.annotations.append($0) }Memory Management
// Use weak self
mapView.gestures.onMapTap.observe { [weak self] context in
self?.handleTap(context.coordinate)
}.store(in: &cancelables)Use Standard Style
// ✅ Recommended
.mapStyle(.standard)
// Use others only when needed
.mapStyle(.standardSatellite)Quick Checklist
✅ MBXAccessToken in Info.plist ✅ MapboxMaps imported ✅ Location permissions if needed ✅ Use .standard style (recommended) ✅ Weak self in closures ✅ Cancelables stored and cancelled ✅ Annotation managers reused
Resources
{
"skill_name": "mapbox-ios-patterns",
"evals": [
{
"id": 1,
"prompt": "I'm setting up Mapbox Maps SDK in my iOS app. Where do I put the access token?",
"expectations": [
"Token goes in Info.plist with the key MBXAccessToken",
"Shows the correct plist entry: <key>MBXAccessToken</key> <string>pk.your_token</string>",
"Presents the Info.plist approach as the primary/standard/recommended way for iOS (a programmatic alternative via MapboxOptions.setAccessToken is valid and may be mentioned, but should not be promoted as the main approach)",
"Notes the token should be a public token (starts with pk.)"
]
},
{
"id": 2,
"prompt": "My iOS app creates a new `PointAnnotationManager` inside `updateMarkers()` every time markers change, and performance is bad when called frequently. What's wrong?",
"expectations": [
"Creating a new annotation manager on every update is the problem — managers should be created once and reused",
"Store the PointAnnotationManager as an instance property and initialize it once",
"To update markers, just reassign the annotations array: pointAnnotationManager.annotations = newAnnotations",
"Also recommends batch updates: set all annotations at once rather than appending one by one"
]
},
{
"id": 3,
"prompt": "I'm using the Mapbox Standard style on iOS and want to detect when users tap on POIs (coffee shops, restaurants, etc.) on the map. How do I implement this?",
"expectations": [
"Use the Interactions API with TapInteraction(.standardPoi) — the modern recommended approach for Standard style featuresets",
"Shows the SwiftUI or UIKit pattern: TapInteraction(.standardPoi) { poi, context in ... }",
"The poi object provides typed access to POI properties like poi.name",
"Notes that returning true from the closure stops propagation to other interactions"
]
},
{
"id": 4,
"prompt": "I have a SwiftUI Mapbox map and I want to drop coffee shop markers from a GeoJSON feed. I don't have a custom image — I just need simple pins. What should I use?",
"expectations": [
"Recommends `Marker` (the Markers API) as the simplest fit",
"Imports with `@_spi(Experimental) import MapboxMaps`",
"Uses `ForEvery` with `Marker` inside the Map block",
"Mentions that for hundreds or thousands of features a SymbolLayer on a GeoJSONSource is more appropriate"
]
}
]
}
Annotations: Markers, Points, Circle, Polyline, Polygon
Deeper reference for the annotation APIs summarized in SKILL.md.
---
Picking an annotation API
- `Marker` (Markers API) — SwiftUI-only convenience pin. No image assets required. Marked
@_spi(Experimental); import with@_spi(Experimental) import MapboxMaps. Best when you just need a pin and have < 100 points. - `PointAnnotation` — SwiftUI + UIKit. Use when you have a custom image. Backed by a symbol layer under the hood, so it scales better than view annotations.
- View annotations (`ViewAnnotation` / `MapViewAnnotation`) — SwiftUI + UIKit. Render an arbitrary native view anchored to a coordinate. In SwiftUI use
MapViewAnnotation { SomeView() }; in UIKit usemapView.viewAnnotations.add(_:)with aViewAnnotation. Expensive per annotation. - `SymbolLayer` on `GeoJSONSource` — for thousands of features, clustering, or data-driven styling.
Markers API (SwiftUI)
import SwiftUI
@_spi(Experimental) import MapboxMaps
struct MyMap: View {
let locations: [Location]
var body: some View {
Map {
ForEvery(locations, id: \.id) { location in
Marker(coordinate: location.coordinate)
.color(.red)
.stroke(.white)
.innerColor(.white)
.text(location.name)
.onTapGesture {
print("tapped \(location.name)")
}
}
}
}
}Remove a marker by taking it out of the Map block — if / switch inside the builder work as expected.
Markers appear above all other map content (layers, annotations, puck). If you need layer-ordered placement, use PointAnnotation.
Point Annotations: custom image
var manager = mapView.annotations.makePointAnnotationManager()
var annotation = PointAnnotation(coordinate: coordinate)
annotation.image = .init(image: UIImage(named: "marker")!, name: "marker")
annotation.iconAnchor = .bottom
manager.annotations = [annotation]---
Circle Annotations
var circleAnnotationManager = mapView.annotations.makeCircleAnnotationManager()
var circle = CircleAnnotation(coordinate: coordinate)
circle.circleRadius = 10
circle.circleColor = StyleColor(.red)
circleAnnotationManager.annotations = [circle]Polyline Annotations
var polylineAnnotationManager = mapView.annotations.makePolylineAnnotationManager()
let coordinates = [coord1, coord2, coord3]
var polyline = PolylineAnnotation(lineCoordinates: coordinates)
polyline.lineColor = StyleColor(.blue)
polyline.lineWidth = 4
polylineAnnotationManager.annotations = [polyline]Polygon Annotations
var polygonAnnotationManager = mapView.annotations.makePolygonAnnotationManager()
let coordinates = [coord1, coord2, coord3, coord1] // Close the polygon
var polygon = PolygonAnnotation(polygon: .init(outerRing: .init(coordinates)))
polygon.fillColor = StyleColor(.blue.withAlphaComponent(0.5))
polygon.fillOutlineColor = StyleColor(.blue)
polygonAnnotationManager.annotations = [polygon]Camera Control + Map Styles
---
Camera Control
Set Camera Position
// SwiftUI - Update viewport state
viewport = .camera(
center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060),
zoom: 14,
bearing: 90,
pitch: 60
)
// UIKit - Immediate
mapView.mapboxMap.setCamera(to: CameraOptions(
center: CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060),
zoom: 14,
bearing: 90,
pitch: 60
))Animated Camera Transitions
// Fly animation (dramatic arc)
mapView.camera.fly(to: CameraOptions(
center: destination,
zoom: 15
), duration: 2.0)
// Ease animation (smooth)
mapView.camera.ease(to: CameraOptions(
center: destination,
zoom: 15
), duration: 1.0)Fit Camera to Coordinates
let coordinates = [coord1, coord2, coord3]
let camera = mapView.mapboxMap.camera(for: coordinates,
padding: UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50),
bearing: 0,
pitch: 0)
mapView.camera.ease(to: camera, duration: 1.0)---
Map Styles
Built-in Styles
// SwiftUI
Map(viewport: $viewport)
.mapStyle(.standard) // Mapbox Standard (recommended)
.mapStyle(.streets) // Mapbox Streets
.mapStyle(.outdoors) // Mapbox Outdoors
.mapStyle(.light) // Mapbox Light
.mapStyle(.dark) // Mapbox Dark
.mapStyle(.standardSatellite) // Satellite imagery
// UIKit
mapView.mapboxMap.loadStyle(.standard)
mapView.mapboxMap.loadStyle(.streets)
mapView.mapboxMap.loadStyle(.dark)Custom Style URL
// SwiftUI
Map(viewport: $viewport)
.mapStyle(MapStyle(uri: StyleURI(url: customStyleURL)!))
// UIKit
mapView.mapboxMap.loadStyle(StyleURI(url: customStyleURL)!)Style from Mapbox Studio:
let styleURL = URL(string: "mapbox://styles/username/style-id")!Custom Data (GeoJSON): Lines, Polygons, Points, Update/Remove
Add your own data to the map using GeoJSON sources and layers.
---
Add Line (Route, Path)
// Create coordinates for the line
let routeCoordinates = [
CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
CLLocationCoordinate2D(latitude: 37.7849, longitude: -122.4094),
CLLocationCoordinate2D(latitude: 37.7949, longitude: -122.3994)
]
// Create GeoJSON source
var source = GeoJSONSource(id: "route-source")
source.data = .geometry(.lineString(LineString(routeCoordinates)))
try? mapView.mapboxMap.addSource(source)
// Create line layer
var layer = LineLayer(id: "route-layer", source: "route-source")
layer.lineColor = .constant(StyleColor(.blue))
layer.lineWidth = .constant(4)
layer.lineCap = .constant(.round)
layer.lineJoin = .constant(.round)
try? mapView.mapboxMap.addLayer(layer)Add Polygon (Area)
let polygonCoordinates = [coord1, coord2, coord3, coord1] // Close the polygon
var source = GeoJSONSource(id: "area-source")
source.data = .geometry(.polygon(Polygon([polygonCoordinates])))
try? mapView.mapboxMap.addSource(source)
var fillLayer = FillLayer(id: "area-fill", source: "area-source")
fillLayer.fillColor = .constant(StyleColor(.blue.withAlphaComponent(0.3)))
fillLayer.fillOutlineColor = .constant(StyleColor(.blue))
try? mapView.mapboxMap.addLayer(fillLayer)Add Points from GeoJSON
let geojsonString = """
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4194, 37.7749]},
"properties": {"name": "Location 1"}
},
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4094, 37.7849]},
"properties": {"name": "Location 2"}
}
]
}
"""
var source = GeoJSONSource(id: "points-source")
source.data = .string(geojsonString)
try? mapView.mapboxMap.addSource(source)
var symbolLayer = SymbolLayer(id: "points-layer", source: "points-source")
symbolLayer.iconImage = .constant(.name("marker"))
symbolLayer.textField = .constant(.expression(Exp(.get) { "name" }))
symbolLayer.textOffset = .constant([0, 1.5])
try? mapView.mapboxMap.addLayer(symbolLayer)Update Layer Properties
try? mapView.mapboxMap.updateLayer(
withId: "route-layer",
type: LineLayer.self
) { layer in
layer.lineColor = .constant(StyleColor(.red))
layer.lineWidth = .constant(6)
}Remove Layers and Sources
try? mapView.mapboxMap.removeLayer(withId: "route-layer")
try? mapView.mapboxMap.removeSource(withId: "route-source")Interactions: Featureset, Custom Layer Taps, Long Press, Gestures
---
Featureset Interactions (Recommended)
The modern Interactions API allows handling taps on map features with typed feature access. Works with Standard Style predefined featuresets like POIs, buildings, and place labels.
SwiftUI Pattern:
import SwiftUI
import MapboxMaps
struct MapView: View {
@State private var viewport: Viewport = .camera(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
zoom: 12
)
@State private var selectedBuildings = [StandardBuildingsFeature]()
var body: some View {
Map(viewport: $viewport) {
// Tap on POI features
TapInteraction(.standardPoi) { poi, context in
print("Tapped POI: \(poi.name ?? "Unknown")")
return true // Stop propagation
}
// Tap on buildings and collect selected buildings
TapInteraction(.standardBuildings) { building, context in
print("Tapped building")
selectedBuildings.append(building)
return true
}
// Apply feature state to selected buildings (highlighting)
ForEvery(selectedBuildings, id: \.id) { building in
FeatureState(building, .init(select: true))
}
}
.mapStyle(.standard)
}
}UIKit Pattern:
import MapboxMaps
import Combine
class MapViewController: UIViewController {
private var mapView: MapView!
private var cancelables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
setupMap()
setupInteractions()
}
func setupInteractions() {
// Tap on POI features
let poiToken = mapView.mapboxMap.addInteraction(
TapInteraction(.standardPoi) { [weak self] poi, context in
print("Tapped POI: \(poi.name ?? "Unknown")")
return true
}
)
// Tap on buildings
let buildingToken = mapView.mapboxMap.addInteraction(
TapInteraction(.standardBuildings) { [weak self] building, context in
print("Tapped building")
// Highlight the building using feature state
self?.mapView.mapboxMap.setFeatureState(
building,
state: ["select": true]
)
return true
}
)
// Store tokens to keep interactions active
// Cancel tokens when done: poiToken.cancel()
}
}Tap on Custom Layers
let token = mapView.mapboxMap.addInteraction(
TapInteraction(.layer("custom-layer-id")) { feature, context in
if let properties = feature.properties {
print("Feature properties: \(properties)")
}
return true
}
)Long Press Interactions
let token = mapView.mapboxMap.addInteraction(
LongPressInteraction(.standardPoi) { poi, context in
print("Long pressed POI: \(poi.name ?? "Unknown")")
return true
}
)Handle Map Taps (Empty Space)
// UIKit
mapView.gestures.onMapTap.observe { [weak self] context in
let coordinate = context.coordinate
print("Tapped map at: \(coordinate.latitude), \(coordinate.longitude)")
}.store(in: &cancelables)Gesture Configuration
// Disable specific gestures
mapView.gestures.options.pitchEnabled = false
mapView.gestures.options.rotateEnabled = false
// Configure zoom limits
mapView.mapboxMap.setCamera(to: CameraOptions(
zoom: 12,
minZoom: 10,
maxZoom: 16
))Location Tracking: Camera Follow User + Get Current Location
Advanced location patterns beyond basic user location display (covered in SKILL.md).
---
Camera Follow User Location
To make the camera follow the user's location as they move:
import Combine
class MapViewController: UIViewController {
private var mapView: MapView!
private var cancelables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
setupMap()
setupLocationTracking()
}
func setupLocationTracking() {
// Request permissions
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
// Show user location
mapView.location.options.puckType = .puck2D()
mapView.location.options.puckBearingEnabled = true
// Follow user location with camera
mapView.location.onLocationChange.observe { [weak self] locations in
guard let self = self, let location = locations.last else { return }
self.mapView.camera.ease(to: CameraOptions(
center: location.coordinate,
zoom: 15,
bearing: location.course >= 0 ? location.course : nil,
pitch: 45
), duration: 1.0)
}.store(in: &cancelables)
}
}Get Current Location Once
if let location = mapView.location.latestLocation {
let coordinate = location.coordinate
print("User at: \(coordinate.latitude), \(coordinate.longitude)")
// Move camera to user location
mapView.camera.ease(to: CameraOptions(
center: coordinate,
zoom: 14
), duration: 1.0)
}Related skills
How it compares
Use mapbox-ios-patterns for SDK v11 Swift/SwiftUI snippets instead of generic iOS map tutorials or older Mapbox v10 APIs.
FAQ
Which Mapbox iOS SDK version does mapbox-ios-patterns target?
mapbox-ios-patterns documents Mapbox Maps SDK v11 on iOS, including Swift Package Manager install from mapbox-maps-ios.git at version 11.0.0 or newer.
How do you configure Mapbox access tokens on iOS?
mapbox-ios-patterns shows adding MBXAccessToken to Info.plist with a pk. prefixed token string before initializing SwiftUI Map or UIKit Mapbox views.
Is Mapbox Ios Patterns safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.