7

I am writing a HomeKit app that successfully shows live data from my supported accessories in-app. I can read single values (HMCharacteristic.readValue) or use notifications to stay updated (HMCharacteristic.enableNotification).

Now I want to implement Widgets that show this data on the user's Home Screen. This consists of four steps:

  1. A dynamic Intent fetches all the registered (and supported) Accessories from the HMHomeManager and enables the user to select one of them to be shown on the Widget.
  2. Inside the IntentTimelineProvider's getTimeline function I can then again use the HMHomeManager to retrieve the Accessory I want to display on the Widget (based on the Accessory's UUID which is stored inside the getTimeline's configuration parameter - the Intent).
  3. Still inside the getTimeline function I can choose the Services and Characteristics I need for displaying the Accessory's Widget from the HMHomeManager.

Up until here everything works fine.

  1. However, when I try to read the values from the Characteristics I chose before using HMCharacteristic.readValue, the callback contains an error stating

Error Domain=HMErrorDomain Code=80 "Missing entitlement for API."

The Widget's Info.plist contains the 'Privacy - HomeKit Usage Description' field and the Target has the HomeKit capability.

After some research I came up with the following theory: Obviously the whole WidgetKit API runs my code in background. And it seems like HomeKit does not allow access from a background context. Well, it does allow access to Homes/Services/Characteristics, but it does not allow reading or writing on Characteristics (I guess to make sure App developers use HomeKit Automations and don't try to implement custom automations that are controlled by some background process of their app running on the iPhone).

My (simplified) getTimeline code:

func getTimeline(for configuration: SelectAccessoryIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        // id stores the uuid of the accessory that was chosen by the user using the dynamic Intent
        if let id = configuration.accessory?.identifier {
            // Step 2.: fetch the accessory
            // hm is a HMHomeManager
            let hm = HomeStore.shared.homeManager
            // take a short nap until the connection to the local HomeKit instance is established (otherwise hm.homes will create an empty array on first call)
            sleep(1)
            
            let accessories = hm.homes.flatMap({ h in h.accessories })
            
            if let a = accessories.filter({ a in a.uniqueIdentifier.uuidString == id }).first {
                // a holds our HMAccessory
                
                // Step 3.: select the characteristic I want
                // obviously the real code chooses a specific characteristic
                let s: HMService = a.services.first!
                let c: HMCharacteristic = s.characteristics.first!
                
                // Step 4.: read the characteristic's value
                c.readValue(completionHandler: {err in
                    if let error = err {
                        print(error)
                    } else {
                        print(c.value ?? "nil")
                    }
                    
                    // complete with timeline
                    completion(Timeline(entries: [RenderAccessoryEntry(date: Date(), configuration: configuration, value: c.value)], policy: .atEnd))
                })
            }
        }
    }
}

My questions:

First: Is my theory correct?

If so: What can I do? Are there any entitlements that allow me to access HomeKit in background or similar? Do I need to perform the readValue call elsewhere? Or is it just impossible to use the HomeKit API with WidgetKit with the current versions of HomeKit/WidgetKit/iOS and best I can do is hope they introduce this capability at some point in the future?

If not: What am I missing?

theMomax
  • 919
  • 7
  • 8
  • It looks like that the entitlement for HomeKit is not eligible to fetch in the background see: https://developer.apple.com/forums/thread/22279?answerId=74731022#74731022 – Christian Anchor Dampf Jan 22 '21 at 11:28
  • Sad to hear, but thanks for sharing. I guess best we can do ist wait... – theMomax Jan 22 '21 at 18:20
  • Hey @theMomax, did you ever find any workaround for this issue? I've made the exact same observations, though I've seen apps on the store able to fetch up to date data… – AnthoPak Apr 25 '23 at 11:45

0 Answers0