1. Watch Kit App Architecture

We have 2 bundles: WatchKit app (runs on Watch) and WatchKit extension (runs on iPhone). WatchKit app only has storyboards. (WatchKit app and WatchKit extension are bundled together and packaged inside your iOS app bundle – prompt to install Watch app if paired Apple Watch is present).

Screen Shot 2015-05-01 at 2.17.05 pm

  • The WatchKit App = VIEW of our app.
  • The WatchKit Extension = CONTROLLER & MODEL of our app.
  • The Watch is really just an additional display for our app.

2. Interfaces

Glance Interface: glance is read-only, non-scrolling, has no buttons/ switches, when tapped -> open the Watch app.

Custom Notification Interface: once the app has notification tapped – notification can display the custom notification screen.

Interface Controller: In Watch, there is no auto-layout, every object has to be pinned to something, either the edges of screen or other objects.

Lifecycle of Interface Controller:

Update: on Watch OS2 – the app will be running on the Watch itself, not the iPhone.

3. Actions and Outlets

Apple takes care of the communication between the Watch and the Extension, so we can just connect the UI of the Watch to the functions of the Extension, and the communication will work over Bluetooth Low Energy.

4. How to share data between iPhone app and WatchKit extension? (Link)

4.1 For the iPhone app to communicate with Watch app:

The iPhone app can not launch it’s Watch extension. Except replying to Watch extension requests, iPhone app can write to Shared Container. It can use Darwin Notification Center (API of Core Foundation) to inform the Watch app about some events.

However the Watch app has to be foreground, and no objects can be passed through Darwin (only userInfo dictionary).

4.2 For the Watch app to talk with iPhone app:

The Watch app can launch the iPhone app on background. To share information, it can write to Shared Container and then notify the iPhone app to read.

It can also pass data through openParentApplication(userInfo:reply:),

5. How to do network call for WatchApp: using parent app or calling directly from WatchKitExtension?

According to this article there are 3 ways: http://blog.curtisherbert.com/data-synchronization-with-watchkit/

However, we can call directly from WatchKit Extension itself, by using a framework as in RayWenderlich’s BitWatch sample:

By importing the framework, the tracker class now can be called inside WatchKit itself.

6. Compile Error: 

error: WatchKit apps must have a deployment target equal to iOS 8.2 (was 8.3)

(Stackoverflow link)

 Screen Shot 2015-05-07 at 3.49.24 pm

error: The value of CFBundleVersion in your WatchKit app's Info.plist does not match the value in your companion app's Info.plist

Remember that Version (1.1.0) and the Build Number (12) have to match for the parent app and the watch app. (Version != Build Nb).

7. Number Input For Watch

https://github.com/BalestraPatrick/AppleWatchCalculator

A PIN screen is being used in Apple Watch

Screen Shot 2015-06-22 at 3.45.50 pm

8. App Structure as Hierarchical (using Table):

We can start as a table: it can be an empty InterfaceController.h file, and have the weak IBOutlet WKInterfaceTable* table.

Use a table: strong NSArray* elements for storing data.

The functions needed in InterfaceController.m will be:

  1. init: set the elements value – f.e. calling loadTableRows:
  2. awakeWithContext
  3. willActivate
  4. didDeactivate
  5. handleActionWithIdentifier: -> forLocalNotification: & forRemoteNotification:
  6. handleUserActivity: -> pushControllerWithName:@”…” context:userInfo[@””]
  7. table: didSelectRowAtIndex:
  8. loadTableRows (self-defined):
[self.table setNumberOfRows:self.elements.count withRowType:@"default"];

    // Create all of the table rows.

[self.elementsList enumerateObjectsUsingBlock:^(NSDictionary *rowData, NSUInteger idx, BOOL *stop) {                    
          MyRowController *elementRow = [self.table rowControllerAtIndex:idx];
          [elementRow.elementLabel setText:rowData[@"label"]];  
}];

