iOS で API を構造化する

Structure API には、iOS 向けの Home API からアクセスできます。

Structure API を使用するには、まず GoogleHomeSDK パッケージをアプリにインポートします。

import GoogleHomeSDK

エラー処理

Home API の一部の関数は HomeError をスローするため、do-catch ブロックを使用して、これらの呼び出しで HomeError をキャッチすることをおすすめします。

HomeError を処理する際は、code フィールドと message フィールドを確認して、何が問題だったかを確認します。

処理されないエラーがあると、アプリがクラッシュします。

詳細については、エラー処理をご覧ください。

Structure API

Home はホームグラフを表し、Structure API のエントリ ポイントです。ストラクチャ、部屋、デバイスへの参照を提供します。

Structure は、ホームグラフ内の構造を表します。idname などの構造メタデータにアクセスできます。

structures() を使用して、アカウント内のすべての構造体を取得します。構造は Query の形式で返されます。これにより、データを使用するためのさまざまな方法を選択できます。

API 説明
stream() 変更が発生するたびに各オブジェクトを個別に出力する Publisher を返します。
batched() 現在の結果をオブジェクトの Set として出力する Publisher を返します。出力される各 Set は、オブジェクトグラフの現在の状態を表します。
list() 現在の結果をオブジェクトの Set として返します。

structures().list() 呼び出しで有効な構造のセットがすぐに返されない場合があります。アプリがリアクティブで、stream() を呼び出してすべての構造変更をサブスクライブし、UI を駆動する場合、最終的には有効な構造リストが返されます。空の構造リストが返される状況は他にもあります。たとえば、ユーザーのスマートフォンが接続を失った場合や、ユーザーがアプリの権限を取り消した場合などです。このようなケースはアプリで必ず処理する必要があります。

@Published public private(set) var structures: [Structure] = []
private var structuresCancellable: AnyCancellable?

  self.structuresCancellable = home
    .structures()
    .batched()
    .receive(on: DispatchQueue.main)
    .map { Array($0) }
    .catch {
      Logger.error("Failed to load structures: \($0)")
      return Just([Structure]())
    }
    .assign(to: \.structures, on: self)

サンプルの構造呼び出し

構造体のセットを取得する

Query<Structure>list() を呼び出すと、最新の要素セットが返されます。

// Get a stream of all structures accessible to the user
let allStructuresChanges = try await self.home.structures()
let allStructures = try? await allStructuresChanges.list()

リアクティブ アプリを設計する場合は、list() ではなく batched()stream() の呼び出しを使用します。これらの呼び出しは、ホームグラフが変更されたときにデータを自動的に生成します。

構造のプロパティを取得する

構造物のリストを取得したら、そのプロパティにアクセスできます。

// Get a stream of changes taking place on a structure.
let structureChanges = try await home.structures().list().filter { $0.id == structureID }

// Get a snapshot of the structure.
let structure = try await structureChanges.first!

// Get structure properties
print("id \(structure.id) ")
print("name \(structure.name) ")

名前で構造を検索する

構造の名前がわかっている場合は、name プロパティを使用してアクセスすることもできます。

do {
  structure1 = try await home.structures().list().first(where: { $0.name == "Main House" })
} catch let error as HomeError {
  // Code for handling the exception
}

そこから、各ストラクチャのプロパティ、部屋、デバイスにアクセスできます。

複数の構造を扱う

複数の構造を使用するには、各構造への個別の参照を取得します。

var structure1: Structure!
var structure2: Structure!
do {
  structure1 = try await home.structures().list().first(where: { $0.name == "Main House" })
} catch let error as HomeError {
  // Code for handling the exception
}
do {
  structure2 = try await home.structures().list().first(where: { $0.name == "Guest Cottage" })
} catch let error as HomeError {
  // Code for handling the exception
}

Rooms

