> ## 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.

# はじめに

> OneS1ght SDK for iOS

<Info>
  インストールと初期化については [SDK クイックスタート](/ja/sdk/quick-start) をご覧ください。<br />
</Info>

## リアルタイム測位情報の受信

セッションの `onPosition` コールバックでアプリ利用者の座標を受信します。<br />
サーバー送信とは関係なく呼び出され、1 秒あたり 4 回座標を受信します。<br />
座標の単位は **メートル（m）** です。

<Tabs>
  <Tab title="SwiftUI">
    ```swift theme={null}
    struct StoreMapView: View {
        @State private var me: Coordinates?

        var body: some View {
            MapCanvas(marker: me)
                .task {
                    guard let session = try? OneS1ght.floorSession() else { return }
                    // coord.x, coord.y: 図面ローカル座標（メートル、左下が原点）· coord.z: 高さ
                    session.onPosition = { coord in me = coord }
                }
        }
    }
    ```
  </Tab>

  <Tab title="UIKit">
    ```swift theme={null}
    final class StoreMapViewController: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()

            guard let session = try? OneS1ght.floorSession() else { return }
            // coord.x, coord.y: 図面ローカル座標（メートル、左下が原点）· coord.z: 高さ
            session.onPosition = { [weak self] coord in
                self?.updateMyLocationMarker(x: coord.x, y: coord.y)
            }
        }
    }
    ```
  </Tab>
</Tabs>

## 図面の表示

測位中のフロアの図面を利用者に表示します。<br />図面を表示するには建物 ID とフロア ID が必要です。
図面の上に利用者の位置を表示するには、`onPosition` の座標を図面にマッピングします。

<Tabs>
  <Tab title="SwiftUI">
    ```swift theme={null}
    struct FloorPlanView: View {
        let building: Building
        let floorId: String
        @State private var plan: UIImage?

        var body: some View {
            Group {
                if let plan { Image(uiImage: plan).resizable().scaledToFit() }
                else { ProgressView() }
            }
            .task {
                guard let floor = try? await OneS1ght.floor(building.id, floorId) else { return }
                plan = floor.image.flatMap(UIImage.init(data:))
                setBounds(minX: floor.minX, minY: floor.minY,
                          maxX: floor.maxX, maxY: floor.maxY)
            }
        }
    }
    ```
  </Tab>

  <Tab title="UIKit">
    ```swift theme={null}
    final class FloorPlanViewController: UIViewController {
        private let planView = UIImageView()

        override func viewDidLoad() {
            super.viewDidLoad()

            Task { @MainActor in
                let floor = try await OneS1ght.floor(building.id, floorId)
                planView.image = floor.image.flatMap(UIImage.init(data:))
                setBounds(minX: floor.minX, minY: floor.minY,
                          maxX: floor.maxX, maxY: floor.maxY)
            }
        }
    }
    ```
  </Tab>
</Tabs>

<Note>
  `hasPlan == false` の場合は、そのフロアに図面が設定されていないことを表します。<br />
  [ドキュメント - 図面の配置・クラスター](/ja/geospace/placement) をご覧ください。
</Note>

ロケーターの配置と UWB セッション ID を確認したい場合は `locators(_:_:)` で個別に取得できます。
`positioningReady` が `false` のフロアはロケーター・セッション情報がなく、測位を開始できない状態です。

## ゾーンイベント

進入・退出・滞在はそれぞれ別のコールバックで届きます。判定は**端末内**で行われるため、サーバー往復の遅延がありません。

<Tabs>
  <Tab title="SwiftUI">
    ```swift theme={null}
    @State private var banner: String?

    session.onZoneEnter = { zone in banner = "進入: \(zone.name)" }
    session.onZoneExit  = { zone in banner = nil }
    session.onZoneDwell = { zone, seconds in
        banner = "滞在: \(zone.name) — \(Int(seconds))秒"
    }
    ```
  </Tab>

  <Tab title="UIKit">
    ```swift theme={null}
    session.onZoneEnter = { [weak self] zone in self?.showBanner("進入: \(zone.name)") }
    session.onZoneExit  = { [weak self] _    in self?.hideBanner() }
    session.onZoneDwell = { [weak self] zone, seconds in
        self?.showBanner("滞在: \(zone.name) — \(Int(seconds))秒")
    }
    ```
  </Tab>
</Tabs>

| コールバック        | 発生タイミング                            |
| ------------- | ---------------------------------- |
| `onZoneEnter` | 特定のゾーンに進入したときに発生します。               |
| `onZoneExit`  | 特定のゾーンから退出したときに発生します。              |
| `onZoneDwell` | 特定のゾーンに滞在しているときに発生します。（デフォルト: 5 秒） |

各フロアのゾーンは [空間管理](/ja/locator/areas) で設定できます。

<Warning>
  フロアにゾーンが 1 つもない場合、座標は蓄積されますが進入・退出は発生しません（`E3004`）。
