> For the complete documentation index, see [llms.txt](https://docs.multiset.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.multiset.ai/native-sdk/ios-swift-native.md).

# iOS Swift Native

Add VPS localization, object tracking and map creation to an iOS app with the MultiSet Swift SDK, including quick start, integration, requirements and token renewal.

## Overview

The MultiSet iOS SDK provides Visual Positioning System (VPS) localization, object tracking and map creation for iOS applications. It localizes the device against pre-mapped 3D environments from camera images and aligns AR content to the real world, and it can scan a space and upload it to be built into a new map.

The SDK ships as two parts that you add together:

| Part                      | What it is                                                                                                                                                                            |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MultiSetSDK.xcframework` | The SDK as a binary framework. It does not link ARKit or RealityKit.                                                                                                                  |
| `MultiSetAR/`             | The ARKit and RealityKit adapter, as source: `MultiSetARView`, mesh rendering, the `.metal` shaders, GPS hints, and `Mapping/` for map creation. You compile it into your app target. |

**Current version:** 2.1.0

**GitHub Repository:** <https://github.com/MultiSet-AI/multiset-ios-sdk.git>

## What's New in 2.1.0

| Change                                     | Notes                                                                                                                                                                                                                                                                                                   |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Map creation**                           | Scan a space with LiDAR or, on any ARKit device, without it, keep it as a draft and upload it to be built into a map. See [Map Creation](/native-sdk/ios-swift-native/api-reference/map-creation.md).                                                                                                   |
| **Recovery after a tracking break**        | With `relocalization` on, the SDK re-localizes once tracking is normal again and hides the map mesh until it succeeds. In 2.0 the flag had no effect. See [Lifecycle and AR Runtimes](/native-sdk/ios-swift-native/api-reference/lifecycle-and-runtimes.md#recovery-after-a-tracking-break).            |
| **Fresh world frame after the background** | `MultiSetARView` starts a new world frame when the app returns from the background, and re-localizes and re-tracks in it.                                                                                                                                                                               |
| **Run progress**                           | `onLocalizationStageChanged(_:isBackground:)` and `onObjectTrackingStageChanged(_:isBackground:)` report each run as capturing, uploading, waiting on the server or idle. See [Showing a Run's Progress](/native-sdk/ios-swift-native/api-reference/lifecycle-and-runtimes.md#showing-a-runs-progress). |
| **Recovery state**                         | `onRecoveryPhaseChanged(_:needsRealignment:)`. Hide content placed in the map frame while `needsRealignment` is `true`.                                                                                                                                                                                 |
| **VPS-2 in multi-frame mode**              | `queryMode` now applies to multi-frame localization too.                                                                                                                                                                                                                                                |
| **First fix retries are capped**           | `firstLocalizationUntilSuccess` and `firstObjectTrackingUntilSuccess` retry at most 5 times before reporting a failure. In 2.0 they retried indefinitely.                                                                                                                                               |
| **Client source**                          | `MultiSetConfig.clientSource` names your app in MultiSet's query logs. Unset, queries are tagged `ios-sdk-<version>`.                                                                                                                                                                                   |
| **Credentials-only config**                | `initialize(config:callback:)` accepts credentials without a map or object code, for apps that only create maps.                                                                                                                                                                                        |
| **Mesh loading state**                     | `MultiSet.shared.isMapMeshLoading` and `isObjectMeshLoading`.                                                                                                                                                                                                                                           |

{% hint style="warning" %}
**Upgrading from 2.0** needs no code changes unless you create maps: replace `MultiSetSDK.xcframework` and the `MultiSetAR/` folder together. `bgLocalizationDurationSeconds` now accepts 10 to 180 s. If you run your own `ARView`, see [Using Your Own ARView](/native-sdk/ios-swift-native/api-reference/lifecycle-and-runtimes.md#using-your-own-arview) for the return from the background.

**Upgrading from 1.16.0** is breaking. The ARKit code moved into the `MultiSetAR/` folder, `MultiSet` and `MultiSetCallback` are `@MainActor`, `GeoCoordinates.altitude` is an optional ellipsoidal height, `poseConsistencyCheck` defaults to `false`, `poseConsistencyThreshold` is a flat 1 to 15 m limit, and `GateTuning` lost six fields. The [repository README](https://github.com/MultiSet-AI/multiset-ios-sdk) has the full upgrade checklist.
{% endhint %}

## Table of Contents

### Sample Views

* [LandingView](/native-sdk/ios-swift-native/sample-views/landingview.md) - Authentication, SDK initialization, and navigation
* [ARLocalizationView](/native-sdk/ios-swift-native/sample-views/arlocalizationview.md) - AR localization (Single-frame and Multi-frame)
* [ARObjectTrackingView](/native-sdk/ios-swift-native/sample-views/arobjecttrackingview.md) - AR object tracking with outline mesh rendering

### API Reference

* [MultiSetConfig](/native-sdk/ios-swift-native/api-reference/multisetconfig.md) - Configuration parameters
* [MultiSetCallback](/native-sdk/ios-swift-native/api-reference/multisetcallback.md) - SDK event callbacks and `FalsePositiveInfo`
* [LocalizationResult](/native-sdk/ios-swift-native/api-reference/localizationresult.md) - Pose data, `GeoCoordinates` and `GeoPose`
* [Lifecycle and AR Runtimes](/native-sdk/ios-swift-native/api-reference/lifecycle-and-runtimes.md) - Retries, recovery, run progress, threading, your own ARView and custom runtimes
* [Map Creation](/native-sdk/ios-swift-native/api-reference/map-creation.md) - Scanning, drafts and uploads

## Quick Start

The sample project comes pre-configured with the SDK framework, the `MultiSetAR/` adapter and the demo app. Follow these steps to get up and running:

### 1. Clone the Repository

```bash
git clone https://github.com/MultiSet-AI/multiset-ios-sdk.git
cd multiset-ios-sdk
```

### 2. Open the Project

Open `MultiSet-iOS-SDK.xcodeproj` in Xcode.

### 3. Configure Credentials

Open `MultiSet-iOS-SDK/SDKConfig.swift` and replace the placeholder values with your credentials:

```swift
struct SDKConfig {
    // API Environment
    static let baseURL = MultiSetConfig.productionBaseURL
    static let clientSource: String? = nil   // nil sends "ios-sdk-<version>"

    // Authentication Credentials
    static let clientId = "YOUR_CLIENT_ID"
    static let clientSecret = "YOUR_CLIENT_SECRET"

    // Map Configuration (use either mapCode OR mapSetCode)
    static let mapCode = "YOUR_MAP_CODE"
    static let mapSetCode = ""

    // Object Tracking Configuration (max 10 codes)
    static let objectCodes: [String] = []
}
```

| Property       | Description                            | Required                       |
| -------------- | -------------------------------------- | ------------------------------ |
| `baseURL`      | API host, defaults to production       | No                             |
| `clientSource` | Name for your app in the query logs    | No                             |
| `clientId`     | Your client identifier                 | Yes                            |
| `clientSecret` | Your secret key                        | Yes                            |
| `mapCode`      | Single map identifier                  | For localization, one of these |
| `mapSetCode`   | MapSet identifier                      | For localization, one of these |
| `objectCodes`  | Codes to track in object tracking mode | Only for object tracking       |

Map creation needs only the credentials. To obtain credentials, visit: <https://developer.multiset.ai/credentials>

### 4. Build and Run

1. Select your development team under **Signing & Capabilities**, and a physical device (ARKit does not run in the simulator)
2. Press **Cmd + R** to build and run
3. The app opens on the [LandingView](/native-sdk/ios-swift-native/sample-views/landingview.md), where the SDK authenticates automatically
4. **Localization:** Select a mode (Single-Frame or Multi-Frame) and tap **Start Localization** to enter [ARLocalizationView](/native-sdk/ios-swift-native/sample-views/arlocalizationview.md)
5. **Object Tracking:** Tap **Start Object Tracking** to enter [ARObjectTrackingView](/native-sdk/ios-swift-native/sample-views/arobjecttrackingview.md)
6. **Map creation:** Use the **Mapping** card to scan a space, and **Draft Maps** to see scans not yet uploaded

## Add the SDK to Your App

1. Drag `MultiSetSDK.xcframework` into your Xcode project, with **Copy items if needed** checked.
2. Select your app target, then **General → Frameworks, Libraries, and Embedded Content**, and set `MultiSetSDK.xcframework` to **Embed & Sign**.
3. Drag the `MultiSet-iOS-SDK/MultiSetAR` folder into your project with **your app target checked**. Check that all three `.metal` files appear under **Build Phases → Compile Sources**, not Copy Bundle Resources. `MultiSetAR/Mapping/` is needed only for map creation.

Then implement the callback, initialize and show the AR view:

```swift
import SwiftUI
import MultiSetSDK

final class MySDKDelegate: MultiSetCallback {
    func onSDKReady() { }
    func onAuthenticationSuccess() { print("Ready for localization") }
    func onAuthenticationFailure(error: String) { print("Auth failed: \(error)") }
    func onLocalizationSuccess(result: LocalizationResult) { print("Localized at map: \(result.mapCode)") }
    func onLocalizationFailure(error: String) { print("Localization failed: \(error)") }
    func onTrackingStateChanged(state: TrackingState) { }
}

let config = MultiSetConfig.default(
    clientId: "YOUR_CLIENT_ID",
    clientSecret: "YOUR_CLIENT_SECRET",
    mapCode: "YOUR_MAP_CODE"
)
MultiSet.shared.initialize(config: config, callback: myDelegate)

struct MyARView: View {
    var body: some View {
        ZStack {
            MultiSetARView()          // from the MultiSetAR folder, not the framework
                .ignoresSafeArea()
            Button("Localize") { MultiSet.shared.localize() }
        }
    }
}
```

A config with credentials and no map or object codes is valid. `localize()` then reports `Missing mapCode or mapSetCode` through `onLocalizationFailure`, and `startObjectTracking()` reports `Missing objectCodes` through `onObjectTrackingFailure`.

## Requirements

* iOS 16.0+
* Xcode 26 or later
* A device with ARKit support (A12 chip or later). ARKit does not run in the simulator
* For a LiDAR map scan: a device with the LiDAR scanner (iPhone 12 Pro or a later Pro model, iPad Pro 2020 or later). A Non-LiDAR scan runs on any device above
* Camera permission and internet connectivity

## API Environment

Every endpoint the SDK calls is built from `MultiSetConfig.baseURL`, which defaults to the production API at `https://api.multiset.ai`. Override it to point a build at a staging, on-premise or proxied deployment:

```swift
var config = SDKConfig.buildConfig()
config.baseURL = "https://staging-api.multiset.ai"   // no trailing slash needed

MultiSet.shared.initialize(config: config, callback: delegate)
```

The value is applied at `initialize(config:callback:)`, before authentication. An access token is only valid against the host that issued it, so switching environments later requires `release()` followed by a fresh `initialize(...)`. Read `MultiSet.shared.activeBaseURL` to see which host requests are going to.

## Naming Your App on Query Logs

Localization and object tracking queries carry an `X-Client-Source` header naming the client, so MultiSet's query logs can tell your traffic apart. Unset, it is `ios-sdk-<version>`. Set your own name with `MultiSetConfig.clientSource`:

```swift
config.clientSource = "acme-museum-ios"
```

## Session Length and Token Renewal

Access tokens issued by the MultiSet API are short-lived, measured in minutes rather than hours. The SDK renews them for you: it reads each token's own lifetime, refreshes ahead of expiry, and if a request is rejected with HTTP 401 it refreshes once and replays that request. Concurrent requests share a single renewal rather than each triggering their own.

Nothing is required of your app, and sessions of any length work without re-initializing. Two consequences are worth knowing:

* `MultiSet.shared.authToken` changes during a session. Read it at the point of use and never cache it.
* If renewal ultimately fails, for example because credentials were revoked mid-session, the error surfaces through the operation that was in flight (`onLocalizationFailure` or `onObjectTrackingFailure`) with the authentication error in its message. `MultiSet.shared.isAuthenticated` remains `true`.

## Handling False Positives

In visually repetitive spaces the server can return a confident pose for the wrong location, which the user sees as the mesh jumping several metres. Enabling the pose consistency check makes the SDK compare each result against the device's own AR trajectory and discard results that contradict it:

```swift
var config = SDKConfig.buildConfig()
config.poseConsistencyCheck = true        // off by default
config.poseConsistencyThreshold = 4       // distance limit, metres, 1 to 15
config.poseConsistencyYawThreshold = 25   // heading limit, degrees, 5 to 60
```

A discarded result leaves the AR scene untouched and calls [`onLocalizationFalsePositive(info:)`](/native-sdk/ios-swift-native/api-reference/multisetcallback.md) instead of `onLocalizationSuccess`. See [Pose Consistency Settings](/native-sdk/ios-swift-native/api-reference/multisetconfig.md#pose-consistency-settings) for how the check decides and how to tune it.

## Threading

`MultiSet` and `MultiSetCallback` are isolated to the main actor. Every callback arrives on the main thread, so update UI directly; call `MultiSet.shared` from the main thread, or `await` it from a background task. See [Threading](/native-sdk/ios-swift-native/api-reference/lifecycle-and-runtimes.md#threading).

## Info.plist Configuration

Add the following keys to your `Info.plist`:

```xml
<key>NSCameraUsageDescription</key>
<string>Camera access is used to determine your position and to scan spaces into maps</string>

<key>NSLocationWhenInUseUsageDescription</key>
<string>Location access is used to improve localization accuracy and to record where a map was scanned</string>
```

Location is optional: without it, localization sends no `geoHint` and a scanned map carries no coordinates.

## License

Copyright (c) 2026 MultiSet AI. All rights reserved. Licensed under the MultiSet License. For license details, visit [www.multiset.ai](https://www.multiset.ai).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.multiset.ai/native-sdk/ios-swift-native.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
