“WatchKit での失敗” 続編

“私の WatchKit での大きな失敗” の続編となります。さらに何人かの友達に Apple Watch のアプリ開発時に役立つことについて共有してもらうようお願いしました!

パート1はこちらです。


Joe Hughes

...

Mobile lead at Citymapper

ついに Citymapper の Watch アプリが人々の手元に届くようになりました。今後も Watch と iPhone でどのようなやりとりがされ、ユーザーにとってどのような道標となるか、より深く研究していきたいと思います。

Handoff 機能は本当に便利な機能です。- 先ほどまで Watch で見ていた画面を iPhone でスワイプアップするだけで素早く見れるようになります。Citymapper ではライブでの出発ページや旅行やサービスのアラートなどでこの機能をサポートしています。しかし、初めの頃は iPhone アプリ側で設定の必要があるときに、設定のステップでどのくらい Handoff の機能が使えるものなのか見落としていました。そして、いくつかのアプリでこの機能を上手に実装しているものを見てきました。

別のやり方として iPhone で見始め、Watch で続きを見るパターンもあります。たとえば、旅行の設定を iPhone でして Watch でその続きを見たい時などがあります。Watch ユーザーの iPhone 側については、ユーザーが Watch と接続されているか知る API はありませんが、できることとしては Watch アプリが実行されたときに共有された NSUserDefaults にフラグを保存することができます。これで、Watch 特有のメッセージや機能が iPhone 上で利用できるようになったことを知ることができます。

実際に Watch アプリがリリースされて反応が見れるのが楽しみです。- このウェアラブルの短い持続時間をどのように作っていくのかいろいろ学んでいきたいと思います。

Joe のツイッターは @joooe です。


Phillipe Casorla

Director of Mobile Engineering at Lifesum

私の一番の失敗は iOS 7 のことを考えずに iOS 8 向けに開発していたことです。iOS 7 対応をする方法を理解しました。

Lifesum の開発ではリファクタリングを熱心に行っています。コアのライブラリをアプリ間やウィジェット、Watch アプリでコードが共有できる Embeded Framework でビルドし開発を行っていました。順調に進んでいましたが、リリース間近のときに Embeded Framework が iOS 7 ではサポートされていないことに気付きました。その時点では、スタティックライブラリとして組み込むか、全てのソースファイルをメインターゲットに戻すか二つ選択肢がありました。どちらを選んだと思いますか?

その始めの失敗がより難しい二つ目の失敗を招きました。Embeded Framework を使う利点の一つは、AppGroup で CoreData を使うことを引立たせてくれることです。Framework を使うことで CoreData のファイルコーディネータで簡単にメインアプリと Extension の同じデータに読み書きができます。

CoreData のデータファイルを Document ディレクトリから Shared Container に移動する方法について Apple からの案内は何もありません。始めに、古いフォルダから新しいフォルダに手動でファイルを移動させます。これは簡単でした。しかし、CoreData は、これよりも必ず複雑になっていきます。一番良い解決策は NSPersistentStore migratePersistentStore:toURL:options:withType:error を使うことです。しかし、かなり複雑なコードを書くことが必要とされます。考えらなければいけないことが増えていき、大変になると思います。

全ての適切に行われたのでラッキーでした。しかし、一つ計算に入れるのを忘れていたことがありました。 マイグレーションにかかる時間を計ることです。iPhone 4S では 30秒以上もかかりました。 この遅れは application:didFinishLaunchingWithOptions の中でマイグレーション処理を行いしばらく何も返さないようにしていたため、ユーザーはアプリを開くことができませんでした。なぜなら処理が終わる前に OS によってアプリが終了させられてしまうからです。ここから多くの教訓を学びました!

Phillipe のツイッターは @Darkzlave です。


Kostiantyn Koval

Organizer and blogger at Rocketfarm

