2014年12月 1日

iOS

Today ウィジェットの実装方法~導入から Tips まで #ios8yahoo

  • このエントリーをはてなブックマークに追加

Yahoo! JAPAN Tech Advent Calendar 2014の1日目の記事です。一覧はこちら

こんにちは。ヤフーで新規アプリ開発をしています田邉裕貴です。

この記事では、iOS 8 から導入された Today ウィジェットの開発の仕方についてご紹介します。

具体的な内容は以下のとおりで、iOS 8/Swift 勉強会で発表させていただいた「iOS 8 Widget ~ 導入から Tips まで」に添って説明します。スライドだけでは伝わらない部分を補完できればと思っています。

  1. Today ウィジェットとは
  2. Hello world ウィジェットの作り方
  3. 詰まりどころと Tips 集

Today ウィジェットとは

まずは簡単に Today ウィジェットの説明をします。

Today ウィジェットとは

Today ウィジェットとは、iOS 8 から新たに通知センターに追加された Today タブ内のコンテンツです。

Today という名前の通りリアルタイム性の高いコンテンツが並びます。
Apple のプリインストールされているアプリだと、カレンダー、リマインダー、株価などのアプリがウィジェットを導入しています。

Hello world ウィジェットの作り方

前提知識

Hello world ウィジェットを作成する前に前提として理解すべきことがあります。

ウィジェット作成の上での前提知識

それは、ウィジェットはそれ単体で申請することができないということです。
そのため、通常通りプロジェクトを作り、その中にウィジェットを追加して実装する流れとなります。

Hello world ウィジェットの作成

まずは通常のアプリと同様の流れで Xcode プロジェクトを作成します。(分からない場合はスライドの14~17枚目をご参照ください)

空のプロジェクトを作成したところで Today Extension を追加します。手順を以下に示します。

Target を追加

新しく Target を追加します。

Application Extension から Today Extension を選択

Application Extension から Today Extension を選択します。

Product Name の設定

ウィジェットの Product Name を記述して Target の追加を終えます。今回は Widget という名前にしています。

ウィジェットファイル群追加完了

Target を追加するとウィジェットのファイル群が追加されます。

MainInterface.storyboard の初期状態

MainInterface.storyboard の初期状態で Hello world の UILabel がセットされています。

ウィジェットの起動

Hello world 完成

そのため、ウィジェットを起動すると Hello world と表示されるウィジェットが完成していることが分かります。

以上で、Hello world ウィジェットの作成は完了です。

非常に簡単に作れたことが分かったと思います。
しかし一方で、実際にリリースするレベルのウィジェットを作ろうとすると非常に詰まりどころが多いです。
そのため、次のセクションでは詰まりどころや Tips を紹介していきます。

詰まりどころと Tips 集

詰まりどころと Tips 集について以下の項目を順番に説明していきます。

ウィジェット表示名変更

まずはじめにウィジェットの表示名についてです。

ウィジェット表示名変更

ウィジェット名はデフォルトではウィジェットの Product Name が表示されています。

Bundle display name を変更

これを変更するにはウィジェットの Info.plistBundle display name を指定する必要があります。

左余白の消し方

ウィジェットの表示エリアはデフォルトだと左側に余白ができます。

この余白を消したい場合には、TodayViewController.m で以下のような指定が必要です。

- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:
  (UIEdgeInsets)defaultMarginInsets
{
    return UIEdgeInsetsZero;
}

上の画像のように余白を消すことができました。
ただし、あくまで Apple のデフォルトは余白ありなので、必要な場合のみ検討した方が良いです。リジェクトのリスクが高くなると思われます。

ビューのライフサイクル

ビューのライフサイクルは実は通常のアプリとほとんど変わりません。

表示のタイミングでは viewDidLoad ~ viewWillAppear ~ viewDidAppear の順で呼ばれ、
非表示のタイミングでは viewWillDisappear ~ viewDidDisappear が呼ばれます。

※viewDidLoad に関しては WWDC の発表で言及されていませんでしたが、
開発時に毎回呼ばれることが確認されたのでここでは記述しました。

ウィジェットからアプリを起動

