Apple Watch 2.0 With TDD Setup

October 29, 2015 Priyanka Ranjan

sfeatured-applewatchIf you haven’t heard by now, we can finally create native apps for Apple Watch! As great as that is, it now means there are some changes as to how we communicate between devices for Watch OS 2. Assuming you have a basic understanding of the WatchKit architecture, this post will cover the new types of communication methods, my experiences with Watch OS 2, and how to get started with TDD for Watch.

The image below is referenced from Apples Watch OS 2 developer documentation and shows the change in architecture from Watch OS 1. The primary difference is that the WatchKit extension now runs on the Apple Watch which makes it easier for your app to access resources from the watch extension.

image02

SETUP:

To start off, the first thing you would need to do is setup both apps to receive data. This should preferably be done at launch of each application.


if ([WCSession isSupported]){
      WCSession *session = [WCSession defaultSession];
      session.delegate = self;
      [session activateSession];
}

COMMUNICATION:

There is a new framework called Watch Connectivity that offers multiple ways of communicating between devices, which replace the one (openParentApplication:), that was provided by Watch OS 1. The different ways fall under two categories:

Background transfers Interactive messaging
Best when information isn’t needed immediately Best for information needed immediately
Operating system determines the most suitable time to send the data Requires reachable state
Content is queued up for transfer

Background Transfers:

1) Application Context: The most beneficial thing about using updateApplicationContext is that information gets overridden by the latest data. This means that only the most up-to-date information is passed over at an opportune time determined by the OS.

NSDictionary *applicationDict = // Create a dict of application data
[[WCSession defaultSession] updateApplicationContext:applicationDict error:nil];

To receive the file on the other side, use the delegate callback for this called
session:didReceiveApplicationContext: to grab the application context that you just sent through.

2) User Info Transfer: User info transfer seems somewhat similar to openParentApplication of WatchOS 1. Unlike application context, all content is queued up for delivery in FIFO order, so nothing will be overridden. This is useful for cases where all data is needed by the receiving device.

NSDictionary *applicationDict = // Create a dict of application data
[[WCSession defaultSession] transferUserInfo:applicationDict];

On the opposite end, use session:didReceiveUserInfo: to receive the application dictionary you just sent.

3) File Transfer: Use this when you want to transfer files between your devices.

NSURL *url = // URL of file
NSDictionary *metadataDict = // Create dictionary of data
WCSessionFileTransfer *fileTransfer = [[WCSession defaultSession] 
transferFile:url metadata:metadataDict];

You can check on the queued file transfers waiting to be delivered by accessing the outstandingFileTransfers property on WCSession. To receive the file on the other side, use session:didReceiveFile:.

Interactive Messaging:

This category requires the devices to be reachable. However, it differs based on where you are communicating from.

  • iOS app: The paired Apple Watch must be connected via Bluetooth and the watch app must be running in the foreground.
  • Watch app: The paired iPhone has to be connected via Bluetooth but does not need to be running in foreground.

You can check for reachability by using the isReachable property on your session. Also, you can be notified of a change in reachability by using the WCSessionDelegate method:

- (void)sessionReachabilityDidChange:(WCSession *)session;

Once you have established a connection that is reachable, you can begin communicating with your application.

NSDictionary *applicationDict = // Create a dict of application data
[[WCSession defaultSession] sendMessage:applicationDict
replyHandler:^(NSDictionary *replyHandler) { }
errorHandler:^(NSError *error) { }
];

NSData *applicationData = // Create data
[[WCSession defaultSession] sendMessageData:applicationData
replyHandler:^(NSDictionary *replyHandler) { }
errorHandler:^(NSError *error) { }
];

For the callbacks on these functions, check out session:didReceiveMessage: and session:didReceiveMessage:replyHandler:

Setting Up Testing Environment For Watch OS 2

This is an overview on how to setup a testing environment for Watch OS 2. For a helpful blog on how to write tests for WatchKit, check out Announcing new WatchKit testing tools in Pivotal Core Kit.

To start off, if your project doesn’t already use Cedar, follow the Cedar installation instructions to install Cedar in your host project.

As you would have noticed, Xcode 7 does not provide a testing target for your watch app. The following steps will demonstrate how to create an iOS test target and use it to run tests for your watch app

  • Create a testing target, making sure the host application corresponding to this target is “None” :
      • Go to: General:Testing
      • Set: HostApplication:None

    image01

  • Add your Watch App target (not the Watch Extension target) as a Target Dependency for this newly created testing target.
  • Include all the files you want to test in both targets.

Using Cocoa Pods, you can include PivotalCoreKit in your test target by including the following lines in your Podfile.


target Watch-test-target
pod 'PivotalCoreKit/WatchKit/WatchKit'
end

PivotalCoreKit is a great helper library for iOS projects. Over the last few weeks Watch OS 2 support has been added to Pivotal Core Kit’s existing WatchKit framework. This simplifies testing since it consists of an interface-identical copy of every class in Apple’s WatchKit.

Another core component is PivotalCoreKit’s helper class PCKInterfaceControllerLoader. For Watch Os 1 this class helps you instantiate your app’s WKInterfaceController subclasses in a testing environment. In theory, the loader uses your storyboard as a template to construct a test double of your interface controller.

However, the target you built is an iOS testing target and Xcode 7 does not allow iOS to compile any watch storyboards. Thus to instantiate view controllers from storyboards in your testing target, you will need a product built by Xcode accessible by your watch testing target. This can be achieved by adding a new ‘Copy File phase’ in Build Phases for the test target:

  • Modify ‘Destination’ to ‘Resources’
  • Add your <watch-app>.app located under ‘Products’

image00

Once that’s done, you should be able to load your storyboard using the PCKInterfaceControllerLoader:


[[PCKInterfaceControllerLoader new] interfaceControllerWithStoryboardName:(Storyboard name)
                                       identifier:(Storyboard Identifier)
                                         bundle:[NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:(Watch App Name)
withExtension:@"app"]]];

Strictly speaking you don’t need the whole watch app copied into the testing target, just the storyboard files within. However there is no simple way to import just the storyboard files (since you would have to add explicit search paths for each storyboard file you want to access).

Lastly, there is a downside to building a iOS test target to run watch tests since there is a larger level of separation. Recently, Cedar 0.12.0 introduced the capability of using a spec suite to test watch. This is another way you could setup a testing environment for watch but since Xcode 7 doesn’t support test bundles for watch it’s fairly limited and has lesser Xcode integration.

To get a clearer understanding of the setup process, check out this WatchTDDDemo project. This should help you setup TDD for Watch. Happy Testing!

About the Author

Biography

More Content by Priyanka Ranjan
Previous
This Month In Data Science: October 2015
This Month In Data Science: October 2015

The promise and risks of big data analysis received attention of scrutiny in recent weeks. While data scien...

Next
Introducing Greenplum: World’s First Open Source MPP Data Warehouse
Introducing Greenplum: World’s First Open Source MPP Data Warehouse

Today, Pivotal unveiled the first massively parallel processing (MPP) data warehouse to open source. The re...

×

Subscribe to our Newsletter

Thank you!
Error - something went wrong!