Objective-C
iOS
AppleWatch

AppleWatchアプリのHello WorldからiPhoneとの通信まで開発ノウハウまとめ

More than 3 years have passed since last update.

4月24日のAppleWatch発売に向けてWatchKitを使用したアプリの作り方を紹介します。

簡単なWatchKitの起動の方法から、

WatchKitからiPhoneにリクエストを投げて、iPhone側で処理を行いWatchKitに応答する方法を説明します。

また、現時点で分かっているAppleWatchで可能なこと/不可能なことをまとめました。

実機ではできるが、シミュレーターではできないこともまとめました。

(2015年4月6日 現在)

スクリーンショット 2015-04-05 18.36.00.png


AppleWatchアプリの種類

AppleWatchアプリには3種類あります。

・WatchKit Apps

・Glance

・Notification

WatchKit Apps:メインとなるAppleWatch内で操作できるアプリ

Glance:スクロールなどはできない一枚絵の画面。タップするとWatchKit Appsへ遷移。

Notification:プッシュ通知やローカル通知を表示する

スクリーンショット 2015-04-05 23.50.39.png

公式のビデオガイドを見ると、

起動画面から上にスワイプするとGlanceが表示され、

起動画面から下にスワイプするとNotificationが表示されるようです。

https://www.apple.com/jp/watch/guided-tours/

本記事ではWatchKit Appsの作り方を紹介します。


WatchKit Appの作り方

AppleWatchのアプリは単体では作れません。

iPhone本体アプリが必要です。

既存のXcodeプロジェクトにWatchKit Appsを追加します。

①「File」→「New」→「Target」を選択

スクリーンショット 2015-04-05 16.04.59.png

②「Apple Watch」を選択

スクリーンショット 2015-04-05 16.06.02.png

③デフォルトのままでFinishします。

スクリーンショット 2015-04-05 16.06.35.png

④WatchKit Appsを起動できるように新しいスキームを追加します。

スクリーンショット 2015-04-05 16.07.00.png

⑤ファイルが追加されていることを確認

「(プロジェクト名) WatchKitExtension」と「(プロジェクト名) WatchKit App」のフォルダが追加されているはずです。

スクリーンショット 2015-04-05 16.07.31.png

⑥WatchKit Appsを起動

コードに何も追加していませんが、まずはWatchKit Appsをシミュレーターで動かしてみます。

スクリーンショット 2015-04-05 17.50.56.png

⑦AppleWatchシミュレーターの追加

デフォルトではiPhoneシミュレーターしか起動しないので、手動でAppleWatchシミュレーターを起動します。

iOSシミュレーターをクリックしてカーソルを合わせて、

「Hardware」→「External Displays」→「Apple Watch - 38mm」を選択。

スクリーンショット 2015-04-05 18.54.09.png

⑧AppleWatchシミュレーターの起動を確認

AppleWatchの画面に何も追加していませんので、今は何も表示されません。

スクリーンショット 2015-04-05 18.57.50.png


AppleWatchにボタンとラベルの追加

iOSアプリと同じ要領でStoryboardにUIを追加します。

追加したボタンが押された時の処理の追加はiOSと同様に、画面に対応するInterfaceController.mに線を引っ張ってリンクさせます。

(詳細は割愛させて頂きます。)

スクリーンショット 2015-04-05 19.06.18.png

ビルドすると追加されたUIが表示されます。

スクリーンショット 2015-04-05 19.11.33.png


AppleWatchとiPhoneとの通信

AppleWatchは単体では通信の仕組みなどを持っておらず、iPhoneに処理を依頼してデータを受け取ります。


AppleWatch側からのリクエスト

画面が表示されたときにiPhone側にリクエストを投げるサンプルコードを紹介します。

iPhoneとの情報のやり取りはNSDictionaryに開発者が定義した中身でやり取りします。

WatchKit AppからはWKInterfaceController -openParentApplication:reply:を使用します。

※このメソッドを受け取ったからといってiPhoneアプリを開くことはできません。

(=フォアグラウンドにはなりません。)

iPhone側は状態を維持したままになります。


Objective-C


- (void)willActivate {
// 画面が表示されたとき
[super willActivate];

NSDictionary *userInfo = @{@"command": @"hello"};

// iPhoneにリクエストを投げる
[WKInterfaceController openParentApplication:userInfo reply:^(NSDictionary *replyInfo, NSError *error) {
// 結果が返ってきたとき
if (replyInfo) {

// エラー時
NSString *result = replyInfo[@"result"];
if ([result isEqualToString:@"error"]) {
// エラー理由の表示
NSString *message = replyInfo[@"message"];
if (message) {
NSLog(@"error: %@", message);

}
return;
}

// 成功時
if ([result isEqualToString:@"success"]) {
// 応答文の表示
NSString *message = replyInfo[@"message"];
if (message) {
NSLog(@"success: %@", message);
}
return;
}
}
}];
}



iPhoneでのAppleWatch Appからのリクエストの受信

iPhone側はリクエストをAppDelegateのapplication:handleWatchKitExtensionRequest:reply: で受け取ります。

