HomeKit Singleton Thoughts & Code

Apple’s HomeKit API is pretty cool. For one, HomeKit comes with its own data model and store, one that likely will be shared across a person’s devices via iCloud. That’s very cool.

But if you’ve started playing with HomeKit’s HMHomeManager, you might have noticed that there are cases where a singleton containing, or of, the HMHomeManager would be a good idea. That’s because changes that might occur to the Home Manager in one part of a HomeKit app may not be reflected in another part, or on another device. Such as…”Where’s the room and 10 devices I just added to my home?!?!” Oops! Customers hate that.

So you need a singleton. The next issue is a singleton of what? One route is to create a delegate to act as a singleton. This delegate could contain an array of HMHomeManager type instances, some to serve as “scratch pads” and others as set as the Home Manager. Another route is to create an instance of HMHomeManager in the AppDelegate since (hopefully) you only have one of those running around in an app. And then there’s just creating a custom class to represent HMHomeManager. It is this last option on which this post focuses.

Apple includes a nice little HomeKit sample app, HMCatalog. One of the nice things included in that sample code is a HMHomeManager singleton. Since it’s in Obj-C, I took the liberty of writing it in Swift. Here’s the code:


let HomeStoreDidChangeSharedHomeNotification: String    = "HomeStoreDidChangeSharedHomeNotification"
let HomeStoreDidUpdateHomeNotification: String          = "HomeStoreDidUpdateHomeNotification"




class HomeStore: NSObject, HMHomeManagerDelegate
{
    static let sharedInstance = HomeStore()
    
    var homeManager: HMHomeManager
    var homeQueue: dispatch_queue_t
 
    var home: HMHome{
        get
        {
            var oldHome: HMHome     = self.home
            var newHome: HMHome?
            
            if let aHome = self.homeMatchingName(oldHome.name)
            {
                newHome     = aHome
                self.home   = newHome!
            }
            
            if (oldHome === self.home) && (newHome != nil)
            {
                self.alertForHomeDidChange()
            }
            return self.home
        }
        set(newHome)
        {
            if newHome == self.home
            {
                return
            }
            
            dispatch_async(self.homeQueue, {
                self.home   = newHome
                self.alertForHomeDidChange()
            })
        }
    }
    
    
    
    class func sharedStore()-> HomeStore
    {
        return sharedInstance
    }
    
    
    override init()
    {
        self.homeManager            = HMHomeManager()
        self.homeQueue              = dispatch_queue_create("com.portablefrontier.PFHouseWorks.HomeQueue", DISPATCH_QUEUE_SERIAL)
    
        super.init()
    
        self.homeManager.delegate   = self
    }
    
    
    /*
    func home() -> HMHome
    {
        var oldHome: HMHome     = self.home
        var newHome: HMHome?
        
        if let aHome = self.homeMatchingName(name: oldHome.name)
        {
            newHome     = aHome
            self.home   = newHome!
        }
        
        if (oldHome === self.home) && (newHome != nil)
        {
            self.alertForHomeDidChange()
        }
        return self.home
    }
    */
    
    
    /*
    func setHome(newHome: HMHome)
    {
        if newHome == self.home
        {
            return
        }
        
        dispatch_async(self.homeQueue, {
            self.home   = newHome
            self.alertForHomeDidChange()
        })
    }
    */


    func homeMatchingName(name: String) -> HMHome?
    {
        for aHome in homeManager.homes
        {
            if name == home.name
            {
                return home
            }
        }
        return nil
    }


    
    //: NotificationCenter Functions
    func alertForHomeDidChange()
    {
        dispatch_async(dispatch_get_main_queue(), {
            NSNotificationCenter.defaultCenter().postNotificationName(HomeStoreDidChangeSharedHomeNotification, object: self)
        })
    }
    
    
    
    func homeManagerDidUpdateHomes(manager: HMHomeManager)
    {
        NSNotificationCenter.defaultCenter().postNotificationName(HomeStoreDidUpdateHomeNotification, object: self)
        
        if let aHome = self.homeMatchingName(home.name)
        {
            self.home    = aHome
        }
    }
}