一番の失敗は Embedded Framework でメインバンドルのリソースを探していたことです。フレームワークにすることで iOS と WatchKit のコードとリソースが共有できます。しかし、覚えておいてください。フレームワーク内で画像や plist ファイルやデータモデルを使うときは、必ずバンドルを指定する必要があります。

// The wrong way
UIImage(named: "Rectangle")

// Instead, look for image in main Bundle

// The right way
let bundleForClass = NSBundle(forClass: self)
let bundleForId = NSBundle(identifier:"kkoval.SharedKit")

// "kkoval.SharedKit" is a framework bundle identifier

UIImage(named: "Rectangle", inBundle: bundleForClass, compatibleWithTraitCollection: nil)
UIImage(named: "Rectangle", inBundle: bundleForId,    compatibleWithTraitCollection: nil)

let modelURL = bundleForId?.URLForResource("Model", withExtension: "momd")
let model = modelURL.flatMap { NSManagedObjectModel(contentsOfURL: $0) }

私の試行錯誤の結果を見たい方は サンプルの Xcode プロジェクトがあります。
Kostiantyn のブログはこちらです。


Natasha Murashev

iOS Engineer at Capital One and blogger at Natasha The Robot

先週 “私の WatchKit での大きな失敗” でお話ししました。さらに役に立つことがありましたので共有いたします。iOS アプリと WatchKit アプリを同じタイミングでデバッグする方法です。

iOS アプリの AppDelegate には WatchKit Extension から情報を受け取るメソッドがあります。

// AppDelegate
func application(application: UIApplication, handleWatchKitExtensionRequest
                    userInfo: [NSObject: AnyObject]?, 
                       reply: (([NSObject: AnyObject]!) -> Void)!)

このメソッドを始めて使ったとき上手く動かず、どのようにデバッグすればいいのかわかりませんでした。WatchKit アプリかメインの iOS アプリかどちらか一つのターゲットしかブレークポイントなどでのデバッグができないと思っていました。その時は iOS アプリのほうでは AlertView を使って変数の状態などを確認するようにしていました。のちに iOS と WatchKit アプリの両方を同じタイミングでデバッグする方法があることを知りました。

  • WatchKit のアプリターゲットを実行
  • iOS アプリをシミュレータを開きます
  • Xcode に戻ります
  • Debug > Attach to Process > (YOUR iOS App) を選択します

これで、ブレークポイントと普段のデバッグツールが WatchKit アプリを動作させながら iOS アプリで動きます! かなり便利です :)

Natasha の Apple Watch に関するビデオ: Architecting Your App for the Apple Watch


Stephen Tallent

iOS at Mercury

iPhoneアプリにおいて、サーバーからデータや画像を取得し表示するという典型的なアプローチは Watch アプリではうまく機能しないことを学びました。 これは WatchKit アプリでは上手く機能しません。WatchKit アプリはかなり遅いです。Extension とホストアプリ間やサーバー間でやり取りする時間がたくさんあると非常に悪いユーザー体験になります。

ユーザーは iPhone アプリのときよりも Watch アプリのときの方が即座に情報を必要とします。いろいろ試した結果、ユーザーが起動する前に Watch アプリにほとんどのデータをあらかじめ同梱するようにしました。適度にバックグラウンドタスクが動作して、普通にアプリを使用するときと同様にデータを継続的に更新します。この結果、Watch アプリはスクリーンに描画する以外何もしなくて良くなりました。かなり良いパフォーマンスとユーザービリティが得ることができるようになりました。

Stephen のブログはこちらです。


Jeff Grossman

Co-founder of Confide

アプリのローカライズを積極的に行っている場合は 右から左に書く言語のサポートを忘れないでください。インターフェイスの部品のデフォルトは ”potision left horizontal” と “size width to fit content” が設定されています。右から左に書く言語のサポートのために WKInterfaceLabel の左に整列されたテキストの中に右に整列した文字のテキストがぎこちなくスクリーンに表示されることかと思います。これを修正するために、該当する場所の “width” を “relative” に設定してください。