AppDelegateの肥大化を防ぐために、WatchKitManagerクラスを作成しました。

AppDelegateのプロパティにWatchKitManagerを持たせます。


Objective-C

@interface AppDelegate ()

@property (nonatomic) WatchKitManager *watchKitManager;

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

self.watchKitManager = [WatchKitManager new];

return YES;
}

- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply {

[self.watchKitManager handleWatchKitRequest:userInfo reply:reply];
}

@end



WatchKitManagerによる処理

WatchKitManagerはAppleWatchからのリクエストを受けて、

iPhone側で通信処理などを行った結果をAppleWatch側に返すために、

応答用のblock文replyをプロパティに保持します。

こうするとことで、delegateメソッドやcompletionHandlerなどから応答を返すことが可能です。


Objective-C(WatchKitManager.h)

#import <Foundation/Foundation.h>


@interface WatchKitManager : NSObject

- (void)handleWatchKitRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply;

@end



Objective-C(WatchKitManager.m)

#import "WatchKitManager.h"


@interface WatchKitManager ()

// WatchKitManagerがどのタイミングでも応答できるようにblockをプロパティで保持する
@property (nonatomic, copy) void (^reply)(NSDictionary *);

@end

@implementation WatchKitManager

- (void)handleWatchKitRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply {

// エラーハンドリング
if (!userInfo) {
NSDictionary *replyInfo = @{@"result": @"error",
@"message": @"userInfo is nil"};
reply(replyInfo);
return;
}

// WatchKitへの応答を自由なタイミングで実行するためにプロパティに保持する
self.reply = reply;

if (userInfo[@"command"]) {
if ([userInfo[@"command"] isEqualToString:@"hello"]) {

// 通信や遅延処理をする
// ...
// 実行した後にプロパティに保持した応答用のblock文replyを使用する
NSDictionary *replyInfo = @{@"result": @"success",
@"message": @"hello!!"};
self.reply(replyInfo);
}
}
}

@end



サンプルの起動

WatckKitアプリを起動するとiPhoneに"command:hello"が送られ、

iPhoneは"message:hello!!"を返します。

それを受けたAppleWatch AppがログにiPhoneからのメッセージを表示します。

スクリーンショット 2015-04-05 19.34.00.png

以上がAppleWatchとiPhoneとの通信の仕方です。


AppleWatchではできないこと

公式回答に基づいてAppleWatchでできないことをまとめました。


・iPhoneからAppleWatchにリクエストを送れない

AppleWatchからiPhoneにリクエストを送ることはできますが、

iPhoneからAppleWatchにリクエストを送ることはできません。

◯ AppleWatch → iPhone

✕ AppleWatch ← iPhone

どうしてもiPhoneからAppleWatchに情報を送りたければ、

Local noticaitionを使用する手段はありますが、

AppleWatchのみでなく、iPhoneにも通知が表示されてしまいます。

https://devforums.apple.com/thread/266959


・AppleWatchからiPhoneアプリをフォアグラウンドにできない

AppleWatchからiPhoneにリクエストを送り、

AppDelegateのhandleWatchKitExtensionRequest:で受け取ることはできますが、

その中でopenUrlによってURLスキームする方法は、バックグラウンドではサポートされていません。

AppleWatchから強制的にiPhoneアプリをフォアグラウンドにすることはできません。

https://devforums.apple.com/message/1119498


・複数のAppleWacthとiPhoneを連携できない

iPhoneとAppleWatchは1:1でのみしか使用できません。


・iPadではAppleWatchを使えない

iPadではAppleWatchが使用できません。

iPadでiPhoneアプリを動かせますが、その場合にどうなるのかは不明です。

実機の発売が楽しみですね。


(実機ではできるけど)シミュレーターではできないこと


・iPhone端末ロック中のAppleWatchアプリの動作

シミュレーターではiPhone側がロックされているときにAppleWatchの表示が、

「Unlock to activate」となり、操作ができません。

スクリーンショット 2015-04-05 17.55.55.png

iPhoneがロックされているとAppleWatchアプリで何もできない?と驚きましたが、

シミュレーターのみの現象のようです。

公式曰く「試験のためだけ。いつか対応する。それがいつかは言えない。ごめんね。」だそうです。

https://devforums.apple.com/message/1092204


・Local Notificationを受け取れない

Remote Notificationはテスト用のPushNotificationPayload.apnsが使用できますが、

Local Notificationをシミュレーターで使うことは今のところできません。

https://devforums.apple.com/message/1096250


まとめ

未発売のため不明点の多いWatchKitですが、

手探りで情報収集中です。

(間違い等あればご指摘ください。)

新たに分かったことがあれば更新していきます。


参考

公式プログラミング ガイド

https://developer.apple.com/library/ios/documentation/General/Conceptual/WatchKitProgrammingGuide/index.html

公式クラスリファレンス

https://developer.apple.com/library/prerelease/ios/documentation/WatchKit/Reference/WatchKit_framework/

公式 WatchKit Development tips

https://developer.apple.com/watchkit/tips/

Watchkit Advent Calendar

http://qiita.com/advent-calendar/2014/watchkit