pradeepmouli/homeatlas
HomeAtlas delivers compile-time safe wrappers over Apple HomeKit services and characteristics. Metadata for every service originates from Developer Apple Context7 (`developer_apple`, HomeKit topic), ensuring generated Swift APIs remain aligned with the platform source of truth.
Highlights
- Strongly typed descriptors for accessories, services, and characteristics (no
Anyleakage). @MainActorasync helpers that bridge HomeKit callbacks to structured Swift concurrency.- Schema-driven SwiftPM command plugin (
generate-homeatlas) to regenerate sources whenever the HomeKit catalog evolves. - Deterministic error surface that captures accessory, service, and characteristic context for diagnostics.
- Built-in snapshot export for diagnostics and monitoring with deterministic JSON serialization.
Requirements
- Xcode 16 beta or newer with the Swift 6.0 toolchain.
- Apple platforms with HomeKit support (iOS 16+, macOS 13+, tvOS 16+, watchOS 9+).
Installation
Add HomeAtlas to your package dependencies:
.package(url: "https://github.com/pradeepmouli/HomeAtlas.git", from: "0.1.0")Then add the library to your target:
.target(name: "MyApp", dependencies: [
.product(name: "HomeAtlas", package: "HomeAtlas")
])Release & Publishing
This package is prepared for the Swift Package Index (SPI):
- Manifest includes library and executable products, platforms, and targets
- MIT license and README are present
- Semantic versions are tagged on Git for releases (e.g.,
0.1.0)
To publish a new release:
- Update CHANGELOG.md
- Create a new tag:
git tag -a v0.1.0 -m "Initial release" && git push origin v0.1.0 - Verify SPI ingestion and documentation build
Compile-Time Accessory Control
import HomeAtlas
enum ToggleError: Error {
case accessoryNotFound
case characteristicUnavailable
}
@MainActor
func toggleLightbulb(named accessoryName: String, manager: HomeKitManager) async throws {
await manager.waitUntilReady()
guard
let accessory = manager.accessory(named: accessoryName),
let lightbulb = accessory.service(of: LightbulbService.self)
else {
throw ToggleError.accessoryNotFound
}
guard let power = lightbulb.powerState else {
throw ToggleError.characteristicUnavailable
}
try await power.write(true)
}The example mirrors the Developer Apple Context7 HomeKit Lightbulb service documentation. LightbulbService exposes a PowerStateCharacteristic wrapper (Characteristic<Bool>) so attempts to write a non-boolean value fail at compile time rather than at runtime.
Serialization for Diagnostics
HomeAtlas wrappers do not conform to Encodable because they wrap non-Encodable HomeKit framework types (HMAccessory, HMService, etc.). For diagnostics or logging that require serialization, use the DTO (Data Transfer Object) pattern to extract encodable snapshots:
struct AccessorySnapshot: Codable {
let name: String
let uniqueIdentifier: UUID
let isReachable: Bool
let categoryType: String
init(from accessory: Accessory) {
self.name = accessory.name
self.uniqueIdentifier = accessory.uniqueIdentifier
self.isReachable = accessory.isReachable
self.categoryType = accessory.category.categoryType
}
}
// Usage:
let snapshot = AccessorySnapshot(from: myAccessory)
let jsonData = try JSONEncoder().encode(snapshot)See docs/encodable-exclusion-rationale.md for detailed rationale and DTO examples.
Deterministic Error Insights
HomeAtlas converts every fallible operation into a HomeKitError, enriching the error with the accessory, service, and characteristic metadata documented by Developer Apple.
do {
try await power.write(true)
} catch let error as HomeKitError {
switch error {
case .characteristicTransport(_, let context, _):
print("Write failed for", context.characteristicType, "on", context.serviceType ?? "<unknown>")
print("Metadata:", error.diagnosticsMetadata)
default:
throw error
}
}The shared DiagnosticsLogger already emits structured telemetry whenever a HomeKit operation fails or exceeds the configurable latency budget (default 500 ms). Register an observer to integrate with your logging stack:
let token = DiagnosticsLogger.shared.addObserver { event in
print("HomeKit", event.operation.rawValue, event.outcome, event.metadata)
}Pair the metadata with the relevant Developer Apple Context7 topic (e.g., accessory communication or latency guidance) to provide actionable insights to end users.
Context Entities & Organization
HomeAtlas provides strongly-typed wrappers for HomeKit context entities that organize accessories:
import HomeAtlas
@MainActor
func listHomeRooms(manager: HomeKitManager) async {
await manager.waitUntilReady()
guard let primaryHome = manager.homes.first else {
print("No homes configured")
return
}
// Wrap HMHome for type-safe access
let home = Home(primaryHome)
print("Home: \(home.name)")
print("Rooms:")
for hmRoom in home.rooms {
let room = Room(hmRoom)
print(" - \(room.name) (\(room.accessories.count) accessories)")
}
print("\nZones:")
for hmZone in home.zones {
let zone = Zone(hmZone)
print(" - \(zone.name) (\(zone.rooms.count) rooms)")
}
}These wrappers conform to the Developer Apple HMHome, HMRoom, and HMZone APIs while providing @MainActor safety and diagnostic logging.
Cache Lifecycle Management
For performance optimization, HomeAtlas exposes cache warm-up and reset APIs:
@MainActor
func optimizeAccessoryAccess(manager: HomeKitManager) async {
// Warm up caches for all services and characteristics
await manager.warmUpCache(includeServices: true, includeCharacteristics: true)
// Later, if accessories are modified externally:
await manager.resetCache(includeCharacteristics: true)
}Cache operations emit diagnostics events for monitoring performance:
let token = DiagnosticsLogger.shared.addObserver { event in
if event.operation == .cacheWarmUp || event.operation == .cacheReset {
print("Cache operation:", event.metadata)
}
}See Developer Apple - Interacting with a home automation network for best practices on accessory lifecycle management.
Generating Sources
HomeAtlas uses a two-stage pipeline to keep service definitions synchronized with Apple's SDK:
1. Extract Catalog from iOS SDK
swift run HomeKitCatalogExtractor \
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk \
--output Resources/homekit-services.yamlThis parses HomeKit framework headers and validates exported symbols against HomeKit.tbd, producing a normalized YAML catalog.
2. Generate Swift Wrappers
swift run HomeKitServiceGenerator \
Resources/homekit-services.yaml \
--output Sources/HomeAtlas/GeneratedOr use the SwiftPM plugin for integrated builds:
swift package plugin generate-homeatlasThe plugin/generator emits typed Swift wrappers into:
Sources/HomeAtlas/Generated/Servicesfor service classes such asLightbulbService.Sources/HomeAtlas/Generated/Characteristicsfor characteristic wrappers such asPowerStateCharacteristic.
Every generated file includes @MainActor annotations and doc comments referencing the corresponding Developer Apple HomeKit documentation so the code stays aligned with the platform source of truth.
For detailed workflow, see Service Extension Guide.
Testing
swift testSmoke tests validate the schema pipeline and ensure integration logic compiles on platforms without HomeKit support by exercising fallback paths.
Documentation
- Service Extension Guide - SDK extraction and code generation workflow
- Troubleshooting Guide - Common errors and solutions
- Developer Apple Reference Index - Complete API reference citations
- Quickstart Guide - Step-by-step setup
License
HomeAtlas is available under the MIT license. See LICENSE for details.
Package Metadata
Repository: pradeepmouli/homeatlas
Default branch: master
README: README.md