部屋にはデバイスのグループが含まれます。部屋は常にストラクチャの一部であり、ストラクチャには複数の部屋を含めることができます。ストラクチャから部屋を削除しても、その部屋のデバイスはストラクチャから削除されません。ただし、部屋を削除すると、その部屋のデバイスは割り当て解除されます。

Home.rooms() を使用してアカウント内のすべての部屋を取得し、roomID = device.roomID を使用して各部屋に対応するデバイスを表示します。

self.home.rooms().batched()
  .combineLatest(self.home.devices().batched())
  .receive(on: DispatchQueue.main)
  .catch { error in
    Logger.error("Failed to load rooms and devices: \(error)")
    return Just((Set<Room>(), Set<HomeDevice>()))
  }
  .map { rooms, devices in
    var devicesByRoom = [Room: [HomeDevice]]()
    for room in rooms where room.structureID == currentStructureID {
      devicesByRoom[room] = devices.filter { $0.roomID == room.id }
    }
    return devicesByRoom
  }.assign(to: &self.$devicesByRoom)

Room 呼び出しのサンプル

部屋のリストを取得する

Home クラスを使用すると、部屋のリストを取得して、部屋のプロパティにアクセスできます。

let allRoomsChanges = self.home.rooms()
let allRooms = try await allRoomsChanges.list()
let room = allRooms.first!
XCTAssertTrue(allRooms.contains(room))

print("id \(room.id) ")
print("name \(room.name) ")

チャットルームを作成する

Structure に新しい部屋を作成するには:

let testName = "Test Room Name"
var newRoom: Room!
do {
  newRoom = try await structure.createRoom(name: testName)
  XCTAssertNotNil(newRoom)
} catch let error as HomeError {
  // Code for handling the exception
}

部屋を削除する

または、部屋を削除することもできます。

val roomToDelete = structure.rooms().list().filter { it.name == "room_id1" }.firstOrNull()
    structure.deleteRoom(roomToDelete!!)

部屋の ID を使用して削除することもできます。

let roomToDelete = allRooms.first(where: { $0.id == room.id })
if let roomToDelete1 = roomToDelete {
  do {
    try await structure.deleteRoom(roomToDelete1)
  } catch let error as HomeError {
    // Code for handling the exception
  }
}

デバイスのある部屋を削除しても、デバイスは構造内に残りますが、部屋には割り当てられなくなります。

デバイスを別の部屋に移動する

Structure では、デバイスを別の部屋に移動することもできます。

do {
  try await structure.move(device: light, to: room)
} catch let error as HomeError {
  // Code for handling the exception
}

API リスト

Home のインスタンスが作成されると、次の Structure API にアクセスできるようになります。

API 説明
devices() このアカウントに表示されるすべてのデバイスを取得します。
device(id:) 指定したデバイスの Publisher を取得し、現在の状態を出力し、今後の状態更新時に再度出力します。
structures() Google アカウントのすべての構造を取得します。詳細な取得とフィルタリング オプションを提供する Query<Structure> を返します。
structure(id:) 一致する ID の構造を取得します。
rooms() Google アカウントのすべての部屋を取得します。詳細な取得とフィルタリング オプションを提供する Query<strRoom> を返します。
room(id:) 指定した部屋の Publisher を取得して、現在の状態を出力し、今後の状態更新時に再度出力します。

Structure には次の API があります。

API 説明
deleteRoom(id:) 部屋 ID を指定して部屋を削除します。
id 構造物の一意のシステム ID。
move(device:, to:) デバイスをストラクチャ内の別の部屋に移動する。
move(device:, to:) 指定された ID のデバイスを、指定された ID の部屋に移動します。
move(devices:, to:) 指定したデバイスを指定された部屋に移動します。
move(devices:, to:) 指定された ID のデバイスを、指定された ID の部屋に移動します。
name 構造のユーザー指定名。

Room には次の API があります。

API 説明
id 客室の一意のシステム ID。
name ユーザーが指定した部屋の名前。
structureID 客室が属する構造物の一意のシステム ID。