[Swift]iOS8から搭載されるウィジェット(Today Extension)の使い方

Swiftでウィジェットを作ってみた[iOS]
http://sakahara.hatenablog.jp/entry/2014/07/11/014033

上記のサイトを参考にさせていただきました。
自分なりに理解するのが難しかったところを噛み砕きながらまとめます。

#iOS端末上でウィジェットはどこに表示するの？
今回扱うものはToday Extensionというもの。
iOS端末の画面の上から下に引っぱると出てくるNotification CenterのToday（日本語だと「今日」）タブの中にウィジェットを作ります。
今回はやりませんが他にもアプリアイコンが並んでいる画面に表示するウィジェットもあるそうです。

#アプリとウィジェットで2つのターゲットを用意してそれらを連携させる
普段アプリを作るときは基本的にプロジェクトの下に一つのターゲットを用意してその下にソースファイルを生成してコードを書いていました。
ウィジェットを使う時はアプリ側のターゲットともう一つ、ウィジェット側もまた別ターゲットを用意してコードを書いていきます。
一つのプロジェクト内で2つのアプリを書いていくような形になります。

#実際に作ってみる
##1.プロジェクトを立ち上げる
Single View Applicationを選択してProject NameはTodayExtensionExampleとします。
LanguageはSwiftでDeviceはiPhoneを選択します。

##2.Today Extensionを新規ターゲットとして作る
File>New>Targetを押すとChoose a template for your new target:のウィンドウが出てきます。
iOSの中のApplication ExtensionのToday Extensionを選んでNextを押します。
するとプロジェクトを立ち上げたときと同じようなウィンドウに進むので、Product NameにはTodayExtensionと入力してFinishを押します。
Active "TodayExtension" scheme?というウィンドウが出てくるのでActiveを押してください。

##3.アプリとウィジェット間でのデータ共有
今回はApp Groupというものを使います。
xcodeの左側のファイルが並んでいるところの一番上、プロジェクト名であるTodayExtensionExampleという部分を選択します。
その中のTargetsというところからTodayExtensionExampleを選択し、General, Capabilities, Info, ...と並んでいるうちのCapabilitiesを選択します。
iCloudから始まりGame Center, Passbook, ...と続くテーブルが表示されるのでその中のApp GroupsをONにして、出てくるウィンドウには自分のProvisioningを選択してください。
App Groupsの中の+ボタンを押してAdd a new containerでgroup.TodayExtensionSampleと入力してOKを押します。
Targetsの中にある先ほど作ったTodayExtensionを選択して同様の手順を行えばデータ共有の用意は完了です。

この時、group.TodayExtensionSampleが赤字表示になっていたり、Stepsにエラーのようなものが出ていますが無視してください。

##4.アプリ側でウィジェットに渡す文字列を入力するUIを作る
今回はUITextFieldとUIButtonを使って、入力した文字をウィジェットに表示するということをやります。
ちなみにTodayExtensionExampleのフォルダ内がアプリ、TodayExtensionフォルダ内がウィジェット側の処理を書く場所です。
TodayExtensionExampleフォルダ内のMain.StoryBoardを開いてStoryBoard上の適当なところにUITextFieldとUIButtonを配置します。
次に、同じくTodayExtensionExampleフォルダ内のViewController.swiftをアシスタントエディタ上で開いてcontrol + ドラッグでStoryBoard上のUITextFieldをcommentTextFieldという変数名でIBOutletで紐付けします。

```
@IBOutlet var textLabel: UILabel!
```

StoryBoardの詳しい操作方法などに自信の無い方は調べて一度やってみてください。

ボタンの設定もします。
アプリ側のViewController.Swiftに

```
@IBAction func saveCommentAction(sender: AnyObject)
{
    let sharedDefaults:NSUserDefaults = NSUserDefaults(suiteName: "group.TodayExtensionSample")
    
    sharedDefaults.setObject(commentTextField.text, forKey: "textValue")
    sharedDefaults.synchronize()
}
```

というコードを書きます。
@IBActionの左側にある◯からドラッグ＆ドロップで先ほどStoryBoardに設置したボタンに引っぱってください。

これはボタンが押された時にcommentTextFieldの中の文字列をNSUserDefaultsでに保存しておくという処理を書いています。

##5.ウィジェットのUIを作る
アプリ側と同様にStoryBoard上にUILabelを設置してtextLabelという変数名でウィジェットのViewController.swiftに紐付けします。

```
@IBOutlet var textLabel: UILabel!
```

次に初期化処理。
初期化時にNSUserDefaultsDidChangeNotificationで変更を検知してuserDefaultsDidChangeメソッドを呼び出すようにします。

```
init(coder aDecoder: NSCoder!) {
    super.init(coder: aDecoder)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "userDefaultsDidChange:",
        name: NSUserDefaultsDidChangeNotification, object: nil)
}
```

ウィジェットを表示するための追加の処理をviewDidLoad()内に書きます。

```
override func viewDidLoad() {
    super.viewDidLoad()
    
    // Auto Layoutを使用しない場合、preferredContentSizeで高さを指定する
    preferredContentSize = CGSizeMake(320, 50)
    // テキスト表示
    updateTextLabel()
}
```

アプリ側でテキストが変更されたらウィジェットに反映する関数を用意します。

```
func userDefaultsDidChange(notification: NSNotification) {
    updateTextLabel()
}
```

アプリ側で保存したテキストを取得してラベルに表示する関数です。

```
func updateTextLabel() {
    let defaults:NSUserDefaults = NSUserDefaults(suiteName: "group.TodayExtensionSample")
    textLabel.text = defaults.stringForKey("textValue")
}
```

##6.完成
これを実行すれば動きます。
アプリのTextFieldに文字列を入力し、ボタンを押してiOS端末の上画面端を下に引っぱるとNotification Center内に入力した文字列のラベルを貼ったウィジェットが表示されているはずです。

#まとめ
普段作るようなアプリと違った手順をxcodeでしなくてはならないので少し戸惑うかもしれませんが一度出来てしまうと案外こんなものかという感じ。
NDAの関係上画像が貼れず、大分見づらい書き方になってしまったのが悔やまれます。
今回は文字列を扱いましたが、UIImageViewで画像を貼ることも出来るそうなのでそちらも試し次第投稿したいと思います。