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

# iOS リファレンス

> OneS1ght iOS SDK の全 API シグネチャ・モデル・エラー定義です。

<Info>
  このドキュメントは **v0.1.13** 基準です。稼働中のバージョンは `OneS1ght.sdkVersion` で確認できます。
</Info>

`OneS1ght` はアプリ全体で 1 つだけの静的エントリーポイントです — インスタンスは作らず、すべての API を型から
直接呼び出します。すべての API は **メインアクター（@MainActor）** で呼び出します。

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

## ライフサイクル

| API                                                  | 説明                                                                                           |
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `initialize(sdkKey:geoSdkKey:baseURL:) async throws` | アプリ起動時に 1 回。端末チェック → キー検証 → 設定の読み込み。冪等 — 成功後の再呼び出しは無視、失敗後の再呼び出しはリトライ、**別のキーで呼び出すとセッションを再構成** |
| `permissions() async -> PermissionStatus`            | 測位の権限を確認 — **呼び出すとシステムのポップアップが表示されます**                                                       |
| `reset() async`                                      | セッションを破棄。以降、別のキーで `initialize` できます（ランタイムでのキー入れ替え用）                                          |
| `setLanguage(_ code: String?)`                       | SDK のログ・案内文言の言語 — `"ko"`·`"ja"`·`"en"`。`nil` なら端末の言語（既定）。`0.1.11~`                           |

`initialize` のパラメーター:

| パラメーター      | 型                      | 説明                                                                      |
| ----------- | ---------------------- | ----------------------------------------------------------------------- |
| `sdkKey`    | `String`               | OneS1ght コンソールで発行（`ock_sdk_…`）— 認証・ゾーン・収集・イベント・図面                       |
| `geoSdkKey` | `String?`（デフォルト `nil`） | **暫定パラメーター** — ロケーター・セッション・フロア情報の取得用の過渡的な値で、担当者からお渡しします。**省略すると測位のみ無効** |
| `baseURL`   | `URL`（デフォルトは本番サーバー）    | 自社サーバーを構築したお客様のみ。開発／本番の区別はこのパラメーターではなく **キー** で分かれます                    |

デフォルトの `baseURL` は `https://console.ones1ght.com/api/sdk/v1` です。

<Warning>
  **`initialize` は建物・フロアを取得しません。** 空間の設定は `setFloorMap` の役割です —
  省略すると測位パイプラインは動きますが座標が出ません（`E3001`）。
</Warning>

## 測位セッション — FloorSession

<Tabs>
  <Tab title="SwiftUI">
    ```swift theme={null}
    .task {
        guard let session = try? OneS1ght.floorSession() else { return }
    }
    ```
  </Tab>

  <Tab title="UIKit">
    ```swift theme={null}
    override func viewDidLoad() {
        super.viewDidLoad()
        guard let session = try? OneS1ght.floorSession() else { return }
    }
    ```
  </Tab>
</Tabs>

| API                                     | 説明                                                           |
| --------------------------------------- | ------------------------------------------------------------ |
| `floorSession() throws -> FloorSession` | セッションを取得。**常に同じインスタンス** — UWB ラジオ・判定エンジン・座標バッファは端末に 1 つだけです  |
| `session.begin() async throws`          | 測位を開始（店舗への進入時）                                               |
| `session.begin(provider:) async throws` | カスタム測位の注入 — シミュレーターでのテスト（`MockPositioningProvider`）など特殊な経路向け |
| `session.end() async`                   | 測位を停止し、残りの座標を送信。初期化の状態は保持されるため `begin` の再呼び出しで再開できます         |
| `session.floor: Floor?`                 | 現在設定されているフロア                                                 |
| `session.isRunning: Bool`               | 測位が稼働中かどうか                                                   |

## コールバック

セッションのコールバックは `FloorSession` インスタンスに、デバッグログは `OneS1ght` 型に設定します。

| コールバック                | 型                                 | 説明                                                |
| --------------------- | --------------------------------- | ------------------------------------------------- |
| `session.onPosition`  | `((Coordinates) -> Void)?`        | リアルタイム座標（図面ローカルのメートル）— 最大 4 回/秒                   |
| `session.onZoneEnter` | `((Zone) -> Void)?`               | ゾーン進入 — 端末内の判定と同時（サーバー往復なし）                       |
| `session.onZoneExit`  | `((Zone) -> Void)?`               | ゾーン退出                                             |
| `session.onZoneDwell` | `((Zone, TimeInterval) -> Void)?` | 滞在の発火 —（ゾーン、滞在秒数）                                 |
| `session.onTriggers`  | `((String, [Trigger]) -> Void)?`  | (zoneId, トリガー配列) — **サーバーがマッチング** したパーソナライズアクション  |
| `OneS1ght.onDebugLog` | `((LogLevel, String) -> Void)?`   | SDK 内部の動作ログ。`initialize` より先に登録すると初期化のログを取りこぼしません |

