p-3 / sample-iOS-WatchKit

NativeScript iOS WatchKit proof of concept example.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

TNS WatchKit App

This is a proof of concept app with NativeScript for iOS and the WatchKit APIs.

Walkthrough

To set up a project:

tns create project TNSWatchKitApp
cd TNSWatchKitApp
tns platform add ios
tns prepare ios

Open the Xcode project in /platforms/ios/TNSWatchKitApp.xcodeproj.

NOTE: To manually set up a WatchKit extension with NativeScript you will have to work in Xcode. This is not supported from the CLI yet.

Add WatchKit Extension

Go to File > New > Target and select Apple Watch > WatchKit App. In the dialogue set:

  • Include Notification Scene.
  • Include Glance Scene.

Xcode Quirks

Set the Deployment Target to iOS 8.2

Click on the TNSWathcKitApp to open the targets and set the deployment targets to iOS8.2 in:

  • Targets > TNSWathcKitApp WatchKit App > Build Settings > iOS Deployment Target

NOTE: This may change in future. I have the iOS8.3 SDK and my Xcode generates the WatchKit App extensions with deployment target set to iOS8.3.

Set Bundle Versions

I had CFBundleVersion mismatch in the Info.plist files in:

  • TNSWathcKitApp WatchKit Extension > Supporting Files > Info.plist > (was '1.0')
  • TNSWatchKitApp WatchKit App > Supporting Files > Info.plist > Bundle version (was '1')

Just set it to '1.0' everywhere to perfectly match the version string.

Open the WatchKit simulator

Run TNSWatchKitApp WatchKit App in iPhone 6 simulator. Select the simulator and show the watch display from: Hardware > External Displays > Apple Watch - 42mm.

It should be still blank.

Add Some UI in the Storyboards

Click on the TNSWatchKitApp > TNSWatchKitApp WatchKit App > Interface.storyboard to open the interface builder. From the widgets menu drag a label and a button on the small clock the "Main" arrow points to.

AddUI

Note that you have WatchKit Extension and WatchKit App pair. The App is deployed on the watch and has storyboards describing the UI. The Extension will be executed on the phone and will handle input and changes. There is a nice overview of the framework in the apple's documentation.

Replace the Static NativeScript.framework with the Shared NativeScript.framework

NOTE: We plan to distribute the shared NativeScript.framework in "tns-ios" in a "NativeScriptEmbedded/" folder of sort.

Open the Targets > TNSWatchKitApp > Build Phases > Link Binary With Libraries and remove the static NativeScript.framework from there.

Then from the NativeScriptEmbedded folder add the shared NativeScript.framework to:

  • Targets > TNSWatchKitApp > General > Embedded Binaries
  • Targets > TNSWatchKitApp WatchKit Extension > Embedded Binaries

Add Metadata in the Extension

You also have to add metadata in the Extension to support the Objective-C APIs in JavaScript. You need to copy the metadata build phase from the app.

Get to the Targes> TNSWatchKitApp WatchKit Extension > Build Phases and add new Run Script Phase executing the following:

pushd "$SRCROOT/metadataGenerator/bin"
./metadata-generation-build-step.sh
popd

Check if the app is running.

Bootstrap the NativeScript in the Extension

Go to the TNSWatchKitApp/platforms/ios/TNSWatchKitApp WatchKit App folder and create app folder with bootstrap.js inside:

.
├── Base.lproj
................
├── Info.plist
└── app
    └── bootstrap.js

bootstrap.js:

console.log("Hello World!");

And add the app folder to the TNSWatchKitApp WatchKit Extension as reference. It should look similar to the TNSWatchKitApp's app.

You also need to add a main.m file in TNSWatchKitApp WatchKit Extension/Supporting Files to bootstrap the runtime.

#import <WatchKit/WatchKit.h>
#import <NativeScript/NativeScript.h>

static TNSRuntime* runtime;

