Skip to content

12 Today Extension(ウィジェット)

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

12.1 Extensionの作成

File > New > Target > iOS > Application Extension から、Today Extension を選択し、Extension を作成します。
12-1.png

12.2 App Groupの作成

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

ブラウザから iOS Dev Center にログインし、App Groups から App Group を作成します。
12-2.png

App IDs から iPhone アプリを選択し、App Groups を有効化します。プロビジョニングプロファイルは再作成してください。
12-3.png

iOS Dev Center において、Today ExtensionのApp ID を作成します。(Xcode によって自動的に作られている場合もあります。)
Bundle ID は、Xcode で Today Extension の TARGETS を作成した際のものにします。
そして、前項と同様に App Groups を有効化します。この時、iPhone アプリの App ID とは異なり、Push Notifications を有効にする必要はありません。
App ID の作成が完了したら、Today Extensionn のプロビジョニングプロファイルも Provisioning Profiles から作成し、ダウンロードします。

ここからは Xcode 上の作業となります。
Today Extension に対して、ダウンロードしたプロビジョニングプロファイルを TARGETS > Build Settings > Code Signing > Provisioning Profile に設置してください。

iPhone アプリと Today Extension の TARGETS において、それぞれ Capabilities から App Groups を有効化します。
すると、選択可能な Group ID が列挙されますので、上記で作成した Group ID にチェックを入れます。

12-4.png

Xcode 11 の Capabilities 設定について

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

12-5.png

なお、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")
}