5

Several of the good blog posts detailing Watch Connectivity (http://www.kristinathai.com/watchos-2-tutorial-using-application-context-to-transfer-data-watch-connectivity-2/ and http://natashatherobot.com/watchconnectivity-application-context/) use simple app examples that send data to the watch when you tap on UI on the iPhone.

My app simply lists the data from my iPhone app, so I don't need to send data immediately, I just wanted to send it when the app loads or enters background...to this end I've made the updateApplicationContext in didFinishLaunching and didEnterBackground...however my dataSource delegate in my watch interface controllers are very spotting at getting triggered...particularly the glance only loads on the simulator and never on device. Is there a better time and place to push the info?

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    WatchSessionManager.sharedManager.startSession()      
    do {
         try WatchSessionManager.sharedManager.updateApplicationContext(["peopleDict" : peopleDict])                                        
    } catch {
        print(error)
    }
     return true
}

func applicationDidEnterBackground(application: UIApplication) {     
     do {
         try WatchSessionManager.sharedManager.updateApplicationContext(["peopleDict" : peopleDict])                                      
     } catch {
         print(error)
     }
}

below is my WatchSessionManager I used to call activiateSession in my extensionDelegate's appliciationDidFinishLaunching

import WatchConnectivity

protocol DataSourceChangedDelegate {
    func dataSourceDidUpdate(dataSource: DataSource)
}


class WatchSessionManager: NSObject, WCSessionDelegate {

    static let sharedManager = WatchSessionManager()
    private override init() {
        super.init()
    }

    private var dataSourceChangedDelegates = [DataSourceChangedDelegate]()

    private let session: WCSession = WCSession.defaultSession()

    func startSession() {
        session.delegate = self
        session.activateSession()
    }

    func addDataSourceChangedDelegate<T where T: DataSourceChangedDelegate, T: Equatable>(delegate: T) {
        dataSourceChangedDelegates.append(delegate)
    }

    func removeDataSourceChangedDelegate<T where T: DataSourceChangedDelegate, T: Equatable>(delegate: T) {
        for (index, indexDelegate) in dataSourceChangedDelegates.enumerate() {
            if let indexDelegate = indexDelegate as? T where indexDelegate == delegate {
                dataSourceChangedDelegates.removeAtIndex(index)
                break
            }
        }
    }
}

// MARK: Application Context
// use when your app needs only the latest information
// if the data was not sent, it will be replaced
extension WatchSessionManager {

    // Receiver
    func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {

        dispatch_async(dispatch_get_main_queue()) { [weak self] in
            self?.dataSourceChangedDelegates.forEach { $0.dataSourceDidUpdate(DataSource(data: applicationContext))}
        }

    }
}
GarySabo
  • 5,806
  • 5
  • 49
  • 124
  • https://stackoverflow.com/questions/73769169/how-to-make-auto-approve-from-iwatch-to-iphone can anyone help me to solve this problem – Azzo Hiro Sep 19 '22 at 06:29

1 Answers1

8

As updateApplicationContext only stores the newest application context you can update it whenever you like. The watch will only get the newest data. There is no queue with old contexts.

On the watch side the most secure location to activate the session and set the WCSessionDelegate is in the ExtensionDelegate init method:

class ExtensionDelegate: NSObject, WKExtensionDelegate {

    override init() {
        super.init()
        WatchSessionManager.sharedManager.startSession()
    }
    ...
}

Your Glance does not update because when the Glance is shown, applicationDidFinishLaunching is not being called (because the watch app is not launched when only the Glance is launched)

joern
  • 27,354
  • 7
  • 90
  • 105
  • Thank you, and maybe I'm misunderstanding the API then... I thought that updateApplicationContext would send the data to the watch, and when the session was activated on the watch, the dataSourceDelegate would observe the updated context and make it available for use? Said another way, how can I get the updated information to my glance? I do activateSession in the ExtensionDelegate. – GarySabo Oct 21 '15 at 21:27
  • No, you do not misunderstand the API that's how it works. Where exactly in your ExtensionDelegate do you activate the session? In `applicationDidFinishLaunching` or do you override `init` like I suggested in my answer? – joern Oct 21 '15 at 21:31
  • I added my WatchSessionManager class to my question – GarySabo Oct 21 '15 at 21:46
  • I update my answer so it calls your WatchSessionManager from the ExtensionDelegate's init method. Is this still not working? – joern Oct 21 '15 at 21:51
  • so take startSession out of applicationDidFinishLaunching and put it in the init only? – GarySabo Oct 21 '15 at 22:33
  • Yes please try that. And also make sure that you added your glance view controller to the SessionManager by calling `addDataSourceChangedDelegate` – joern Oct 21 '15 at 22:42
  • it works much better now, thanks again...I've been staring at this for a week. – GarySabo Oct 21 '15 at 23:47
  • did you manage to get data to the Glance as described above? (session setup in ExtensionDelegate init). In my app this works perfect on simulator but on device the Glance does not trigger extensionDelegate to initiate connection. – TPeter May 14 '16 at 03:43