Skip to main content
Version: 1.x (beta)

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​

ModifierDelivers
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)
}