iOS で API を構造化する

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

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

import GoogleHomeSDK

エラー処理

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

HomeError を処理するときは、code フィールドと message フィールドで問題の内容を確認します。

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

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

Structure API

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

Structure は、ホームグラフ内のストラクチャを表します。id や name などの構造メタデータへのアクセスを提供します。

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 {
      // Failed to load structures
      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 = 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 = 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
}

Rooms

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

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 オブジェクトに反映されます。

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

デバイス グループ

デバイス グループは、単一の構造内のデバイスのユーザー定義コレクションを表します。

物理的な部屋と同様に、デバイス グループは 1 つのストラクチャに属します。ただし、ルームとは異なり、デバイス グループは論理グループです。デバイスは複数のデバイス グループに同時に属することができます。デバイス グループには最大 100 台のデバイスを含めることができます。

デバイス グループは Group エンティティで表されます。これは HomeDevice とは異なります。

デバイス グループのリストを取得する

ストラクチャ内のすべてのデバイス グループをモニタリングするには:

for await groups in structure.deviceGroups.batched() {
  for group in groups {
    print("Found group: \(group.name) with ID: \(group.id)")
  }
}

デバイス グループを作成する

構造体で GroupManagementTrait を使用して、デバイス グループを作成します。

let groupsManagementTrait = try await structure.traits(Google.GroupManagementTrait)
let _ = try await groupsManagementTrait.createUserDefinedGroup(withName: "Kitchen Lights",
         withDevices: [light1, light2])

グループ メンバーを管理する

既存のグループにメンバーを追加するには:

let groupManagementTrait = try await structure.traits(Google.GroupManagementTrait.self)
groupManagementTrait.addGroupMembers(
  groupObjectId: group.id,
  memberDeviceObjectIds = [light3.id],
)

グループからメンバーを削除するには:

let groupManagementTrait = try await structure.traits(Google.GroupManagementTrait.self)
_ = try await groupManagementTrait.removeGroupMembers(
            groupObjectId: group.id,
            memberDeviceObjectIdsArray: Array(removedIDs)

デバイス グループの名前を変更する

デバイス グループの名前を変更するには、GroupTrait の name フィールドを更新します。

let groupTrait = group.traits.get(Google.GroupTrait.self)
_ = try await groupTrait.update { $0.name = "Movie Corner" }

デバイス グループを削除する

ストラクチャからデバイス グループを削除するには:

let groupManagementTrait = try await structure.traits.get(Google.GroupManagementTrait.self)

_ = try await groupManagementTrait.deleteGroup(groupObjectId: group.id)