<Warning>
  `0.1.12` から `onDebugLog` は**レベルと文字列の両方**を渡します（以前は `(String) -> Void`）。
  [移行ガイド](/sdk/integration/ios/migration-guide) をご確認ください。
</Warning>

### LogLevel

```swift theme={null}
public enum LogLevel { case log, info, warn, error }   // log < info < warn < error
```

`Comparable` なので `level >= .warn` のように絞り込めます。

| レベル      | 意味                                    | 例                          |
| -------- | ------------------------------------- | -------------------------- |
| `.log`   | 流れの記録 — 普段は読まなくて構いません                 | 座標の送信、エリアの投入               |
| `.info`  | 知っておくと役立つこと — 正常ですが目に留まると助かります        | この階にエリアがない、リアルタイム接続完了      |
| `.warn`  | 確認が必要 — 不具合ではありませんが、このままでは期待どおりに動きません | 集計間隔が座標周期より短く IN が発火しない    |
| `.error` | 不具合 — 直さないとその機能が動きません                 | 有効アンカー不足で測位不可、セッション ID 未設定 |

<Note>
  「この階に登録されたエリアがありません」は、コンソールでまだエリアを作っていない
  **正常な状態**です。不具合ではないため `.info` です。
</Note>

<Warning>
  サーバー応答は `onTriggers` だけです — ネットワークが切れると進入判定は届きますが、トリガーは届きません。
</Warning>

## 空間の取得

エンドポイントごとに 1 メソッドで、一覧と単件が対になっています。

| API                                            | 説明                                            |
| ---------------------------------------------- | --------------------------------------------- |
| `buildings() async throws -> [Building]`       | 建物の一覧。`geoSdkKey` なしで初期化した場合は空の配列             |
| `building(_:) async throws -> Building`        | 建物 1 件                                        |
| `floors(_:) async throws -> [Floor]`           | フロアの一覧 — **図面画像なし**（`image == nil`。一覧を軽く保つため） |
| `floor(_:_:) async throws -> Floor`            | フロア 1 件 — 図面画像を含む（キャッシュから返るため往復は増えません）        |
| `zones(_:_:) async throws -> [Zone]`           | フロアのゾーン一覧                                     |
| `zone(_:_:_:) async throws -> Zone`            | ゾーン 1 件                                       |
| `locators(_:_:) async throws -> FloorLocators` | フロアのロケーター配置と UWB セッション ID                     |

## フロアの指定

| API                                       | 説明                                                                             |
| ----------------------------------------- | ------------------------------------------------------------------------------ |
| `setFloorMap(_:buildingID:) async throws` | フロアを指定 — ロケーター・セッション・ゾーンを内部エンジンに注入します。稼働中は **即座にフロアを切り替え**（セッションは維持）。`nil` で解除 |
| `refreshZones() async -> [Zone]`          | 現在のフロアのゾーンのみ再取得（軽量 — 図面の再ダウンロードなし）。判定エンジンに即時反映                                 |

## プロフィール

| API                                               | 説明                                                      |
| ------------------------------------------------- | ------------------------------------------------------- |
| `createProfile(_:) async throws -> String`        | プロフィールを作成 — サーバーが発行した `profileId` を返します。**アプリが保管して再利用** |
| `getProfile(_:) async throws -> [String: String]` | 属性の取得                                                   |
| `putProfile(_:_:) async throws`                   | 属性の **全置換**（部分更新ではありません）                                |
| `deleteProfile(_:) async throws`                  | プロフィールの削除（蓄積された座標・イベントは保存ポリシーに従います）                     |
| `identify(profileId:)`                            | 連携 — 測位の前に必ず実行。ログアウト時は `nil`                            |

<Note>
  お客様の会員 ID はサーバーには届きません — マッピングはお客様側でのみ保持します。
</Note>

<Warning>
  `savedProfileId ?? (try await OneS1ght.createProfile(…))` は **コンパイルできません。**
  `??` の右辺は autoclosure のため `try await` を置けません — `if let` で書いてください。
</Warning>

## データ送信

座標は **300 件または 60 秒** のいずれか早い方で送信されます。
バックグラウンドへの遷移時と `end()` 時にも残りを送信します。

| API            | 説明                    |
| -------------- | --------------------- |
| `send() async` | バッファを今すぐ送信            |
| `empty()`      | バッファを **破棄** — 送信しません |

## 状態の取得

