handleWatchKitExtensionRequest not responding to openParentApplication in Watchkit Extension (Swift)

Joe Devine picture Joe Devine · Jan 23, 2015 · Viewed 7.9k times · Source

I am trying to send information from my WatchKit App over to my main parent application and from what I understand I should just be able to use openParentApplication in my watchkit Extension which will be received by handleWatchKitExtensionRequest in AppDelegate.swift, but I cant seem to get handleWatchKitExtensionRequest to be triggered.

I've been having some issues, so at this point I'm just trying to establish any connection at all before worrying about what information is actually passed. so currently in my Watchkit ViewController I have the following:

 let testDict = [
    "value1" : "Test 1",
    "value2" : "Test 2"
]

@IBAction func saveButtonFunction() {
    openParentAppForBalance(testDict)
}

private func openParentAppForInfo(Dict: [String: String]) {

    WKInterfaceController.openParentApplication(testDict,
        reply: {(reply, error) -> Void in
            println("openParentApplication called in button function")
    })
}

which shows in the output that the function is being called, but handleWatchKitExtensionRequest just wont respond. Currently it's set to the following in AppDelegate.swift which never gets called:

func application(application: UIApplication!, handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!, reply: (([NSObject : AnyObject]!) -> Void)!) {

    println("we made it!")

    var retValues = Dictionary<String,String>()

    retValues["retval1"] = "return Test 1"
    retValues["retval1"] = "return Test 2"

    reply(retValues)

}

I'm sure I'm probably just missing something really fundamental here in my understanding of how this all works, but any help at all about how to get handleWatchKitExtensionRequest to be triggered would be hugely appreciated!

Answer

Duncan Babbage picture Duncan Babbage · Jan 23, 2015

Ah, I think what is happening here is that your code is both correct, and working exactly as it should, and what you're interpreting here is a result of an overlap of two entirely understandable assumptions, that are actually not correct and have been leading you astray. So the good news is, your code is already working.

You say,

...which shows in the output that the function is being called...

If by that you mean that in the console you are seeing the message, openParentApplication called in button function, then here's what is going on:

This part of your code is a Swift Closure:

{(reply, error) -> Void in
        println("openParentApplication called in button function")
}

When your WatchKit Extension calls WKInterfaceController.openParentApplication it passes to your parent iPhone app a dictionary (your testDict), which the iPhone application can use to return data to you—providing the data has been serialized. It also returns back to you the closure that you passed it. This enables your WatchKit Extension to run code that it has itself defined, at the later point when the reply has been received. You can include in this closure use of both the returned data in the testDict and also other variables that were locally accessible at the time openParentApplication was called. Your WatchKit Extension automatically executes the code in the closure when it is received back.

So when you see openParentApplication called in button function, this indicates that the reply from the iPhone application has been received, and the closure has been executed. Therefore, your WatchKit test code should really change the println statement to be:

WKInterfaceController.openParentApplication(testDict,
    reply: {(reply, error) -> Void in
        println("Reply to openParentApplication received from iPhone app")
    })

Now, the reason why you quite understandably didn't realise the code was executing correctly was because you were expecting to see rejected in the console that this code had been executed in your iPhone app:

println("we made it!")

However, Xcode does not support attaching to two processes simultaneously. Therefore, when you are attached to your WatchKit app, you will not see any log messages of your iPhone app. Your iPhone app also will not respond to breakpoints when it is not the attached process. Both of these are true whether it is running in the background (woken by openParentApplication) or running in the foreground (if you launch it manually in the simulator after the WatchKit app has been run. You can see the effects of the iPhone app activity, but cannot directly introspect it while you are attached to the WatchKit app.

So firstly, your code is working correctly. You can move past your test code! And in relation to introspecting the workings in the iPhone side when it is responding to your WatchKit app, there is a partial solution. Launch the WatchKit app from the simulator, and once it is running, in Xcode activate the menu option Debug > Attach to process... and select your iPhone app process under Likely targets at the top. Now you will see your iPhone app console messages and your iPhone app will respond to breakpoints—but of course you will no longer see these from the WatchKit app side. You continue to be able to interact with both apps in the simulators, and can swap back and forth between which one you are attached to during execution.