NSUserNotificationを使って、通知のポップアップを表示する


# 概要
- 画面右上に出てくる通知画面を実装します。

![](https://i.imgur.com/IKOMtMb.jpg)

# 参考
- [User Notifications in macOS](https://blog.gaelfoppolo.com/user-notifications-in-macos-66c25ed5c692)
- [OS Xの通知センターに通知する方法](https://qiita.com/arthur87/items/39ce070ae41b8c012dc9)

# NSUserNotificationはmacOS 10.14から非推奨

- `NSUserNotification`はmacOS 10.14からdeprecatedです。
-  代替の`UserNotifications`は、macOS 10.14以上のみ対応。
-  そのためバージョンで切り分ける対応をするのが正しい対応ですね。（今回は触れませんが。）
    - [UserNotifications](https://developer.apple.com/documentation/usernotifications?language=objc)
    - [How to implement push notification for iOS 10\[Objective C\]?](https://stackoverflow.com/questions/39850603/how-to-implement-push-notification-for-ios-10objective-c)
    - [NSUserNotificationはMojaveで非推奨](https://forums.developer.apple.com/thread/107878)
     
# 実装
## コード全体

```objc
#import "AppDelegate.h"

@interface AppDelegate () <NSUserNotificationCenterDelegate>
@property (weak) IBOutlet NSWindow *window;
@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
    [self showNotification];    // 通知を表示する
}


- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
}

- (void)showNotification {
    NSUserNotification *userNotification = [[NSUserNotification alloc] init];
    userNotification.identifier      = [NSString stringWithFormat:@"IKE_%@", [self createNotificationId]];    // ユニークなidをつけるためPrefix+日付を採用
    userNotification.title           = @"タイトル";
    userNotification.subtitle        = @"サブタイトル";
    userNotification.informativeText = @"詳細";
    
    NSUserNotificationCenter *userNotificationCenter = [NSUserNotificationCenter defaultUserNotificationCenter];
    userNotificationCenter.delegate = self;
    [userNotificationCenter deliverNotification:userNotification];
}

/**
    @brief 現在日時の文字列を返す
    e.g. 20190930_093503
    ユニークなidをつけるためPrefix+日付を採用
*/
- (NSString *)createNotificationId {
    NSDate          *now           = [NSDate date];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    dateFormatter.dateFormat = @"yyyyMMdd_HHmmss";
    
    return [NSString stringWithFormat:@"IKE_%@", [dateFormatter stringFromDate:now]];
}


// MARK:- NSUserNotificationCenterDelegate Methods

// 通知ウィンドウを選択されたときに呼ばれる
- (void) userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification {
    NSLog(@"通知が選択されました。");
    [center removeDeliveredNotification: notification]; // 通知の削除（削除しないと通知センターに通知が残る）
    NSLog(@"通知を削除しました。");
}

// 通知を受け取ったときに呼ばれる
- (void) userNotificationCenter:(NSUserNotificationCenter *)center didDeliverNotification:(NSUserNotification *)notification {
    NSLog(@"通知を受け取りました。");
}

// 画面がアクティブ名祭に呼ばれる
// デフォルトでは`NO`を返し、アプリがアクティブな場合（前面）は通知を行わない設定になっている
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification {
    NSLog(@"画面がアクティブですが通知を表示します。");
    return YES;
}

@end
```

## コード詳細

```objc
@interface AppDelegate () <NSUserNotificationCenterDelegate>
```

通知関連のアクションを受け取るため、`NSUserNotificationCenterDelegate`を設定する。

```objc
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
userNotification.identifier      = [NSString stringWithFormat:@"IKE_%@", [self createNotificationId]];    // ユニークなidをつけるためPrefix+日付を採用
userNotification.title           = @"タイトル";
userNotification.subtitle        = @"サブタイトル";
userNotification.informativeText = @"詳細";
```

- 通知を作成する。その他の設定として、右側に画像の追加や、アクションボタンの追加等が行える
    - [User Notifications in macOS](https://blog.gaelfoppolo.com/user-notifications-in-macos-66c25ed5c692)を参考にすると良い。

```objc
NSUserNotificationCenter *userNotificationCenter = [NSUserNotificationCenter defaultUserNotificationCenter];
userNotificationCenter.delegate = self;
[userNotificationCenter deliverNotification:userNotification];
```

- 通知をPostする。
- Delegateを自身に設定することを忘れないように。

```objc
// MARK:- NSUserNotificationCenterDelegate Methods

// 通知ウィンドウを選択されたときに呼ばれる
- (void) userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification {
    NSLog(@"通知が選択されました。");
    [center removeDeliveredNotification: notification]; // 通知の削除（削除しないと通知センターに通知が残る）
    NSLog(@"通知を削除しました。");
}

// 通知を受け取ったときに呼ばれる
- (void) userNotificationCenter:(NSUserNotificationCenter *)center didDeliverNotification:(NSUserNotification *)notification {
    NSLog(@"通知を受け取りました。");
}

// 画面がアクティブ名祭に呼ばれる
// デフォルトでは`NO`を返し、アプリがアクティブな場合（前面）は通知を行わない設定になっている
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification {
    NSLog(@"画面がアクティブですが通知を表示します。");
    return YES;
}
```

- 呼ばれるタイミングはコメントの通り。

# 通知が表示されないときは

- デフォルトではアプリがアクティブな場合（前面にある場合）は通知は行われない設定になっています。
- アクティブ時も通知を表示したい場合は、下記を追加しで`YES`をreturnします。
    - 基本しないデザインの方いいですが、デバッグ時には便利ですね。

```objc
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification;
```