| API                  | 型                    | 説明                                             |
| -------------------- | -------------------- | ---------------------------------------------- |
| `isInitialized`      | `Bool`               | 初期化に成功したか（端末チェック通過 + キー有効 + 設定読み込み）            |
| `deviceAvailability` | `DeviceAvailability` | 測位可否と、不可の理由。`initialize` の前でも呼び出せます（ネットワーク未使用） |
| `isDeviceAvailable`  | `Bool`               | 上記の簡易版（`== .available`）                        |
| `sdkVersion`         | `String`             | SDK のバージョン（例: `"0.1.13"`）                      |

```swift theme={null}
public enum DeviceAvailability: Equatable {
    case available            // 測位可能
    case osVersionTooLow      // iOS 27 未満 — 「OS を更新すると利用できます」と案内
    case deviceNotSupported   // UWB チップ非対応 — 「iPhone 12 以降が必要です」と案内
}

public enum PermissionStatus: Equatable {
    case authorized           // 測位を開始できる
    case denied               // アプリから再度リクエストできない — 設定アプリへ案内
    case unsupported          // この端末・OS では測位そのものが不可
}
```

## エラー — SdkError

`SdkError` は SDK 自身が投げる 5 種類です。通信の失敗は `ApiError` で届きます。
どちらの型も `.code` から [エラーコード](/ja/sdk/faq/error-code) を取り出せます。

<Tabs>
  <Tab title="SwiftUI">
    ```swift theme={null}
    .task {
        do {
            try await OneS1ght.initialize(sdkKey: key)
        } catch let e as SdkError {
            print(e.code.rawValue)                  // "E1003"
        } catch let e as ApiError {
            print(e.code.rawValue, e.description)   // "E5005" · "応答の解析に失敗 — …"
        }
    }
    ```
  </Tab>

  <Tab title="UIKit">
    ```swift theme={null}
    Task { @MainActor in
        do {
            try await OneS1ght.initialize(sdkKey: key)
        } catch let e as SdkError {
            print(e.code.rawValue)                  // "E1003"
        } catch let e as ApiError {
            print(e.code.rawValue, e.description)   // "E5005" · "応答の解析に失敗 — …"
        }
    }
    ```
  </Tab>
</Tabs>

| ケース                            | コード     | 発生タイミング                       | 対処                           |
| ------------------------------ | ------- | ----------------------------- | ---------------------------- |
| `SdkError.notInitialized`      | `E1001` | `initialize` なしで他の API を呼び出した | 呼び出し順序を修正                    |
| `SdkError.notIdentified`       | `E1004` | `identify` なしで測位を開始した         | 認証直後に `identify(profileId:)` |
| `SdkError.positioningDisabled` | `E1003` | キーは有効だが測位機能がオフ                | コンソール設定を確認・サポートへ問い合わせ        |
| `SdkError.osVersionTooLow`     | `E2001` | iOS 27 未満                     | OS 更新を案内                     |
| `SdkError.deviceNotSupported`  | `E2002` | UWB チップなし                     | `deviceAvailability` で事前に分岐  |
| `ApiError.invalidKey`          | `E1002` | キーが不正、または破棄済み（401）            | コンソールでキーの状態を確認・再発行           |
| `ApiError.forbidden`           | `E5004` | 他テナントのリソース（403）               | キーのスコープを確認                   |
| `ApiError.notFound`            | `E5003` | 対象なし（404）                     | 多くは契約の不一致                    |
| `ApiError.unprocessable`       | `E5003` | ペイロードの問題（422）                 | SDK・サーバーのバージョン不一致を疑う         |
| `ApiError.server`              | `E5002` | サーバー 5xx                      | 統合管理者に確認                     |
| `ApiError.network`             | `E5001` | オフライン・タイムアウト                  | 自動でリトライ                      |
| `ApiError.decoding`            | `E5005` | 応答 JSON の形式が不一致               | 理由は `description` に入ります      |

測位ランタイム（`E4001`〜`E4003`）と空間設定（`E3001`〜`E3004`）は throw されず、**ログにのみ** 残ります —
`onDebugLog` またはコンソールのログ分析でご確認ください。[全一覧](/ja/sdk/faq/error-code)

## モデル

### Coordinates — リアルタイム座標

```swift theme={null}
public struct Coordinates: Codable, Equatable {
    public let x: Double   // メートル
    public let y: Double   // メートル
    public let z: Double   // 高さ（2D 測位の場合は 0）
}
```

### Building · Floor — 建物・フロア