他にも早い段階で学んだことがあります。iOS デバイスで Handoff 機能を使用したとき Watch 側に通知する機能は Apple が提供する API には含まれていません。しかし Handoff を使用したとき、今開いてる WKInterfaceController を閉じたいということはよくあります。

これをする一つの方法は handoffUUID ([[NSUUID UUID] UUIDString] で生成される) を userInfo に含め updateUserActivity(:userInfo:webpageURL:) で渡すという方法です。

iOS アプリの中では、UIApplicationDelegate の application(:continueUserActivity:restorationHandler:) を使って受け取ります。そして、成功したときに handoffUUID をチェックします。もしあれば MMWormhole を使い、成功したメッセージを Watch 側に送ります。WKInterfaceController がそのメッセージを受け handoffUUID が一致したとき、それを閉じるか他の成功時の処理を実行するかします。

* 注意することは、キーチェーンや NSDataWritingFileProtectionCompletekSecAttrAccessibleWhenUnlocked を使っている場合、実機のデバイスでテストするときは、パスコードロックが有効であることを覚えておいてください。 Apple Watch がアンロックされているということは iPhone もアンロックされていてデータにアクセスできる状態ということを意味しません。

Jeff のツイッターは @jeffrey903 です。


Chad Etzel

iOS Lead at Jelly

iOS 開発者の中には Interface Builder を使わずに全てのビューを(私も含めて)プログラムで作りたがる人がいます。 WatchKit アプリでは、ユーザーインターフェイスを作るのに StoryBoard を使うことが必須となります。このことは インターフェイスビルダーをどのように使うのか再び学ばなければいけないことを意味します。

WatchKit を学ぶために、はじめに Objective-C で FlickrWatch というサンプルアプリを作りました。 一度動いたので、新しいプロジェクトとしてこれを Swift で全て書き換えようとしました。その時、時間の節約のため Swift プロジェクトに StoryBoard ファイルをコピーすればいいだけだと思いそのままコピーしました。(全く同じインターフェイスを使っていましたし…)しかし、このことは大きな間違いだと気付きました!

StoryBoard ファイルのアウトレットがどのファイルと接続されているのかおかしくなってしまいました。アウトレットを削除し適切なファイルにつなぎかえようとしました。しかし、アプリは起動しましたが、StoryBoard の場面をロードする段階で失敗していました。(Watch には永遠にスピナーが表示されていました。)間違いだと分かっている場所、全てアウトレットを繋ぎ変えていきましたが、結局一からやり直すことになりました。Swift プロジェクトで StorbyBoard を再び作り直し、問題なく動きました。

Chad のツイッターは @jazzychad です。


Andrea Mazzini

Cofounder & iOS developer at FancyPixel

私の大きな失敗は Watch アプリが iOS アプリと同じ方法で作れると思っていたことです。ほとんどのアプリは新しいデータを取得し、画面に表示するような短いやり取りのデザインがされています。WatchKit では、このやり取りはより短いです。ユーザーが Watch アプリを閉じると、動いていた Extension が止まります。

WatchKit Extension の動作は短く、ネットワーク処理などの重い処理はメインのアプリに任せた方が良いということを学びました。WatchKit にはこれらを上手くやる方法があります。WKInterfaceControlleropenParentApplication(:reply:) を使うことによりメインアプリを起動させることができます。これを使い、データを新しくバックグランドで取得したりそれを安全な場所に保存などして、次の Watch とのやりとりの準備を行います。

Andrea のブログはこちらです。


Marthin Freij

Developer and co-founder at Amazing Applications

一番の失敗は大きく考えすぎたことです。開発者は、本当に価値のあることよりも何ができるかに目がいってしまい盲目になってしまうことがあります。Apple Watch は、アイ���ィアのコアの部分とそれをユーザーの手首にどのようにフィットさせるのか理解することが重要となってきます。

