iOS의 Structure API

Structure API는 iOS용 Home API를 통해 액세스할 수 있습니다.

Structure API를 사용하려면 먼저 GoogleHomeSDK 패키지를 앱으로 가져옵니다.

import GoogleHomeSDK

오류 처리

Home API의 일부 메서드는 HomeError을 발생시키므로 이러한 호출에서 do-catch 블록을 사용하여 HomeError을 포착하는 것이 좋습니다.

HomeError를 처리할 때는 codemessage 필드를 확인하여 문제가 무엇인지 알아봅니다.

처리되지 않은 오류가 있으면 앱이 비정상 종료됩니다.

자세한 내용은 오류 처리를 참고하세요.

Structure API

Home는 홈 그래프를 나타내며 Structure API의 진입점입니다. 구조, 방, 기기에 대한 참조를 제공합니다.

Structure: Home 그래프의 구조를 나타냅니다. 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입니다.