| 型          | フィールド                             | 説明                                                            |
| ---------- | --------------------------------- | ------------------------------------------------------------- |
| `Building` | `id` · `name`                     | 建物 ID · 名前                                                    |
|            | `floorCount: Int?`                | フロア数（サーバーが返さない場合は `nil`）                                      |
| `Floor`    | `id` · `name`                     | フロア ID · 名前                                                   |
|            | `image: Data?`                    | 図面 PNG — `floors(_:)` で取得すると `nil`、`floor(_:_:)` で取得すると格納されます |
|            | `hasPlan: Bool`                   | 図面が登録されているか                                                   |
|            | `originX` · `originY`             | 図面原点のオフセット（メートル）                                              |
|            | `widthM` · `heightM`              | 図面の実サイズ（メートル）                                                 |
|            | `minX` · `minY` · `maxX` · `maxY` | 配置範囲（原点 + サイズから導出）                                            |

### Locator · FloorLocators — ロケーター

| 型               | フィールド                    | 説明                                         |
| --------------- | ------------------------ | ------------------------------------------ |
| `Locator`       | `address: Int`           | UWB MAC の下位 2 バイト（例: `0x9DD7`）             |
|                 | `x` · `y` · `z`          | 図面ローカルのメートル                                |
| `FloorLocators` | `locators: [Locator]`    | このフロアに設置されたロケーター                           |
|                 | `sessionId: Int?`        | UWB セッション — フロアごとに異なります。`nil` なら測位を開始できません |
|                 | `positioningReady: Bool` | ロケーターがあり、セッションもあるか（導出値）                    |

### Zone — ゾーン

| フィールド                         | 型            | 説明                                        |
| ----------------------------- | ------------ | ----------------------------------------- |
| `id` / `name`                 | `String`     | ゾーンの識別子 · 名前                              |
| `polygon`                     | `[Position]` | 頂点（順序どおり）— `Position` は図面ローカルのメートル (x, y) |
| `inDist`                      | `Double`     | 進入判定の距離(m) — デフォルト 3.0                    |
| `inCount` / `inCountInterval` | `Int`        | 進入確定に必要な検知回数 · カウント間隔（秒）                  |
| `outPeriod`                   | `Int`        | 退出判定の猶予                                   |
| `priority`                    | `Int`        | エリアが重なった場合の優先度                            |
| `callInout`                   | `Bool`       | 進入・退出コールバックを発行するか                         |
| `dwellSeconds`                | `Int?`       | 滞在の発火周期（秒）— `nil` ならデフォルトの 5 秒            |

これらの判定パラメーターはサーバーのゾーンメタデータから配信され、コンソールの
**空間管理 → ゾーンを選択 → SDK エリア判定** でゾーンごとに設定します（[設定方法](/ja/locator/areas)）。
`zone.contains(Position(x:y:))` で任意の座標がゾーンに含まれるかを直接判定することもできます。

<Warning>
  `inCount` が `0` の場合、進入が永久に確定せずゾーンイベントが発生しません。
  コンソールで登録した新規ゾーンのデフォルトは `1` です。値が `0` の古いゾーンがある場合はコンソールで確認してください。
</Warning>

### Trigger — パーソナライズアクション

| フィールド        | 型                   | 説明                                              |
| ------------ | ------------------- | ----------------------------------------------- |
| `trigger_id` | `String`            | トリガーの識別子                                        |
| `type`       | `String`            | `signage · coupon · tracking · merch · generic` |
| `payload`    | `[String: String]?` | タイプ別の付加情報                                       |

### MockPositioningProvider — テスト用

```swift theme={null}
public final class MockPositioningProvider: PositioningProvider {
    public init()
    public func simulateEnter(buildingId: String)
    public func simulatePosition(_ c: Coordinates, floorId: String, at: Date = Date())
    public func simulateZone(_ zoneId: String, status: ZoneEventStatus, floorId: String, at: Date = Date())
}
```

`session.begin(provider:)` で注入し、UWB なしで SDK のパイプラインを確認します。
`ZoneEventStatus` は `.enter` · `.dwell` · `.exit` です。

## スレッド

* `OneS1ght` のすべての API は `@MainActor` です。SwiftUI のビューや `.task` からはそのまま呼び出せます。
  バックグラウンドのコンテキストからは `await MainActor.run { … }` で包んでください。
* コールバック（`onPosition` など）もメインアクターで呼び出されます — UI をそのまま更新しても安全です。

## 既知の制約

<Warning>
  iOS の UWB は **フォアグラウンド専用** です。アプリがバックグラウンドに入ると測位が停止してバッファを送信し、
  復帰すると再開します。プラットフォームの制約のため SDK では回避できません。
</Warning>

<Warning>
  座標バッファはメモリ上にあります。アプリが強制終了されると、未送信の座標は失われます（`E5006`）。
</Warning>

<Warning>
  LICENSE ファイルはまだありません。利用条件は別途の契約に従います。
</Warning>