Andrea が言うように、Watch アプリを iOS アプリを作るときと同じ���うに考えるところ���らスタートしていました。私たちの料理アプリの Green Kitchen に適用したとき、iOS から Watch に全ての機能を移植しようとしていました。自分の手首でレシピを探しているところを想像してみてください! しかし、デザインをし始めたときすぐに複雑で遅く、ダサいアプリだということが明らかになりました。つまり、Watch でそのようなことは良いユースケースではなかったのです。

少し戻って、ユーザーの立場に立って考えることから始めました。そして、料理する場面というのは、積極的なときと消極的なときの二つの場面に分かれることに気付きました。私たちの iOS アプリはユーザーが実際に料理をしている、積極的な場面で効果的に機能し、しかし、シチューを煮込んでいるときやオーブンのタイマーが鳴るのを待っているときなどの消極的な場面ではあまり役に立ちません。このような場面で、iPhone よりも Watch の方が良いことは何かあるでしょうか?

フォーカスを狭めることで Watch のより特定の解決策に至りました。料理のタイマーは iPhone からスタートします、時間が来た時に、Watch に表示される情報が切り替わります。この場合は、食事がどのような見た目、味になっているのかの説明です。そして、時間が少し足りなかったときは、iPhone を触るのではなく、Watch から直接タイマーを延ばすことができます。食事ができたときは、Handoff を使いすぐにレシピに戻ることができます。ここから得た良い教訓は、小さく考えることが大事だということです!

Marthin のツイッターは @marthinfreij です。


Jeff Forbes

iOS at Foursquare

私がした一番の失敗は、機能の範囲を広げて開発してしまったことです。アプリをシンプルで簡単かつ役に立って気持ちの良いものにするために Watch アプリのユーザー体験を最適化することについて慎重に考えていかなければなりません。常に KISS の原則を適用するようにしてください! “シンプルにしておけ!この間抜け!” です。

Watch アプリにはよく分からない部分が多くあります。Watch がユーザーの手元に届くまでどこの部分にバグがあるのか正確にはわからないものを見つけるのはかなり困難なことです。

Marthin や Andrea が言うように、アプリの機能を Watch に対して再設計しなおす必要があります。結局は Watch にできるだけ短い時間で価値のある情報を届けるユースケースを考えていくことが重要になってくるのです。余計な機能を追加することは、無駄なことをしているだけです。Watch 開発者が複雑にしているだけと言われるかもしれません。

Jeff のツイッターは @masterjeef です。

記事の更新情報を受け取る

About the content

This content has been published here with the express permission of the author.


Joe Hughes

Mobile lead at Citymapper; fan of design, urban form, public transit (and open data), experimental video games, lindy hop, fine cocktails, and ramen

Phillipe Casorla

Taking over storage through Makespace. Previously Director of Mobile at Lifesum and CTO at Sabor Studio. Living the world through my products.

Kostiantyn Koval

iOS Dev at Agens. In love with iOS & Swift. Swift High Performance author

Natasha Murashev

Natasha is secretly a robot that loves to learn about Swift and iOS development. She previously worked as a senior iOS Engineer at Capital One in San Francisco, but now she travels around, writing about her experiences with new technologies. In her free time, she works on personal projects, speaks at meetups and conferences, contributes to open source, and likes to cross things off her bucket list.

Stephen Tallent

Hockey fan and skateboarder when I’m not furiously coding. Actually, even when I am.

Jeffrey Grossman

Developer. Cook. Photographer. Co-founder of Confide.

Andrea Mazzini

Andrea Mazzini writes code primarily for iOS but sometimes for Rails. When coding gets to his nerves he unwinds by writing tech articles and drawing stuff with Sketch. You can checkout his work over at Fancy Pixel and his open source work on his Github page.

Chad Etzel

Overall programming nerd with too many project ideas for one lifetime. Nixie tube enthusiast.

Marthin Freij

Developer and co-founder at Amazing Applications. 💚 Green Kitchen ❤️ Healthy Desserts

Jeff Forbes

Dude. iOS Dev @ Dropbox.

4 design patterns for a RESTless mobile integration »

close