> ## 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 クイックスタート

> OneS1ght SDK を最短で導入する方法をご案内します。

## 測位情報の設定

測位情報に関する項目は [SDK 導入前のチェックリスト](/ja/sdk/overview#sdk-導入前のチェックリスト) をご覧ください。

## SDK・システム要件

OneS1ght SDK は以下の環境で動作します。

<Tabs>
  <Tab title="iOS (iPhone)">
    | 項目            | 要件                             |
    | ------------- | ------------------------------ |
    | 端末            | iPhone 12 以降（UWB モジュール搭載モデル）   |
    | iOS           | 27.0 以降（Nearby Interaction 対応） |
    | 開発環境          | Xcode 27 以降                    |
    | パッケージの最小ターゲット | iOS 15 以降                      |
  </Tab>

  <Tab title="Android">
    準備中です。
  </Tab>
</Tabs>

<Note>
  SDK が対応していない端末・OS では測位とデータ収集は停止しますが、アプリ自体は正常に動作します。
</Note>

### 1. SDK の導入

OneS1ght iOS SDK を導入します。

<Tabs>
  <Tab title="Swift Package Manager">
    <Steps>
      <Step title="パッケージ追加を開く">
        Xcode で **\[File] → \[Add Package Dependencies…]** をクリックします。
      </Step>

      <Step title="リポジトリ URL の入力">
        検索欄に以下のアドレスを入力し、**\[Add Package]** をクリックします。

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

        **Dependency Rule** は `Up to Next Major Version` — `0.1.0` のままにします。
      </Step>

      <Step title="ターゲットへの追加">
        **\[Add Package]** をクリックし続けて、`OneS1ght` ライブラリをアプリのターゲットに追加します。
      </Step>

      <Step title="確認">
        Xcode の **\[Package Dependencies]** で OneS1ght iOS SDK が追加されたことを確認できます。
      </Step>
    </Steps>

    `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">
    **準備中です。** → [Android SDK](/ja/sdk/integration/android)
  </Tab>
</Tabs>

### 2. 位置情報の権限リクエスト

利用者の動線を収集するには、UWB モジュールの権限の許可が必要です。

<Tabs>
  <Tab title="iOS">
    まず、アプリターゲットの **Info** タブ（または `Info.plist`）に権限の説明を 2 つ追加します。

    ```xml theme={null}
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>店舗内での位置確認のために使用します。</string>
    <key>NSNearbyInteractionUsageDescription</key>
    <string>UWB 精密測位のために使用します。</string>
    ```

    | キー                                    | 用途                           | リクエストのタイミング                             |
    | ------------------------------------- | ---------------------------- | --------------------------------------- |
    | `NSLocationWhenInUseUsageDescription` | 位置情報の権限 — **UWB 測位の前提条件**    | アプリが直接リクエスト                             |
    | `NSNearbyInteractionUsageDescription` | UWB 精密測位（Nearby Interaction） | `permissions()` の呼び出し時、または最初の測位セッション開始時 |

    先に位置情報の権限を取得します。

    ```swift theme={null}
    import CoreLocation

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

    Nearby Interaction の権限は SDK で確認します。

    ```swift theme={null}
    switch await OneS1ght.permissions() {
    case .authorized:  break
    case .denied:      showSettingsGuide()      // 再リクエスト不可 — 設定アプリへ案内
    case .unsupported: showUnsupportedNotice()
    }
    ```

    <Warning>
      Nearby Interaction の権限がないと、**UWB モジュールと通信できません。**
      `CLLocationManagerDelegate` の `locationManagerDidChangeAuthorization` で許可の状態を確認してください。
    </Warning>

    <Warning>
      UWB モジュールの権限が一度拒否されると、**仕様上アプリから直接リクエストすることはできません。** 再度お願いする場合は **設定** へ誘導してください。

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

  <Tab title="Android">
    **準備中です。**
  </Tab>
</Tabs>

### 3. SDK の初期化

アプリのライフサイクルに合わせて、**アプリの起動時**に SDK を初期化してください。
`YOUR_SDK_API_KEY` は OneS1ght コンソールで発行する SDK API キー、`YOUR_GEOSPACE_SDK_KEY` は GeoSpace の SDK キーです。

<Tabs>
  <Tab title="SwiftUI (Swift)">
    ルートビューの `.task`、または `App` の `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 の初期化に失敗: \(error)")   // アプリの実行は妨げません
                }
            }
        }
        var body: some Scene { WindowGroup { ContentView() } }
    }
    ```
  </Tab>

  <Tab title="AppDelegate (Swift)">
    `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 の初期化に失敗: \(error)")
                }
            }
            return true
        }
    }
    ```
  </Tab>

  <Tab title="SceneDelegate (Swift)">
    シーン単位で組み込むアプリでは `willConnectTo` で呼び出します。`initialize` は **冪等**なので、
    シーンが複数回接続されても安全です。

    ```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 の初期化に失敗: \(error)")
                }
            }
        }
    }
    ```
  </Tab>

  <Tab title="Android">
    **準備中です。**
  </Tab>
</Tabs>

**初期化成功時のログ**

`YOUR_TENANT_NAME` は OneS1ght が付与する組織の名前です。

```
[I1001] 初期化完了 — tenant=YOUR_TENANT_NAME
```

### 4. プロフィールの連携

レポートのフィルタリングやセグメント設定のために **指標の対象** を設定します。指標対象の属性に制限はなく、
最初に指標対象を設定すると、サーバーが **一意のプロフィール ID**（`profileId`）を返します。

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

     if let saved = savedProfileId {
         profileId = saved                       // 起動のたびに保存しておいた値を再利用します。
     } else {
         profileId = try await OneS1ght.createProfile([   // プロフィール情報がない場合に実行します。
             "gender":   "F",
             "ageBand":  "20s",        // セグメントに使いたいデータを自由に指定してください。
             "interest": "cosmetics",
         ])
         save(profileId)                         // プロフィール作成時に一意の ID が発行され、次回の来訪時に必要です。
     }

     OneS1ght.identify(profileId: profileId)    // 発行されたプロフィール ID で認証します。
    ```
  </Tab>

  <Tab title="Android">
    **準備中です。**
  </Tab>
</Tabs>

<Warning>
  **プロフィール ID を紛失すると復元できません。** サーバーは発行した値を返すだけでアプリの代わりに保管はしないため、
  別途ストレージを用意して保存してください。
</Warning>

<Warning>
  プロフィールを連携せずに測位を開始した場合は `.notIdentified`（`E1004`）が発生し、測位は開始されません。
</Warning>

### 5. 測位空間の設定

利用者を測位する空間を指定します。
OneS1ght では、テナント（組織）に複数の **建物** が存在し、1 つの建物には複数の **フロア** があります。
つまり、測位を行うには 1 つのフロアが選択されている必要があります。

<Tabs>
  <Tab title="iOS (Swift)">
    ```swift theme={null}
     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) // 建物 ID とフロア ID で対象の地図を受け取ります。

     try await OneS1ght.refreshZones() // 地図内のゾーン情報を更新します。
    ```
  </Tab>

  <Tab title="Android">
    **準備中です。**
  </Tab>
</Tabs>

<Note>
  フロアの内容はコンソールの [空間管理](/ja/locator/areas) で変更できます。<br />
  変更した内容は refreshZones の呼び出しで反映されます。
</Note>

### 6. 進入コールバックの登録

フロア内の特定のゾーンにイベントを設定できます。イベントの内容は自由に実装できます。

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

       // ゾーンの進入・退出・滞在
       session.onZoneEnter = { zone in showCoupon(zone) }   // 特定のゾーンに進入したときのコールバックです。
       session.onZoneExit  = { zone in hideCoupon(zone) }   // 特定のゾーンから退出したときのコールバックです。
       session.onZoneDwell = { zone, seconds in print("滞在 \(Int(seconds))秒 — \(zone.name)") } // 特定のゾーンに滞在したときのコールバックです。

       // リアルタイム座標のコールバックです。（x, y 座標は図面ローカルのメートルで返します。）
       session.onPosition = { coord in
           print("📍 x: \(coord.x), y: \(coord.y)")
       }

       // 特定のゾーンのイベントトリガーです。
       session.onTriggers = { zoneId, triggers in handle(triggers) }

       // SDK のデバッグ用です。（開発用であり、Production 環境では使用できません。）
       OneS1ght.onDebugLog = { level, line in print("[OneS1ght][\(level)] \(line)") }
    ```
  </Tab>

  <Tab title="Android">
    **準備中です。**
  </Tab>
