iOS で API を構造化する

Structure API には、iOS 向け Home API を介してアクセスできます。

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

import GoogleHomeSDK

エラー処理

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

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

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

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

Structure API

Home は Home Graph を表し、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)

Sample Structure 呼び出し

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

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
}

部屋の名前を変更する

setName(_:) メソッドを呼び出して、部屋の名前を変更します。

let updatedRoom = try await theRoom.setName("new room name")

ルームの名前を変更すると、元の Room 構造体はそのまま残り、変更は返される更新された Room オブジェクトに反映されます。

名前が 60 Unicode コードポイント(文字)の上限を超えている場合、名前は切り捨てられます。エラーは発生しません。長い名前の処理はデベロッパーの責任で行われます。たとえば、名前が切り捨てられることをユーザーに通知するかどうかをデベロッパーが決定できます。

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。