> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ones1ght.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK Quickstart

> The fastest way to install the OneS1ght SDK.

## Setting up positioning

For everything positioning-related, see the [checklist before installing the SDK](/en/sdk/overview#checklist-before-installing-the-sdk).

## SDK and system requirements

The OneS1ght SDK runs in the following environments.

<Tabs>
  <Tab title="iOS (iPhone)">
    | Item                    | Requirement                                 |
    | ----------------------- | ------------------------------------------- |
    | Device                  | iPhone 12 or later (models with a UWB chip) |
    | iOS                     | 27.0 or later (Nearby Interaction support)  |
    | Development environment | Xcode 27 or later                           |
    | Minimum package target  | iOS 15 or later                             |
  </Tab>

  <Tab title="Android">
    Coming soon.
  </Tab>
</Tabs>

<Note>
  On unsupported devices or OS versions, positioning and data collection stop, but the rest of your app keeps working normally.
</Note>

### 1. Install the SDK

Install the OneS1ght iOS SDK.

<Tabs>
  <Tab title="Swift Package Manager">
    <Steps>
      <Step title="Open Add Package">
        In Xcode, click **\[File] → \[Add Package Dependencies…]**.
      </Step>

      <Step title="Enter the repository URL">
        Type the address below into the search field and click **\[Add Package]**.

        ```
        https://github.com/onecheck-inc/OneS1ght-iOS-SDK
        ```

        Leave **Dependency Rule** at `Up to Next Major Version` — `0.1.0`.
      </Step>

      <Step title="Add it to your target">
        Keep clicking **\[Add Package]** to add the `OneS1ght` library to your app target.
      </Step>

      <Step title="Verify">
        You can confirm the OneS1ght iOS SDK under **\[Package Dependencies]** in Xcode.
      </Step>
    </Steps>

    If you add it through `Package.swift`:

    ```swift theme={null}
    dependencies: [
        .package(url: "https://github.com/onecheck-inc/OneS1ght-iOS-SDK", from: "0.1.0"),
    ],
    targets: [
        .target(name: "YourApp", dependencies: [
            .product(name: "OneS1ght", package: "OneS1ght-iOS-SDK"),
        ])
    ]
    ```
  </Tab>

  <Tab title="Android">
    **Coming soon.** → [Android SDK](/en/sdk/integration/android)
  </Tab>
</Tabs>

### 2. Request location permission

Collecting visitor movement requires permission to use the UWB chip.

<Tabs>
  <Tab title="iOS">
    First add two permission descriptions to your app target's **Info** tab (or `Info.plist`).

    ```xml theme={null}
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>Used to determine your location inside the store.</string>
    <key>NSNearbyInteractionUsageDescription</key>
    <string>Used for precise UWB positioning.</string>
    ```

    | Key                                   | Purpose                                                      | When it is requested                                                         |
    | ------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------- |
    | `NSLocationWhenInUseUsageDescription` | Location permission — **a prerequisite for UWB positioning** | Requested by your app                                                        |
    | `NSNearbyInteractionUsageDescription` | Precise UWB positioning (Nearby Interaction)                 | When `permissions()` is called, or when the first positioning session starts |

    Ask for location permission first.

    ```swift theme={null}
    import CoreLocation

    let locationManager = CLLocationManager()
    locationManager.requestWhenInUseAuthorization()
    ```

    Check the Nearby Interaction permission through the SDK.

    ```swift theme={null}
    switch await OneS1ght.permissions() {
    case .authorized:  break
    case .denied:      showSettingsGuide()      // Cannot be requested again — send the user to Settings
    case .unsupported: showUnsupportedNotice()
    }
    ```

    <Warning>
      Without Nearby Interaction permission the SDK **cannot talk to the UWB modules.**
      Check the authorization state in `locationManagerDidChangeAuthorization` of `CLLocationManagerDelegate`.
    </Warning>

    <Warning>
      Once UWB permission is denied, **the platform does not let your app ask again.** To ask a second time, guide the user to **Settings**.

      ```swift theme={null}
      UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
      ```
    </Warning>
  </Tab>

  <Tab title="Android">
    **Coming soon.**
  </Tab>
</Tabs>

### 3. Initialize the SDK

Initialize the SDK **when the app opens**, following your app's own lifecycle.
`YOUR_SDK_API_KEY` is the SDK API key from the OneS1ght console, and `YOUR_GEOSPACE_SDK_KEY` is the GeoSpace SDK key.

<Tabs>
  <Tab title="SwiftUI (Swift)">
    Call it from the root view's `.task` or from `App`'s `init`.

    ```swift theme={null}
    import SwiftUI
    import OneS1ght

    @main
    struct MyApp: App {
        init() {
            Task { @MainActor in
                do {
                    try await OneS1ght.initialize(sdkKey: "YOUR_SDK_API_KEY",
                                                  geoSdkKey: "YOUR_GEOSPACE_SDK_KEY")
                } catch {
                    print("OneS1ght initialization failed: \(error)")   // Does not block the app from running
                }
            }
        }
        var body: some Scene { WindowGroup { ContentView() } }
    }
    ```
  </Tab>

  <Tab title="AppDelegate (Swift)">
    Call it from `didFinishLaunchingWithOptions`.

    ```swift theme={null}
    import UIKit
    import OneS1ght

    @main
    class AppDelegate: UIResponder, UIApplicationDelegate {
        func application(_ application: UIApplication,
                         didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            Task { @MainActor in
                do {
                    try await OneS1ght.initialize(sdkKey: "YOUR_SDK_API_KEY",
                                                  geoSdkKey: "YOUR_GEOSPACE_SDK_KEY")
                } catch {
                    print("OneS1ght initialization failed: \(error)")
                }
            }
            return true
        }
    }
    ```
  </Tab>

  <Tab title="SceneDelegate (Swift)">
    If your app is scene-based, call it from `willConnectTo`. `initialize` is **idempotent**, so it is safe
    even when several scenes connect.

    ```swift theme={null}
    import UIKit
    import OneS1ght

    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
        func scene(_ scene: UIScene,
                   willConnectTo session: UISceneSession,
                   options connectionOptions: UIScene.ConnectionOptions) {
            Task { @MainActor in
                do {
                    try await OneS1ght.initialize(sdkKey: "YOUR_SDK_API_KEY",
                                                  geoSdkKey: "YOUR_GEOSPACE_SDK_KEY")
                } catch {
                    print("OneS1ght initialization failed: \(error)")
                }
            }
        }
    }
    ```
  </Tab>

  <Tab title="Android">
    **Coming soon.**
  </Tab>
</Tabs>

**Log line for a successful initialization**

`YOUR_TENANT_NAME` is the organization name assigned to you by OneS1ght.

```
[I1001] initialization complete — tenant=YOUR_TENANT_NAME
```

### 4. Connect a profile

Set the **subject of your metrics** so you can filter reports and define segments. There is no restriction on
the attributes you attach; the first time you register a subject, the server returns a **unique profile ID** (`profileId`).

<Tabs>
  <Tab title="iOS (Swift)">
    ```swift theme={null}
     let profileId: String

     if let saved = savedProfileId {
         profileId = saved                       // Reuse the stored value on every launch.
     } else {
         profileId = try await OneS1ght.createProfile([   // Run this when there is no profile yet.
             "gender":   "F",
             "ageBand":  "20s",        // Put in whatever you want to segment by.
             "interest": "cosmetics",
         ])
         save(profileId)                         // Creating a profile issues a unique ID — you need it on the next visit.
     }

     OneS1ght.identify(profileId: profileId)    // Authenticate with the issued profile ID.
    ```
  </Tab>

  <Tab title="Android">
    **Coming soon.**
  </Tab>
</Tabs>

<Warning>
  **A lost profile ID cannot be recovered.** The server returns the value it issued but does not keep it on your
  app's behalf, so store it yourself.
</Warning>

<Warning>
  If you start positioning without connecting a profile, `.notIdentified` (`E1004`) is raised and positioning does not start.
</Warning>

### 5. Select the positioning space

Choose the space you want to position the user in.
In OneS1ght a tenant (organization) can have several **buildings**, and a building has several **floors**.
In other words, positioning requires exactly one floor to be selected.

<Tabs>
  <Tab title="iOS (Swift)">
    ```swift theme={null}
     let buildings = try await OneS1ght.buildings()      // Look up the buildings of the tenant (organization).
     let floors    = try await OneS1ght.floors(buildings[0].id) // Look up the floors of a building.

     try await OneS1ght.setFloorMap(floors[0], buildingID: buildings[0].id) // Fetch the target map by building ID and floor ID.

     try await OneS1ght.refreshZones() // Refresh the zone data on the map.
    ```
  </Tab>

  <Tab title="Android">
    **Coming soon.**
  </Tab>
</Tabs>

<Note>
  You can change what a floor contains in [Space management](/en/locator/areas) in the console. <br />
  Call refreshZones to pick up the changes.
</Note>

### 6. Register enter callbacks

You can attach events to specific zones on a floor. What the event does is entirely up to you.

<Tabs>
  <Tab title="iOS (Swift)">
    ```swift theme={null}
       let session = try OneS1ght.floorSession()

       // Zone enter · exit · dwell
       session.onZoneEnter = { zone in showCoupon(zone) }   // Callback fired when the user enters a zone.
       session.onZoneExit  = { zone in hideCoupon(zone) }   // Callback fired when the user leaves a zone.
       session.onZoneDwell = { zone, seconds in print("dwell \(Int(seconds))s — \(zone.name)") } // Callback fired while the user stays in a zone.

       // Live coordinate callback. (x, y are returned in floor-plan local meters.)
       session.onPosition = { coord in
           print("📍 x: \(coord.x), y: \(coord.y)")
       }

       // Trigger fired for a specific zone.
       session.onTriggers = { zoneId, triggers in handle(triggers) }

       // SDK debugging. (For development only — do not use it in a production build.)
       OneS1ght.onDebugLog = { level, line in print("[OneS1ght][\(level)] \(line)") }
    ```
  </Tab>

  <Tab title="Android">
    **Coming soon.**
  </Tab>
</Tabs>

<Warning>
  Register onDebugLog before initialize so that logs from the initialization step are captured as well.
</Warning>

### 7. Start positioning

Once the floor session is ready, start positioning.

<Tabs>
  <Tab title="iOS (Swift)">
    ```swift theme={null}
    try await session.begin() // Start positioning
    ```
  </Tab>

  <Tab title="Android">
    **Coming soon.**
  </Tab>
</Tabs>

### 8. Stop positioning

Stop positioning when the user leaves the area.
Initialization and the floor selection are kept, so you can restart at any time by calling `begin` again.

<Tabs>
  <Tab title="iOS (Swift)">
    ```swift theme={null}
      await session.end() // Stop positioning
    ```
  </Tab>

  <Tab title="Android">
    **Coming soon.**
  </Tab>
</Tabs>

## Full example

<Tabs>
  <Tab title="iOS (Swift)">
    ```swift theme={null}
    import SwiftUI
    import CoreLocation
    import OneS1ght

    @main
    struct MyApp: App {
        var body: some Scene {
            WindowGroup { StoreView() }
        }
    }

    struct StoreView: View {
        @State private var status = "idle"
        private let locationManager = CLLocationManager()

        var body: some View {
            Text(status)
                .task {
                    do {
                        // ① Initialize (once, at app start)
                        try await OneS1ght.initialize(sdkKey: "YOUR_SDK_API_KEY",
                                                      geoSdkKey: "YOUR_GEOSPACE_SDK_KEY")

                        // ② Permissions — in a real app, confirm the "allowed" response through the
                        //    delegate before starting positioning (starting before the first prompt is answered fails)
                        locationManager.requestWhenInUseAuthorization()
                        guard await OneS1ght.permissions() == .authorized else {
                            status = "Positioning permission is required"
                            return
                        }

                        // ③ Connect a profile (your app stores the issued value and reuses it)
                        let profileId = try await OneS1ght.createProfile(["ageBand": "20s"])
                        OneS1ght.identify(profileId: profileId)

                        // ④ Select the positioning space — skip this and no coordinates come out
                        let buildings = try await OneS1ght.buildings()
                        let floors = try await OneS1ght.floors(buildings[0].id)
                        try await OneS1ght.setFloorMap(floors[0], buildingID: buildings[0].id)

                        // ⑤ Register enter callbacks
                        let session = try OneS1ght.floorSession()
                        session.onPosition = { coord in
                            status = String(format: "x %.2f · y %.2f", coord.x, coord.y)
                        }
                        session.onZoneEnter = { zone in print("enter: \(zone.name)") }
                        session.onZoneExit  = { zone in print("exit: \(zone.name)") }

                        // ⑥ Start positioning
                        try await session.begin()
                    } catch {
                        status = "failed: \(error)"
                    }
                }
                .onDisappear {
                    // ⑦ Stop positioning
                    Task { @MainActor in
                        if let session = try? OneS1ght.floorSession() { await session.end() }
                    }
                }
        }
    }
    ```
  </Tab>

  <Tab title="Android">
    **Coming soon.**
  </Tab>
</Tabs>
