12 Today Extension(ウィジェット)
iOS 8から通知センターに Today Extension(ウィジェット)を設置できるようになりました。
実装は必須ではありませんが、ここでは、最新のお知らせタイトルを表示する方法についてご紹介します。
12.1 Extensionの作成
File > New > Target > iOS > Application Extension から、Today Extension を選択し、Extension を作成します。

12.2 App Groupの作成
アプリ本体と Today Extension でデータを共有するためには、App Group を作成することが必要です。


Bundle ID は、Xcode で Today Extension の TARGETS を作成した際のものにします。
そして、前項と同様に App Groups を有効化します。この時、iPhone アプリの App ID とは異なり、Push Notifications を有効にする必要はありません。
App ID の作成が完了したら、Today Extensionn のプロビジョニングプロファイルも Provisioning Profiles から作成し、ダウンロードします。
Today Extension に対して、ダウンロードしたプロビジョニングプロファイルを TARGETS > Build Settings > Code Signing > Provisioning Profile に設置してください。
すると、選択可能な Group ID が列挙されますので、上記で作成した Group ID にチェックを入れます。

Xcode 11 の Capabilities 設定について
Xcode 11 から Capabilities の操作方法が変更されています。
上部ペインの「+」ボタンをクリックするか、キーボードで Command + Shift + L を押すと、以下のウィンドウが現れますので、App Groups をクリックすれば、 Capabilities の一覧に項目が追加されます。

なお、App Groups をすでに有効にしている場合は、このウィンドウの一覧には表示されませんので、Capabilities の一覧をもう一度確認してください。
12.3 Today Extensionのコーディング
Extension を作成した際、Xcode のアプリプロジェクト上で、TodayViewController.h、TodayViewController.m、MainInterface.storyboard などが自動で生成されます。
MainInterface.storyboard において、最新のお知らせのタイトルと日付を表示するための UILabel を作成してください。
そして、TodayViewController.m の viewDidLoad 内に、以下を記述してください。
// 高さ変更
self.preferredContentSize = CGSizeMake(0, 100);
// 年月日ラベル用フォーマッタ
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
[formatter setCalendar:calendar];
[formatter setLocale:[NSLocale systemLocale]];
[formatter setTimeZone:[NSTimeZone systemTimeZone]];
[formatter setDateFormat:@"yyyy/MM/dd"];
// ラベルの更新
NSUserDefaults *ud = [[NSUserDefaults alloc] initWithSuiteName:@"App Group 名"];
if ([ud objectForKey:@"messageTitle"]) {
self.titleLabel.text = [ud objectForKey:@"massageTitle"];
self.timeLabel.text = [formatter stringFromDate:[ud objectForKey:@"messageTime"]];
} else {
self.titleLabel.text = @"お知らせはありません";
self.timeLabel.text = @"";
}
// 高さ変更
self.preferredContentSize = CGSize(width: 0, height: 100)
// 年月日ラベル用フォーマッタ
let formatter = DateFormatter()
let calendar = Calendar(identifier: .gregorian)
formatter.calendar = calendar
formatter.locale = NSLocale.system
formatter.timeZone = NSTimeZone.system
formatter.dateFormat = "yyyy/MM/dd"
// ラベルの更新
let ud = UserDefaults(suiteName: "App Group 名")
if ud != nil && ud?.object(forKey: "messageTitle") != nil {
self.titleLabel.text = ud!.object(forKey: "messageTitle")
self.timeLabel.text = formatter.string(from: ud!.object(forKey: "messageTime") as! Date)
} else {
self.titleLabel.text = "お知らせはありません"
self.timeLabel.text = ""
}
12.4 アプリ側のコーディング
AppDelegate.m において、お知らせを FANSHIP サーバーから取得した際にコールされる PopinfoReceiver デリゲートメソッド popinfoReceiver:updateMessagesResult: を実装します。
そのタイミングで、Today Extension とデータを共有できる領域に最新のお知らせを保存します。
- (void)popinfoReceiver:(PopinfoReceiver *)pr updateMessagesResult:(BOOL)result
{
// 通信に失敗した場合は return
if (!result) {
return;
}
// 最新のお知らせを取得する
PopinfoListViewController *mylistVC = [[PopinfoListViewController alloc] initWithNibName:@"PopinfoListViewController" bundle:[NSBundle mainBundle]];
NSArray *messages = [mylistVC retrievePopinfoMessages:@""];
if (!messages || [messages count] == 0) {
return;
}
PopinfoMessage *latestMessage = messages[0];
// Today Extension と連携するため、共有領域に最新のお知らせのタイトルと日時を保存する
NSUserDefaults *ud = [[NSUserDefaults alloc] initWithSuiteName:@"App Group 名"];
[ud setObject:latestMessage.piTitle forKey:@"massageTitle"];
[ud setObject:latestMessage.piTime forKey:@"massageTime"];
[ud synchronize];
}
func popinfoReceiver(_ popinfoReceiver: PopinfoReceiver, updateMessagesResult result: Bool) {
// 通信に失敗した場合は return
guard result else {
return
}
// 最新のお知らせを取得する
let myListVC = PopinfoListViewController(nibName: "PopinfoListViewController", bundle: Bundle.main)
let messages = myListVC.retrieveAllPopinfoMessages()
guard messages.count > 0 else {
return
}
let latestMessage = messages[0]
// Today Extension と連携するため、共有領域に最新のお知らせのタイトルと日時を保存する
guard let ud = UserDefaults(suiteName: "App Group 名") else {
return
}
ud.set(latestMessage.piTitle, forKey: "messageTitle")
ud.set(latestMessage.piTime, forKey: "messageTime")
}