Using the AR view in SwiftUI
Embed the AR scene in a SwiftUI view, and bind its state to yours.
Overview
WemapGeoARSDK/GeoAR is a SwiftUI view over the same WemapGeoARSDK/GeoARView the UIKit API exposes.
It follows the map's shape exactly — the view handed to onLoaded is how you reach the managers, and the state you
render arrives through bindings.
import SwiftUI
import WemapGeoARSDK
import WemapPositioningSDKGPS
struct ARScreen: View {
let session: CoreSession
@State private var arView: GeoARView?
@State private var navigationInfo: NavigationInfo?
var body: some View {
VStack {
GeoAR(session: session)
.onLoaded { view in
arView = view
// the AR scene cannot position itself without a location source
view.locationManager.locationSource = GPSLocationSource(session: session)
}
.onFailed { print("Failed to load the AR view: \($0)") }
.navigationInfo($navigationInfo)
if let navigationInfo {
Text("\(Int(navigationInfo.remainingDistance)) m left")
}
}
}
}
Request camera permission before presenting the view, and declare NSCameraUsageDescription in your
Info.plist. Camera capture starts when you assign a location source, not when the view appears.
The view is named after what it shows, like WemapMapSDK/Map: the View suffix belongs to
WemapGeoARSDK/GeoARView, the UIKit class this wraps and hands out. On Compose the same view is WemapGeoAR,
prefixed only because Kotlin imports kotlin.collections.Map everywhere and brand-prefixing is the convention there.
Own the session above the view
A session loads a map's data once and is shared by the Wemap views and location sources of a screen. Create it in
app state, a router, or a view model, and pass it down; a session created inside a view dies with the view and
refetches its data on every navigation. The same session can feed a WemapMapSDK/Map and a
WemapGeoARSDK/GeoAR at the same time, which is what keeps navigation and selection consistent between
them.
- Important: That pairing is the limit — one view of each kind. Two AR views (or two maps) on one session contend for a single renderer slot: the newer one wins and the older stops receiving updates while still drawing. Give the second view its own session. The SDK logs an error naming the fix if it happens.
The AR view is a handle, not view state
The GeoARView from onLoaded exists so controls outside the AR scene can call into the SDK. The same three rules
as the map's apply: declare the @State in the same view as the AR view, clear it when you change session, and
hold it weak from a view model — nothing else retains the view once it leaves the screen.
Observing and binding
| Modifier | Delivers |
|---|---|
WemapGeoARSDK/GeoAR/onLoaded(_:) | the loaded AR view, once |
WemapGeoARSDK/GeoAR/onFailed(_:) | the failure, if loading fails |
WemapGeoARSDK/GeoAR/onPhaseChange(_:) | every LoadPhase transition |
WemapGeoARSDK/GeoAR/userCoordinate(_:) | the user's location as it updates |
WemapGeoARSDK/GeoAR/navigationInfo(_:) | navigation info, reset to nil when it stops |
WemapGeoARSDK/GeoAR/selectedPOIs(_:) | the selected points of interest, both directions |
Observation modifiers compose — applying one twice runs both handlers. Bindings are single: applying one twice keeps only the last, because two bindings would be two sources of truth for the same write.
There is no camera binding here. The AR camera follows the device and the user's position; it is not app state to
drive, which is why the map's WemapMapSDK/Map/camera(_:frequency:) has no counterpart.
Subclassing the AR view
If you subclass WemapGeoARSDK/GeoARView, build it yourself:
GeoAR(session: session) { frame, session, config in
MyARView(frame: frame, session: session, config: config)
}