</Tabs>

<Warning>
  onDebugLog は initialize より先に呼び出すことで、初期化の段階からデバッグログを確認できます。
</Warning>

### 7. 測位の開始

フロアへの進入準備（セッション）が完了したら測位を開始します。

<Tabs>
  <Tab title="iOS (Swift)">
    ```swift theme={null}
    try await session.begin() // 測位の開始
    ```
  </Tab>

  <Tab title="Android">
    **準備中です。**
  </Tab>
</Tabs>

### 8. 測位の終了

エリアから退出した場合は測位を停止します。
このとき初期化とフロアの設定は保持されるため、`begin` を再度呼び出せばいつでも再開できます。

<Tabs>
  <Tab title="iOS (Swift)">
    ```swift theme={null}
      await session.end() // 測位の終了
    ```
  </Tab>

  <Tab title="Android">
    **準備中です。**
  </Tab>
</Tabs>

## コード全体の例

<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 = "待機"
        private let locationManager = CLLocationManager()

        var body: some View {
            Text(status)
                .task {
                    do {
                        // ① 初期化（アプリ起動時に 1 回）
                        try await OneS1ght.initialize(sdkKey: "YOUR_SDK_API_KEY",
                                                      geoSdkKey: "YOUR_GEOSPACE_SDK_KEY")

                        // ② 権限 — 実際のアプリでは delegate で「許可」の応答を確認してから
                        //    測位を開始してください（初回起動時、ポップアップの応答前に開始すると失敗します）
                        locationManager.requestWhenInUseAuthorization()
                        guard await OneS1ght.permissions() == .authorized else {
                            status = "測位の権限が必要です"
                            return
                        }

                        // ③ プロフィールの連携（発行された値をアプリが保管して再利用）
                        let profileId = try await OneS1ght.createProfile(["ageBand": "20s"])
                        OneS1ght.identify(profileId: profileId)

                        // ④ 測位空間の設定 — これを行わないと座標が出ません
                        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)

                        // ⑤ 進入コールバックの登録
                        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("進入: \(zone.name)") }
                        session.onZoneExit  = { zone in print("退出: \(zone.name)") }

                        // ⑥ 測位の開始
                        try await session.begin()
                    } catch {
                        status = "失敗: \(error)"
                    }
                }
                .onDisappear {
                    // ⑦ 測位の終了
                    Task { @MainActor in
                        if let session = try? OneS1ght.floorSession() { await session.end() }
                    }
                }
        }
    }
    ```
  </Tab>

  <Tab title="Android">
    **準備中です。**
  </Tab>
</Tabs>
