iOS で API を構造化する

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

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

import GoogleHomeSDK

エラー処理

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

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

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

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

Structure API

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

Structure は、Home グラフ内のストラクチャを表します。idname などのストラクチャ メタデータへのアクセスを提供します。

structures() を使用して、アカウント内のすべてのストラクチャを取得します。ストラクチャは Queryの形式で返されます。この形式では、データを消費する方法を選択できます。

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

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

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

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

ストラクチャ呼び出しのサンプル

ストラクチャのセットを取得する

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

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

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

ストラクチャのプロパティを取得する

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

// 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 = 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 _ 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 _ as HomeError {
  // Code for handling the exception
}
do {
  structure2 = try await home.structures().list().first(where: { $0.name == "Guest Cottage" })
} catch let _ as HomeError {
  // Code for handling the exception
}

部屋

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

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

self.home.rooms().batched()
  .combineLatest(self.home.devices().batched())
  .receive(on: DispatchQueue.main)
  .catch { error in
    // Failed to load rooms and devices
    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)

部屋の呼び出しのサンプル

部屋のリストを取得する

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 _ 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 _ as HomeError {
    // Code for handling the exception
  }
}

デバイスがある部屋が削除されても、デバイスはストラクチャに残りますが、部屋に割り当てられなくなります。

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

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

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

部屋の名前を変更する

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

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

部屋の名前を変更しても、元の Room 構造体は変更されず、返される更新された Room オブジェクトに反映されます。

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