iOS의 Structure 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는 홈 그래프의 구조를 나타냅니다. 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)

샘플 구조 호출

구조 집합 가져오기

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)

샘플 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 _ 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을 초과하면 잘리며 오류가 발생하지 않습니다. 개발자는 긴 이름을 처리해야 하며, 예를 들어 사용자에게 이름이 잘린다고 알릴지 여부를 결정할 수 있습니다.

기기 그룹

기기 그룹은 단일 구조 내에서 사용자가 정의한 기기 모음을 나타냅니다.

물리적 방과 마찬가지로 기기 그룹은 단일 구조에 속합니다. 하지만 기기 그룹은 방과 달리 논리적 그룹입니다. 기기는 동시에 여러 기기 그룹에 속할 수 있습니다. 기기 그룹에는 최대 100개의 기기가 포함될 수 있습니다.

기기 그룹은 HomeDevice와는 다른 Group 항목으로 표시됩니다.

기기 그룹 목록 가져오기

구조의 모든 기기 그룹을 관찰하려면 다음 단계를 따르세요.

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)