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 20,000+ Swift developers and enthusiasts who get my weekly updates.