PageSourceSearch

https://wemap-documentation.pages.dev/assets/js/f07e2fcd.401d7c7b.js

js wemap-documentation.pages.dev collected 2026-10-03 06:06:36 UTC 8,699 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkdocumentation_v_2=self.webpackChunkdocumentation_v_2||[]).push([[1760],{72307(e,n,i){i.r(n),i.d(n,{assets:()=>d,contentTitle:()=>r,default:()=>l,frontMatter:()=>a,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"geoar-swiftui","title":"Using the AR view in SwiftUI","description":"Embed the AR scene in a SwiftUI view, and bind its state to yours.","source":"@site/ios-native/geoar-swiftui.mdx","sourceDirName":".","slug":"/geoar-swiftui","permalink":"/docs/ios-native/1.x/geoar-swiftui","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"id":"geoar-swiftui","title":"Using the AR view in SwiftUI","sidebar_label":"GeoAR with SwiftUI"},"sidebar":"iosNative","previous":{"title":"GeoARSDK","permalink":"/docs/ios-native/1.x/geoarsdk-getting-started"},"next":{"title":"PositioningSDK Getting Started","permalink":"/docs/ios-native/1.x/positioning/getting-started"}}');var t=i(74848),o=i(28453);const a={id:"geoar-swiftui",title:"Using the AR view in SwiftUI",sidebar_label:"GeoAR with SwiftUI"},r=void 0,d={},c=[{value:"Overview",id:"overview",level:2},{value:"Own the session above the view",id:"own-the-session-above-the-view",level:2},{value:"The AR view is a handle, not view state",id:"the-ar-view-is-a-handle-not-view-state",level:2},{value:"Observing and binding",id:"observing-and-binding",level:2},{value:"Subclassing the AR view",id:"subclassing-the-ar-view",level:2}];function h(e){const n={code:"code",h2:"h2",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.p,{children:"Embed the AR scene in a SwiftUI view, and bind its state to yours."}),"\n",(0,t.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.code,{children:"WemapGeoARSDK/GeoAR"})," is a SwiftUI view over the same ",(0,t.jsx)(n.code,{children:"WemapGeoARSDK/GeoARView"})," the UIKit API exposes.\nIt follows the map's shape exactly \u2014 the view handed to ",(0,t.jsx)(n.code,{children:"onLoaded"})," is how you reach the managers, and the state you\nrender arrives through bindings."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-swift",children:'import SwiftUI\nimport WemapGeoARSDK\nimport WemapPositioningSDKGPS\n\nstruct ARScreen: View {\n\n    let session: CoreSession\n\n    @State private var arView: GeoARView?\n    @State private var navigationInfo: NavigationInfo?\n\n    var body: some View {\n        VStack {\n            GeoAR(session: session)\n                .onLoaded { view in\n                    arView = view\n                    // the AR scene cannot position itself without a location source\n                    view.locationManager.locationSource = GPSLocationSource(session: session)\n                }\n                .onFailed { print("Failed to load the AR view: \\($0)") }\n                .navigationInfo($navigationInfo)\n\n            if let navigationInfo {\n                Text("\\(Int(navigationInfo.remainingDistance)) m left")\n            }\n        }\n    }\n}\n'})}),"\n",(0,t.jsxs)(n.p,{children:["Request camera permission ",(0,t.jsx)(n.strong,{children:"before"})," presenting the view, and declare ",(0,t.jsx)(n.code,{children:"NSCameraUsageDescription"})," in your\n",(0,t.jsx)(n.code,{children:"Info.plist"}),". Camera capture starts when you assign a location source, not when the view appears."]}),"\n",(0,t.jsxs)(n.p,{children:["The view is named after what it shows, like ",(0,t.jsx)(n.code,{children:"WemapMapSDK/Map"}),": the ",(0,t.jsx)(n.code,{children:"View"})," suffix belongs to\n",(0,t.jsx)(n.code,{children:"WemapGeoARSDK/GeoARView"}),", the UIKit class this wraps and hands out. On Compose the same view is ",(0,t.jsx)(n.code,{children:"WemapGeoAR"}),",\nprefixed only because Kotlin imports ",(0,t.jsx)(n.code,{children:"kotlin.collections.Map"})," everywhere and brand-prefixing is the convention there."]}),"\n",(0,t.jsx)(n.h2,{id:"own-the-session-above-the-view",children:"Own the session above the view"}),"\n",(0,t.jsxs)(n.p,{children:["A session loads a map's data once and is shared by the Wemap views and location sources of a screen. Create it in\napp state, a router, or a view model, and pass it down; a session created inside a view dies with the view and\nrefetches its data on every n
1avigation. The same session can feed a ",(0,t.jsx)(n.code,{children:"WemapMapSDK/Map"})," and a\n",(0,t.jsx)(n.code,{children:"WemapGeoARSDK/GeoAR"})," at the same time, which is what keeps navigation and selection consistent between\nthem."]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["Important: That pairing is the limit \u2014 ",(0,t.jsx)(n.strong,{children:"one"})," view of each kind. Two AR views (or two maps) on one session contend for\na single renderer slot: the newer one wins and the older stops receiving updates while still drawing. Give the second\nview its own session. The SDK logs an error naming the fix if it happens."]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"the-ar-view-is-a-handle-not-view-state",children:"The AR view is a handle, not view state"}),"\n",(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.code,{children:"GeoARView"})," from ",(0,t.jsx)(n.code,{children:"onLoaded"})," exists so controls outside the AR scene can call into the SDK. The same three rules\nas the map's apply: declare the ",(0,t.jsx)(n.code,{children:"@State"})," in the same view as the AR view, clear it when you change session, and\nhold it ",(0,t.jsx)(n.code,{children:"weak"})," from a view model \u2014 nothing else retains the view once it leaves the screen."]}),"\n",(0,t.jsx)(n.h2,{id:"observing-and-binding",children:"Observing and binding"}),"\n",(0,t.jsxs)(n.table,{children:[(0,t.jsx)(n.thead,{children:(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.th,{children:"Modifier"}),(0,t.jsx)(n.th,{children:"Delivers"})]})}),(0,t.jsxs)(n.tbody,{children:[(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"WemapGeoARSDK/GeoAR/onLoaded(_:)"})}),(0,t.jsx)(n.td,{children:"the loaded AR view, once"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"WemapGeoARSDK/GeoAR/onFailed(_:)"})}),(0,t.jsx)(n.td,{children:"the failure, if loading fails"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"WemapGeoARSDK/GeoAR/onPhaseChange(_:)"})}),(0,t.jsxs)(n.td,{children:["every ",(0,t.jsx)(n.code,{children:"LoadPhase"})," transition"]})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"WemapGeoARSDK/GeoAR/userCoordinate(_:)"})}),(0,t.jsx)(n.td,{children:"the user's location as it updates"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"WemapGeoARSDK/GeoAR/navigationInfo(_:)"})}),(0,t.jsxs)(n.td,{children:["navigation info, reset to ",(0,t.jsx)(n.code,{children:"nil"})," when it stops"]})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"WemapGeoARSDK/GeoAR/selectedPOIs(_:)"})}),(0,t.jsx)(n.td,{children:"the selected points of interest, both directions"})]})]})]}),"\n",(0,t.jsxs)(n.p,{children:["Observation modifiers ",(0,t.jsx)(n.strong,{children:"compose"})," \u2014 applying one twice runs both handlers. Bindings are single: applying one twice\nkeeps only the last, because two bindings would be two sources of truth for the same write."]}),"\n",(0,t.jsxs)(n.p,{children:["There is no camera binding here. The AR camera follows the device and the user's position; it is not app state to\ndrive, which is why the map's ",(0,t.jsx)(n.code,{children:"WemapMapSDK/Map/camera(_:frequency:)"})," has no counterpart."]}),"\n",(0,t.jsx)(n.h2,{id:"subclassing-the-ar-view",children:"Subclassing the AR view"}),"\n",(0,t.jsxs)(n.p,{children:["If you subclass ",(0,t.jsx)(n.code,{children:"WemapGeoARSDK/GeoARView"}),", build it yourself:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-swift",children:"GeoAR(session: session) { frame, session, config in\n    MyARView(frame: frame, session: session, config: config)\n}\n"})})]})}function l(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}},28453(e,n,i){i.d(n,{R:()=>a,x:()=>r});var s=i(96540);const t={},o=s.createContext(t);function a(e){const n=s.useContext(o);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:a(e.components),s.createElement(o.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.