</Warning>

### ゾーン情報の更新

コンソールでゾーン情報を変更・保存した場合は、コード側で再取得する必要があります。

```swift theme={null}
let zones = await OneS1ght.refreshZones()
```

## プロフィールの作成と連携

来訪者の動線を収集するには、アカウントごとにプロフィールを発行し、その ID を独自のストレージに保管します。

```swift theme={null}
// 1. 初回接続時にプロフィールを発行します。
// プロフィールの内容は、今後コンソールでレポートを発行する際のセグメント構成に使用します。Key-Value に制限はありません。
let profileId = try await OneS1ght.createProfile([
    "gender":   "F",
    "ageBand":  "20s",
    "interest": "cosmetics",
])

// 2. 発行済みのプロフィールで再接続する場合は、プロフィール ID で認証します。
OneS1ght.identify(profileId: savedProfileId)

// 3. 利用者の測位収集を終了し、プロフィール設定を解除します。
OneS1ght.identify(profileId: nil)
```

| メソッド                   | 用途                               |
| ---------------------- | -------------------------------- |
| `createProfile(_:)`    | 新しいプロフィールを作成します。                 |
| `getProfile(_:)`       | 現在のプロフィール情報を取得します。               |
| `putProfile(_:_:)`     | 新しいプロフィール情報に更新します。以前のデータは削除されます。 |
| `deleteProfile(_:)`    | プロフィール情報を削除します。                  |
| `identify(profileId:)` | 有効なプロフィールかどうかを検証します。             |

<Note>
  発行されたプロフィール ID はお客様側で管理してください。紛失すると復元できませんのでご注意ください。
</Note>

## 建物・フロアの選択

測位情報を収集するには、建物とフロアを設定する必要があります。
1 つの建物は複数のフロアを持つことができます。

`setFloorMap` は、測位に使うロケーター情報と地図、そして利用者に表示するゾーン情報をまとめて設定します。

```swift theme={null}
// 建物とフロアを取得します。
let buildings = try await OneS1ght.buildings()
let floors    = try await OneS1ght.floors(buildings[0].id)

// フロアと建物 ID で最初の地図を設定します。
try await OneS1ght.setFloorMap(floors[0], buildingID: buildings[0].id)

// セッションを維持したまま別のフロアに変更し、地図の内容を差し替えます。
try await OneS1ght.setFloorMap(floors[1], buildingID: buildings[0].id)

// すべての測位収集が終わったら地図設定を解除します。
try await OneS1ght.setFloorMap(nil)
```

<Note>
  建物とフロアの情報を取得するには、初期化時に `geoSdkKey` を渡す必要があります。渡していない場合は取得できません。
</Note>

## 非対応端末の例外処理

OneS1ght SDK を利用するには、利用者の端末が最小要件を満たしている必要があります。[SDK クイックスタート](/ja/sdk/quick-start)<br />
非対応端末にインストールされても、SDK の影響を受けずにアプリを利用できます。

<Note>
  `isDeviceAvailable` は端末が SDK を利用できるかを確認する簡易的な形で、Boolean（true/false）を返します。
</Note>

## データ送信の制御

OneS1ght SDK は内部ロジックに従って収集した測位情報を送信しますが、例外的な状況では収集済みの測位情報を即時送信したり破棄したりできます。

```swift theme={null}
await OneS1ght.send()      // 測位情報を手動で送信します。
OneS1ght.empty()           // 収集した測位情報を削除し、送信しません。
```

## セッションのリセット

蓄積されたセッション情報を初期化し、新しいセッションとして開始します。

```swift theme={null}
await OneS1ght.reset()
```

## 環境別 API キーの使い分け

OneS1ght コンソールでは、API キーの発行時に開発用（Development）と本番用（Production）を分けて発行できます。<br />
2 つの環境は隔離されており、互いに干渉することはありません。<br />
テスト中のデータが本番データに混ざらないよう、環境ごとに API キーを設定できます。

```swift theme={null}
#if DEBUG   // 開発モードの場合
let sdkKey = "YOUR_DEVELOPMENT_API_KEY"
#else
let sdkKey = "YOUR_PRODUCTION_API_KEY"
#endif
try await OneS1ght.initialize(sdkKey: sdkKey, geoSdkKey: geoSdkKey)
```

<Note>
  実際の測位内容は iOS の仕様上シミュレーターでは確認できません。実機にインストールしてテストしてください。
</Note>

## トラブルシューティング

<CardGroup cols={2}>
  <Card title="ログの取得方法" icon="wrench" href="/ja/sdk/faq/logging">
    開発中のログの使い方をご案内します。
  </Card>

  <Card title="SDK エラーコード一覧" icon="triangle-exclamation" href="/ja/sdk/faq/error-code">
    開発中に発生するエラーの内容をご案内します。
  </Card>
</CardGroup>