次にウィジェットからアプリを起動する方法です。
これによりウィジェットの特定のビューをタップするとアプリに遷移させるなどの処理が可能になります。

ウィジェットからのアプリ起動はカスタム URL スキームで実現できます。
ウィジェットの UIViewController で以下のように実装します。

[self.extensionContext openURL:url completionHandler:nil];

注意して欲しいのは通常のアプリと呼び出し方が異なることです。
ウィジェットからは UIApplication にアクセスできないため、以下のような呼び出しができません。

[[UIApplication sharedApplication] openUrl:url];

その代わりに extensionContext というプロパティーが用意されているのです。

これでウィジェットからアプリを起動する方法が分かりました。
ちなみに、アプリからウィジェットを開かせるということもできると良いのですが、現在はサポートされていないようです。

本体アプリとデータ共有

本体とウィジェットは別プロセスであり、データの保存領域も異なります。
そのため、データ共有について考える必要があります。

本体アプリとウィジェットのデータ保存領域は異なる

上図は本体アプリとウィジェット(App Extension 全てに言えることですが、説明のためにウィジェットとして進めます)が別プロセスで動き、別のデータ保存領域に保存される様子を表しています。

App Group でデータ共有

データ共有の仕組みは用意されており、shared container という領域を利用します。
この領域に本体とウィジェット両方からアクセスすることが可能です。

shared container は App Group により定義されており、group. から始まる文字列で指定します。
上図では group. という文字列の後に Bundle ID を加えて Group の ID としています。
下の3つの Group があるとすると、それぞれの ID に紐づく shared container が(合計3つ)生成されます。

  • group.jp.co.yahoo.search
  • group.jp.co.yahoo.weather
  • group.jp.co.yahoo.news

App Group を定義して、本体アプリ、ウィジェット間でデータ共有ができるのですが、
これはアプリ、ウィジェット間に限った話ではありません。
Developer が同じであれば他のアプリの shared container にもアクセスできるのです。
これによりアプリ間のデータ共有の幅が広がります。

次に実装の手順を説明しますが、ここでは実装方法のみ説明し、App Group の作成の仕方、Xcode 上での設定については触れません。割愛した部分はスライド(49~57枚目)で手順を説明しているので、そちらをご参照ください。
今回は NSUserDefaults の実装について説明します。NSUserDefaults の他にも CoreData や SQLite などの共有が可能です。NSUserDefaults の場合以下のようにイニシャライズします。

NSString* groupId = @"group.jp.co.yahoo.WidgetSample";
NSUserDefaults* userDefaults = [[NSUserDefaults alloc] initWithSuiteName:groupId];

いつもと違う点はイニシャライズメソッドが initWithSuiteName になっており、そのメソッドが App Group を引数にとるということです。
イニシャライズメソッドが異なる以外(データの読み書き)は通常の NSUserDefaults と変わりません。

以上で本体アプリとデータ共有の説明は終了です。

本体アプリとコード共有

コードに関しても共有が必要になります。本体アプリとウィジェットが別プロセスで動作しているためです。

コード共有のためには Embedded Framework を利用します。手順はスライドで説明しきれていないので、参考になるリンクを貼っておきます。

http://dev.classmethod.jp/references/ios-8-embedded-framework/

NG 事項

最後に NG 事項を紹介します。App Extension Programming Guide: Today に記載されている情報です。

以下の点が Apple が禁止している実装です。リンクの文中に書かれている通り、下記の点は推奨されていません。Human Interface Guidelines や App Review Guildelines に抵触する場合もありますので十分お気をつけ下さい。

  • キーボード使用
  • スクロールビューの配置
  • 高さの高すぎるコンテンツ

おわりに

以上で Today ウィジェットの開発の仕方についての説明を終わります。

皆さんのウィジェット開発のお役に立てればと思います。

長文にお付き合いいただきありがとうございました。

 

-------
Mac、iPad、iPhone、Xcode、OS Xは、Apple Inc.の米国およびその他の国々における登録商標です。
IOSの商標は、Ciscoの米国およびその他の国のライセンスに基づき使用されています。
iPhoneの商標は、アイホン株式会社のライセンスに基づき使用されています。 

Yahoo! JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。

  • このエントリーをはてなブックマークに追加