__attribute__((constructor))
void initialize() {
    runtime = [[TNSRuntime alloc] initWithApplicationPath:[NSBundle mainBundle].bundlePath];
    TNSRuntimeInspector.logsToSystemConsole = YES;
    [runtime executeModule:@"./"];
}

If you run the 'TNSWathcKitApp WatchKit App` now the bootstrap.js will execute printing "Hello World!" in the console.

Implementing the Controllers using NativeScript

The next step will be to implement the controllers in bootstrap.js.

NOTE: Building the WatchKit App interface will use the storyboards and will require the controller headers at compile time. We plan to solve this by providing our own templates that will allow you to focus on the JavaScript.

NOTE: We will try to implement all that work in the CLI so you can start up from here.

For each of the files:

  • InterfaceController.m
  • NotificationController.m
  • GlanceController.m

Go to the "Target Membership" of the file and un-check the "TNSWatchKitApp WatchKit Extension". We do not need these classes to compile, will implement these classes in JavaScript, bootstrap.js:

console.log("Hello World!");

var taps = 42;
var InterfaceController = WKInterfaceController.extend({
    awakeWithContext: function(context) {
        this.super.awakeWithContext(context);
        console.log("InterfaceController: awakeWithContext");
    },
    willActivate: function() {
        this.super.willActivate();
        console.log("InterfaceController: willActivate");
    },
    didDeactivate: function() {
        this.super.didDeactivate();
        console.log("InterfaceController: didDeactivate");
    },
    tapIncrement: function() {
        console.log("Here!");
        taps++;
        this._tapsLabel.setText("Taps: " + taps);
    },
    tapsLabel: function() {
        return this._tapsLabel;
    },
    "setTapsLabel:": function(value) {
        this._tapsLabel = value;
        console.log("Set label: " + value);
    }
}, {
    name: "InterfaceController",
    exposedMethods: {
        tapIncrement: { returns: interop.types.void, params: [] },
        tapsLabel: { returns: interop.types.id, params: [] },
        "setTapsLabel:": { returns: interop.types.void, params: [interop.types.id] }
    }
});

var NotificationController = WKUserNotificationInterfaceController.extend({
    willActivate: function() {
        this.super.willActivate();
        console.log("NotificationController: willActivate");
    },
    didDeactivate: function() {
        this.super.didDeactivate();
        console.log("NotificationController: didDeactivate");
    }
}, {
    name: "NotificationController"
});

var GlanceController = WKInterfaceController.extend({
    awakeWithContext: function(context) {
        this.super.awakeWithContext(context);
        console.log("GlanceController: awakeWithContext");
    },
    willActivate: function() {
        this.super.willActivate();
        console.log("GlanceController: willActivate");
    },
    didDeactivate: function() {
        this.super.didDeactivate();
        console.log("GlanceController: didDeactivate");
    }
}, {
    name: "GlanceController"
});

console.log("declared controllers");

As you have seen we have the tapIncrement, tapsLabel and setTapsLabel: methods in the InterfaceController. They will handle basic interaction.

To make the button fire the tapIncrement and export the label in the "tapsLabel" property in the interface builder (IB) you will have to include the "InterfaceController.m" back in the TNSWatchKitApp WatchKit Extension target.

InterfaceControllerTargetMembership

Then show the "Assistent Editor" and drag the buttons's "Sent Actions" and label's "Referencing Outlets" in the InterfaceController.m to generate the stubs:

AddButtonAction

AddLabelReferencing

Then uncheck the "InterfaceController.m" again, build and run.

NOTE: Having to add the .m files when you work with the IB and remove them during compilation is boring and tricky but we will try to provide automated assistance with templates, custom markup or even generate these manually from by examining your JavaScript.

About

NativeScript iOS WatchKit proof of concept example.


Languages

Language:C 57.8%Language:JavaScript 34.3%Language:C++ 7.7%Language:Objective-C 0.2%Language:Shell 0.0%Language:CSS 0.0%