Skip to main content

Geofencing

The Mapbox Maps SDK for iOS's geofencing service enables your application to receive events when a device enters, exits, or dwells in your own defined geofences (a virtual fence around a geographic feature). When your application receives these events you can then trigger actions, alerts, and notifications.

EXAMPLE
See Geofencing Examples

See how the GeofencingService is used to execute actions when a device enters, exits, or dwells in the geofences.

Inactive geofence
The user is outside the geofence.
Active geofence
The user is in the geofence.
Exit geofence
The user has left the geofence.

Before you can use the geofencing component, users must grant your application permission to access their location. See the User Location section for more details. Additionally, the user must grant background location permission if you need to receive events while the app is in the background.

Geofencing service

The GeofencingService is the main entry point to the Mapbox geofencing component. It allows you to manage geofences, start/stop observing events, and configure the geofencing service itself. See GeofencingOptions for all the available configuration options.

After a user has granted location permission you can create a GeofencingService instance, configure it, and start observing geofencing events. One architectural approach is to create a class that will manage the geofencing service. For example, a Swift UI implementation could look like this:

class Geofencing: ObservableObject {
func start(_ completion: @escaping () -> Void) {
let geofencing = GeofencingFactory.getOrCreate()

// Optionally, configure the geofencing service here to adjust the maximum number of geofences.
let geofencingOptions = GeofencingOptions(maximumMonitoredFeatures: 3000)
geofencing.configure(options: geofencingOptions) { result in
if case let .failure(error) = result {
print("Configuring geofencing failed: \(error)")
return
}
// Once the geofencing service is created and configured, you can add observers for geofencing events.
// Here we add this class as an observer to receive geofencing events.
geofencing.addObserver(observer: self) { result in
print("Add observer: \(result)")
}
completion()
}
}
}

/// Then in your SwiftUI view
struct ContentView: View {
@ObservedObject private var geofencing = Geofencing()

var body: some View {
Map() {
// Your map code
}
.onMapLoaded { _ in
geofencing.start {
// You can now add geofences based on Features
// and start observing geofencing events.
print("Geofencing service is ready")
}
}
}

If your app uses geofencing while in the background, we recommend adding the observer as part of your application's AppDelegate. For a sample implementation of this approach see our Examples application, and specifically the AppDelegate.swift class.

Observing events

Implement the GeofencingObserver protocol to be notified when the user's device enters, exits, or dwells in geofences.

// Extend the Geofencing class created above to conform to the `GeofencingObserver` protocol
// This allows the Geofencing class to receive geofencing events
extension Geofencing: GeofencingObserver {
// Inside the event you can find the specific geofence feature that was triggered
func onEntry(event: GeofencingEvent) {
print("onEntry() called with feature id = \(event.feature.identifier) at \(event.timestamp)")
}

func onDwell(event: GeofencingEvent) {
print("onDwell() called with feature id = \(event.feature.identifier) at \(event.timestamp)")
}

func onExit(event: GeofencingEvent) {
print("onExit() called with feature id = \(event.feature.identifier) at \(event.timestamp)")
}

func onError(error: GeofencingError) {
print("onError() called with: error = \(error)")
}

func onUserConsentChanged(isConsentGiven: Bool) {
print("onUserConsentChanged() called with: isConsentGiven = \(isConsentGiven)")
}
}
Dwell events

For the geofencing service to generate dwell events each geofence feature must define a dwellTimeKey in minutes in its Feature.properties (see GeofencingPropertiesKeys). After a user dwells in a geofence for the specified time, the geofencing service will trigger a dwell event.

{
"type": "Feature",
"id": "eiffel_tower",
"properties": {
...
"MBX_GEOFENCE_DWELL_TIME": 20
},
"geometry": { ... }
}

You can also set the dwell time for Features by setting a property in your Swift code:

geofencingFeature.properties[GeofencingPropertiesKeys.dwellTimeKey] = 20

Geofences management

You must provide a GeoJSON feature for each geofence you want to create. The following GeoJSON geometries are supported:

  • Point: the monitored area is a circle around the point defined by the pointRadiusKey given in the Feature.properties (see GeofencingPropertiesKeys).
  • Polygon: the area is described by the polygon.
  • MultiPolygon: events will be triggered whenever the user's device enters or exits any of the multiple polygons defined in the geometry.
Quickly create GeoJSON features

You can visit geojson.io to create and edit GeoJSON features.

Each feature must define a unique string identifier (id). For example:

{
"type": "Feature",
"id": "eiffel_tower",
"properties": {
"name": "Eiffel Tower"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[2.291903984967945, 48.85772344046961],
[2.293390631169075, 48.85669301371263],
[2.296888622230682, 48.859015366890844],
[2.2952747763545176, 48.860087588194006],
[2.291903984967945, 48.85772344046961]
]
]
}
}

To add above GeoJSON feature to the geofencing service you must call the addFeature method:

let featureJSON = """
{
"type": "Feature",
"id": "eiffel_tower",
"properties": {
"name": "Eiffel Tower"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[ 2.291903984967945, 48.85772344046961 ],
[ 2.293390631169075, 48.85669301371263 ],
[ 2.296888622230682, 48.859015366890844 ],
[ 2.2952747763545176, 48.860087588194006 ],
[ 2.291903984967945, 48.85772344046961 ]
]
]
}
}
"""
if let featureData = featureJSON.data(using: .utf8) {
let feature = try JSONDecoder().decode(Feature.self, from: featureData)
geofencing.addFeature(feature: feature) { result in
print("Add feature result: \(result)")
}
}

To update a geofence you need to call addFeature again with the same feature identifier. For example, let's add a new property rating:

let featureJSON = """
{
"type": "Feature",
"id": "eiffel_tower",
"properties": {
"name": "Eiffel Tower",
"rating": 4.5
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[ 2.291903984967945, 48.85772344046961 ],
[ 2.293390631169075, 48.85669301371263 ],
[ 2.296888622230682, 48.859015366890844 ],
[ 2.2952747763545176, 48.860087588194006 ],
[ 2.291903984967945, 48.85772344046961 ]
]
]
}
}
"""
if let featureData = featureJSON.data(using: .utf8) {
let feature = try JSONDecoder().decode(Feature.self, from: featureData)
geofencing.addFeature(feature: feature) { result in
print("Add feature result: \(result)")
}
}

Later on, you can remove the feature by calling removeFeature:

val id = "eiffel_tower"
geofencing.removeFeature(identifier: id) { result in
print("Remove feature result: \(result)")
}

You can also retrieve geofences directly from the service by their id:

val id = "eiffel_tower"
geofencing.getFeature(identifier: id) { result in
switch result {
case let .success(feature):
print("Feature found: \(feature)")
case let .failure(error):
print("Failed to get feature error: \(error)")
}
}

Further, you can call clearFeatures to remove all geofences:

geofencing.clearFeatures { result in
print("Cleared features: \(result)")
}
Was this page helpful?