WatchKit: A Quick Reply

In XCode 6.2 Beta 2, Apple has introduced the ability to open your iOS application from your Watch App. At the moment when the iOS Application gets the WatchKit extension request, it can also send back a reply to the Watch App. This is the part of WatchKit I’ve been playing with for the past day.

I created a simple Marco – Polo application in which clicking on the Marco button on my Watch App changes the color of my iOS app, and when I click on the Polo button in my iOS app, it changes the color of the Watch App.

The Setup

To understand how to open an iOS app from the Watch App, make sure to go through my example in the WatchKit: Open Your iOS App From The Watch post.

Add a Reply Block

When the user taps on the “Marco” button in the Watch App, the new openParentApplication:reply: class method gets called on WKInterfaceController with a reply block which will change the background color of the Watch App’s Group when it’s called:

//
//  InterfaceController.swift
//  WatchKitOpenAppDemo WatchKit Extension

// a WKInterfaceGroup is similar to UIView
@IBOutlet weak var colorGroup: WKInterfaceGroup!

@IBAction func onMarcoButtonTap() {
    
    // randomly generated color components
    // to be sent to the iOS app for it's background color 
    let randomColorComponents = [
        "red" : CGFloat(arc4random() % 255),
        "green" : CGFloat(arc4random() % 255),
        "blue" : CGFloat(arc4random() % 255)]
    
    WKInterfaceController.openParentApplication(randomColorComponents,
        reply: { [unowned self](reply, error) -> Void in
            
            // reply dictionary from the iOS app
            if let reply = reply as? [String : CGFloat] {

                switch (reply["red"], reply["green"], reply["blue"]) {
                case let (.Some(red), .Some(green), .Some(blue)):
                    
                    // generate color from the
                    // color components received from 
                    // the iOS app
                    let color = UIColor(
                        red: red/255.0,
                        green: green/255.0,
                        blue: blue/255.0,
                        alpha: 1.0)
                    
                    self.colorGroup.setBackgroundColor(color)
                default:
                    self.colorGroup.setBackgroundColor(UIColor.blackColor())
                }
            } else {
                self.colorGroup.setBackgroundColor(UIColor.blackColor())
            }
    })
}

Save the Reply Block

The iOS app will receive the reply block in the new AppDelegate’s application:handleWatchKitExtensionRequest: method along with the userInfo. Since I’m using notifications to pass the information received from the Watch App to parts of my iOS app that need that information, I created an object to store this information:

//
//  ColorInfo.swift
//  WatchKitOpenAppDemo

import UIKit

class ColorInfo: NSObject {
    
    let color: UIColor
    let replyBlock: ([NSObject : AnyObject]!) -> Void
    
    init(userInfo: [NSObject : AnyObject], reply: ([NSObject : AnyObject]!) -> Void) {
        if let userInfo = userInfo as? [String : CGFloat] {
            switch (userInfo["red"], userInfo["green"], userInfo["blue"]) {
            case let (.Some(red), .Some(green), .Some(blue)):
                color = UIColor(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: 1.0)
            default:
                color = UIColor.blackColor()
            }
        } else {
            color = UIColor.blackColor()
        }
        
        replyBlock = reply
    }
}

So now the NotificationCenter can send over the newly generated colorInfo object:

//  AppDelegate.swift
//  WatchKitOpenAppDemo

func application(application: UIApplication!, handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!, reply: (([NSObject : AnyObject]!) -> Void)!) {
    
    let colorInfo = ColorInfo(userInfo: userInfo, reply: reply)
    
    NSNotificationCenter.defaultCenter().postNotificationName("WatchKitSaysHello", 
        object: colorInfo)
}

When the ViewController receives the notification, it will keep track of the colorInfo object:

//
//  ViewController.swift
//  WatchKitOpenAppDemo

import UIKit

class ViewController: UIViewController, UIAlertViewDelegate {
    
    var colorInfo: ColorInfo?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("handleWatchKitNotification:"),
            name: "WatchKitSaysHello",
            object: nil)
    }
    
    deinit {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
    func handleWatchKitNotification(notification: NSNotification) {
        
        if let colorInfo = notification.object as? ColorInfo {
            
            view.backgroundColor = colorInfo.color
            
            self.colorInfo = colorInfo
        }
    }
}

Execute the Reply Block

So now, when the user taps the “Polo” button in the iOS app, the reply block (which changes the color of the Watch App) will be executed:

//  ViewController.swift
//  WatchKitOpenAppDemo

@IBAction func onPoloButtonTap(sender: AnyObject) {
    if let colorInfo = colorInfo {
        let randomColorComponents = [
            "red" : CGFloat(arc4random() % 255),
            "green" : CGFloat(arc4random() % 255),
            "blue" : CGFloat(arc4random() % 255)]
        
        
        colorInfo.replyBlock(randomColorComponents)
    }
}

Limitations

This quick two-way communication between the Watch App and iOS App is really cool, but I’m really not sure how well it will work in real life. I heard that extensions are very short-lived, so it’s very likely that the reply block will not be accessible very soon after the iOS app gets it. So make sure to keep things optional and double check that they exist! Unfortunately, WatchKit is not optimized for Swift optionals yet.

That says, I really like this quick and simple WatchKit / iOS app communication aspect of WatchKit, since it definitely brings a lot more exciting opportunities for WatchKit app concepts.

You can view the source code on Github here.

Enjoy the article? Join over 14,500+ Swift developers and enthusiasts who get my weekly updates.

  • Mrugesh Tank

    This is amazing……
    i am trying to find gestures in image in WatchKit but got failure.
    If you put some tutorial on that, that will very thankful…

  • Alex Shaykevich

    Check out MMWormhole. The idea is to use group user defaults and Darwin notification center to communicate more directly between the extension and the app. The worry is, though, that WatchKit will likely introduce this feature more directly sometime soon.

  • Andy

    Is there any way to get data from iOS app? So I have a pie chart generation logic in my iOS app which can even save the chart as an image. I need to show this graph in the glance of the watch app. How can I possibly achieve that?

  • 市川憲一

    please objective-c source..,..

  • John Read

    Wonderful information shared… I have got some important point from your post…
    iphone application development company US