9. Page-based App Structure:

  • Create Segues on Storyboard of the Watch App to link the Interface Controllers together and the App will just load them based on that order. To change the controllers, we call  reloadRootControllersWithNames:contexts: to load other controllers, different from the one that was setup by segues.
  • All interface controllers will be created and loaded before the first is displayed -> make them small and efficiently loaded. If we want to change a controller to be displayed first: call becomeCurrentPage from its init or awakeWithContext: method.
  • When we move to a new page, the current page’s didDeactivate is called, then the new page’s willActivate method will be called (to reflect new changes/ content).

What I did: create the Interfaces first on Storyboard, than drag the nextpage segue link from the previous to the next page -> paging established.

10. Context Menus

doMenuItemAction : if using page-based root, we can present something modally on Menu Action. To present modally, there are 3 ways to do:

11. WatchKit Assets

For WatchKit app, the AppIcon images are of size:

    (these are all in pixels)
    48 x 48
    55 x 55
    58 x 58
    80 x 80
    87 x 87
    88 x 88
    172 x 172
    196 x 196

All the images have to be inside an image asset file in the WatchKit App.

12. Table with Sections (for Active and Triggered alerts)

We can create a table with 2 row types: HeaderRow and DetailRow.

What I did:  I created 2 RowController classes: HeaderRowController & AlertRowController. The HeaderRowController only has a header label (and a group in case I want to change bg color). The corresponding element in Storyboard’s table is “AlertHeader“. The AlertRowController has many properties: images and labels, to enable changing content. The corresponding element is “AlertRow“.

Then in loadTableData() function, I first added data for all the 2 sections, the active rows: [rowTypes addObject:@”AlertRow”]; Then I added the Header row using [rowTypes insertObject:@”AlertHeader” atIndex:…..]; 

 

Reference:

1. http://www.fiveminutewatchkit.com/

13. Table with Sections (for Active and Triggered alerts)

FAQ (Link)

  1. Can we open website from Watch app?
    1. On OS1, we can ask the opening app to open a website in Safari for us.
  2. Can third-party apps make a phone call from Watch app?
    1. No.
  3. Can we put 1 UI element on top of another?
    1. No. But there is a way to simulate it: “set the background image of the current group to something resembles the previous”.
  4. Can I mix page-based and hierarchical ?
    1. Shortly, once we have a page-based ancestor, no hierarchy from that point (only root will be displayed and nothing can be pushed on it’s stack)!
    2. Once we go page-based, new controllers have to be displayed modal.
    3. Only way to go hierarchical is purely hierarchical with only leave-level are page-based.
  5. Cache image: addCachedImage(_:name:) fromWKInterfaceDevice. or addCachedImageWithData(_:name:)
  6. “Standalone OS2 meaning”: On OS1 – if the Watch can’t reach the phone, the apps won’t run. If already running -> will be suspended. On OS2, the Watch can still be running.
  7. What is Darwin Notification?
    1. Darwin Notification is like a shout in real life. It’s not persisted, so if there is no one out there, your notification is lost.
    2. There is no objects, only a name & userInfo dictionary.
  8. setImageNamed vs setImage:
    1. setImageNamed can only be used if the image is cached on Watch. setImage will send the image data over to the Watch.
  9. Animation fps: no cached ~10fps. Cached ~30 fps. We can set animation length -> system will decide the fps.
  10. Share Data between App and Extension:
    1. Enable App Groups: Target-Capabilities:
    2. Screen Shot 2015-06-25 at 11.59.19 am
    3. AppGroup-enabled: Use NSFileManager to get the URL of the shared directory
      var sharedContainerURL: NSURL? = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier(kMyAppGroupName)
    4. AppGroup-enabled: use NSUserDefaults
      let sharedUserDefaults = NSUserDefaults(suiteName: kMyAppGroupName)
    5. More on Apple Technical Note
Advertisements