Copyright © Up-frontier, Inc. All rights reserved.
New Notification API

in iOS 10
1
Copyright © Up-frontier, Inc. All rights reserved.
アジェンダ
• 新 API でローカル通知
• Media Attachment
• Notification Content Extension
• Notification Service Extension
2
Copyright © Up-frontier, Inc. All rights reserved.
アジェンダ
• 新 API でローカル通知
• Media Attachment
• Notification Content Extension
• Notification Service Extension
3
Copyright © Up-frontier, Inc. All rights reserved.
ローカル通知⽅法
1. コンテンツを作って
2. トリガーを設定して
3. センターにリクエスト
4
Copyright © Up-frontier, Inc. All rights reserved.
ローカル通知⽅法
1. コンテンツを作って
2. トリガーを設定して
3. センターにリクエスト
5
Copyright © Up-frontier, Inc. All rights reserved.
コンテンツ
let content = UNMutableNotificationContent()
content.title = "Title"
content.subtitle = "Subtitle"
content.body = "Body"
content.categoryIdentifier = "sample-category"
6
Copyright © Up-frontier, Inc. All rights reserved.
コンテンツ
let content = UNMutableNotificationContent()
content.title = "Title"
content.subtitle = "Subtitle"
content.body = "Body"
content.categoryIdentifier = "sample-category"
7
Copyright © Up-frontier, Inc. All rights reserved.
コンテンツ
let content = UNMutableNotificationContent()
content.title = "Title"
content.subtitle = "Subtitle"
content.body = "Body"
content.categoryIdentifier = "sample-category"
8
Action や、カスタム UI など
識別に使⽤
Copyright © Up-frontier, Inc. All rights reserved.
ローカル通知⽅法
1. コンテンツを作って
2. トリガーを設定して
3. センターにリクエスト
9
Copyright © Up-frontier, Inc. All rights reserved.
トリガー
UNNotificationTrigger
10
Copyright © Up-frontier, Inc. All rights reserved.
トリガー
UNNotificationTrigger
11
let intervalTrigger = UNTimeIntervalNotificationTrigger(
timeInterval: 5, repeats: false)
Copyright © Up-frontier, Inc. All rights reserved.
リクエスト
let id = "sample-(Date().timeIntervalSince1970)"
let request = UNNotificationRequest(
identifier: id,
content: content,
trigger: intervalTrigger
)
center.add(request) { error in
if let error = error {
print("Error on requesting notification: (error)")
}
print("Finish requesting notification: (id)")
}
12
Copyright © Up-frontier, Inc. All rights reserved.
リクエスト
let id = "sample-(Date().timeIntervalSince1970)"
let request = UNNotificationRequest(
identifier: id,
content: content,
trigger: intervalTrigger
)
center.add(request) { error in
if let error = error {
print("Error on requesting notification: (error)")
}
print("Finish requesting notification: (id)")
}
13
トリガのセット
Copyright © Up-frontier, Inc. All rights reserved.
リクエスト
let id = "sample-(Date().timeIntervalSince1970)"
let request = UNNotificationRequest(
identifier: id,
content: content,
trigger: intervalTrigger
)
center.add(request) { error in
if let error = error {
print("Error on requesting notification: (error)")
}
print("Finish requesting notification: (id)")
}
14
identifier: 識別⼦、更新・削除に利⽤
contents: 通知本体
trigger: 発⽕タイミング
Copyright © Up-frontier, Inc. All rights reserved.
リクエスト
let id = "sample-(Date().timeIntervalSince1970)"
let request = UNNotificationRequest(
identifier: id,
content: content,
trigger: intervalTrigger
)
center.add(request) { error in
if let error = error {
print("Error on requesting notification: (error)")
}
print("Finish requesting notification: (id)")
}
15
Notification Centerに追加で完了
Copyright © Up-frontier, Inc. All rights reserved.
アジェンダ
• 新 API でローカル通知
• Media Attachment
• Notification Content Extension
• Notification Service Extension
16
Copyright © Up-frontier, Inc. All rights reserved.
Media Attachment
• 通知領域で様々なメディアファイルを表⽰する
17
Copyright © Up-frontier, Inc. All rights reserved.
メディア
18
画像

jpg, png, gif
⾳声

mp3, mp4
動画

mp4, avi

Copyright © Up-frontier, Inc. All rights reserved.
メディア
1. 通知内容を作って
2. トリガーを設定して
3. センターにリクエスト
19
Copyright © Up-frontier, Inc. All rights reserved.
メディア
1. 通知内容を作って
2. トリガーを設定して
3. センターにリクエスト
20
Copyright © Up-frontier, Inc. All rights reserved.
メディア
1. 通知内容を作って
2. トリガーを設定して
3. センターにリクエスト
21
ここで設定する
Copyright © Up-frontier, Inc. All rights reserved.
UNNotificationAttachment
guard let imageURL = R.file.sampleJpg() else {
print("Could not instantiate file URL!”)
return
}
let content = defaultContent
do {
let attachment = try UNNotificationAttachment(
identifier: "sample-cat",
url: imageURL,
options: nil
)
content.attachments = [attachment]
} catch(let error) {
print("Could not instantiate attachment: (error)")
}
22
Copyright © Up-frontier, Inc. All rights reserved.
UNNotificationAttachment
guard let imageURL = R.file.sampleJpg() else {
print("Could not instantiate file URL!”)
return
}
let content = defaultContent
do {
let attachment = try UNNotificationAttachment(
identifier: "sample-cat",
url: imageURL,
options: nil
)
content.attachments = [attachment]
} catch(let error) {
print("Could not instantiate attachment: (error)")
}
23
画像の設定
UNNotificationAttachmentを使う
Copyright © Up-frontier, Inc. All rights reserved.
UNNotificationAttachment
guard let imageURL = R.file.sampleJpg() else {
print("Could not instantiate file URL!”)
return
}
let content = defaultContent
do {
let attachment = try UNNotificationAttachment(
identifier: "sample-cat",
url: imageURL,
options: nil
)
content.attachments = [attachment]
} catch(let error) {
print("Could not instantiate attachment: (error)")
}
24
コンテンツに指定
Copyright © Up-frontier, Inc. All rights reserved.
アジェンダ
• 新 API でローカル通知
• Media Attachment
• Notification Content Extension
• Notification Service Extension
25
Copyright © Up-frontier, Inc. All rights reserved.
Notification Content Extension
• オリジナルUIの通知を作成する
26
Copyright © Up-frontier, Inc. All rights reserved.
ターゲット
• Notification Content Extension
27
Copyright © Up-frontier, Inc. All rights reserved.
カスタム UI
• Storyboard を使って UI を構築
28
Copyright © Up-frontier, Inc. All rights reserved.
NotificationViewController
class NotificationViewController
: UIViewController
, UNNotificationContentExtension {
@IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
}
func didReceive(_ notification: UNNotification) {
let content = notification.request.content
if
let lat = content.userInfo["latitude"] as? Double,
let lon = content.userInfo["longitude"] as? Double
{
putPin(into: CLLocationCoordinate2D(latitude: lat, longitude:
lon))
}
}
}
29
Copyright © Up-frontier, Inc. All rights reserved.
NotificationViewController
class NotificationViewController
: UIViewController
, UNNotificationContentExtension {
@IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
}
func didReceive(_ notification: UNNotification) {
let content = notification.request.content
if
let lat = content.userInfo["latitude"] as? Double,
let lon = content.userInfo["longitude"] as? Double
{
putPin(into: CLLocationCoordinate2D(latitude: lat, longitude:
lon))
}
}
}
30
MapViewを持った通知
Copyright © Up-frontier, Inc. All rights reserved.
NotificationViewController
class NotificationViewController
: UIViewController
, UNNotificationContentExtension {
@IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
}
func didReceive(_ notification: UNNotification) {
let content = notification.request.content
if
let lat = content.userInfo["latitude"] as? Double,
let lon = content.userInfo["longitude"] as? Double
{
putPin(into: CLLocationCoordinate2D(latitude: lat, longitude:
lon))
}
}
}
31
通知が発⽕した時の処理
Copyright © Up-frontier, Inc. All rights reserved.
NotificationViewController
class NotificationViewController
: UIViewController
, UNNotificationContentExtension {
@IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
}
func didReceive(_ notification: UNNotification) {
let content = notification.request.content
if
let lat = content.userInfo["latitude"] as? Double,
let lon = content.userInfo["longitude"] as? Double
{
putPin(into: CLLocationCoordinate2D(latitude: lat, longitude:
lon))
}
}
}
32
通知が発⽕した時の処理
通知内容を反映
Copyright © Up-frontier, Inc. All rights reserved.
Info.plist
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>UNNotificationExtensionCategory</key>
<array>
<string>customUI</string>
</array>
<key>UNNotificationExtensionInitialContentSizeRatio</key>
<real>1</real>
<key>UNNotificationExtensionDefaultContentHidden</key>
<true/>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.content-extension</
string>
</dict> 33
Copyright © Up-frontier, Inc. All rights reserved.
Info.plist
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>UNNotificationExtensionCategory</key>
<array>
<string>customUI</string>
</array>
<key>UNNotificationExtensionInitialContentSizeRatio</key>
<real>1</real>
<key>UNNotificationExtensionDefaultContentHidden</key>
<true/>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.content-extension</
string>
</dict> 34
categoryIdentifierと
⼀致している必要あり
Copyright © Up-frontier, Inc. All rights reserved.
Info.plist
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>UNNotificationExtensionCategory</key>
<array>
<string>customUI</string>
</array>
<key>UNNotificationExtensionInitialContentSizeRatio</key>
<real>1</real>
<key>UNNotificationExtensionDefaultContentHidden</key>
<true/>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.content-extension</
string>
</dict> 35
UIの縦横⽐
Copyright © Up-frontier, Inc. All rights reserved.
Info.plist
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>UNNotificationExtensionCategory</key>
<array>
<string>customUI</string>
</array>
<key>UNNotificationExtensionInitialContentSizeRatio</key>
<real>1</real>
<key>UNNotificationExtensionDefaultContentHidden</key>
<true/>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.content-extension</
string>
</dict> 36
デフォルトのタイトル、サブタイ
トル、コンテンツ
Copyright © Up-frontier, Inc. All rights reserved.
アジェンダ
• 新 API でローカル通知
• Media Attachment
• Notification Content Extension
• Notification Service Extension
37
Copyright © Up-frontier, Inc. All rights reserved.
Notification Service Extension
• オリジナルUIの通知を作成する
38
Copyright © Up-frontier, Inc. All rights reserved.
ターゲット
• Notification Service Extension
39
Copyright © Up-frontier, Inc. All rights reserved.
NotificationService
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler:
@escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
bestAttemptContent.title = "(bestAttemptContent.title) [modified]"
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content,
// otherwise the original push payload will be used.
if
let contentHandler = contentHandler,
let bestAttemptContent = bestAttemptContent {
bestAttemptContent.title = "(bestAttemptContent.title) [Time Expired]"
contentHandler(bestAttemptContent)
}
}
}
40
Copyright © Up-frontier, Inc. All rights reserved.
NotificationService
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler:
@escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
bestAttemptContent.title = "(bestAttemptContent.title) [modified]"
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content,
// otherwise the original push payload will be used.
if
let contentHandler = contentHandler,
let bestAttemptContent = bestAttemptContent {
bestAttemptContent.title = "(bestAttemptContent.title) [Time Expired]"
contentHandler(bestAttemptContent)
}
}
}
41
通知内容を編集
Copyright © Up-frontier, Inc. All rights reserved.
NotificationService
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler:
@escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
bestAttemptContent.title = "(bestAttemptContent.title) [modified]"
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content,
// otherwise the original push payload will be used.
if
let contentHandler = contentHandler,
let bestAttemptContent = bestAttemptContent {
bestAttemptContent.title = "(bestAttemptContent.title) [Time Expired]"
contentHandler(bestAttemptContent)
}
}
}
42
処理が既定秒数(MAX30秒)を超えると呼ばれる
Copyright © Up-frontier, Inc. All rights reserved.
NotificationService
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler:
@escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
bestAttemptContent.title = "(bestAttemptContent.title) [modified]"
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content,
// otherwise the original push payload will be used.
if
let contentHandler = contentHandler,
let bestAttemptContent = bestAttemptContent {
bestAttemptContent.title = "(bestAttemptContent.title) [Time Expired]"
contentHandler(bestAttemptContent)
}
}
}
43
サーバからのリモートプッシュを変更する場合
mutable-contetの設定忘れないように
Copyright © Up-frontier, Inc. All rights reserved.
NotificationService
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler:
@escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
bestAttemptContent.title = "(bestAttemptContent.title) [modified]"
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content,
// otherwise the original push payload will be used.
if
let contentHandler = contentHandler,
let bestAttemptContent = bestAttemptContent {
bestAttemptContent.title = "(bestAttemptContent.title) [Time Expired]"
contentHandler(bestAttemptContent)
}
}
}
44
処理が終わったら速やかに呼ぶ
Copyright © Up-frontier, Inc. All rights reserved.
ありそうな使い⽅
• ペイロードにサーバにあるメディアの URL
• ダウンロードして
• Media Attachment
45
Copyright © Up-frontier, Inc. All rights reserved.
リモートコンテンツ
貼り付け
if let bestAttemptContent = bestAttemptContent {
guard
let urlString = bestAttemptContent.userInfo["remote-url"] as? String,
let remoteURL = URL(string: urlString)
else {
contentHandler(bestAttemptContent)
return
}
download(remoteURL) { url, error in
guard let fileURL = url else {
print(error)
contentHandler(bestAttemptContent)
return
}
do {
let attachment = try UNNotificationAttachment(
identifier: "remote", url: fileURL
)
bestAttemptContent.attachments = [attachment]
contentHandler(bestAttemptContent)
} catch(let e) {
print(e)
contentHandler(bestAttemptContent)
}
}
}
46
Copyright © Up-frontier, Inc. All rights reserved.
リモートコンテンツ
貼り付け
if let bestAttemptContent = bestAttemptContent {
guard
let urlString = bestAttemptContent.userInfo["remote-url"] as? String,
let remoteURL = URL(string: urlString)
else {
contentHandler(bestAttemptContent)
return
}
download(remoteURL) { url, error in
guard let fileURL = url else {
print(error)
contentHandler(bestAttemptContent)
return
}
do {
let attachment = try UNNotificationAttachment(
identifier: "remote", url: fileURL
)
bestAttemptContent.attachments = [attachment]
contentHandler(bestAttemptContent)
} catch(let e) {
print(e)
contentHandler(bestAttemptContent)
}
}
}
47
画像のダウンロード
30秒超えるかも
Copyright © Up-frontier, Inc. All rights reserved.
serviceExtensionTimeWillExpire
override func serviceExtensionTimeWillExpire() {
if
let contentHandler = contentHandler,
let bestAttemptContent = bestAttemptContent
{
bestAttemptContent.title += " [頑張ったけれどダメだっ
たよ]"
contentHandler(bestAttemptContent)
}
}
48
Copyright © Up-frontier, Inc. All rights reserved.
serviceExtensionTimeWillExpire
override func serviceExtensionTimeWillExpire() {
if
let contentHandler = contentHandler,
let bestAttemptContent = bestAttemptContent
{
bestAttemptContent.title += " [頑張ったけれどダメだっ
たよ]"
contentHandler(bestAttemptContent)
}
}
49
サーバからのリモートプッシュの内容を
変更しない場合
もとのペイロードのまま表⽰されてしまうので注意
Copyright © Up-frontier, Inc. All rights reserved.
まとめ
• 新 API でローカル通知
• Media Attachment
• Notification Content Extension
• Notification Service Extension
50

New Notification API in iOS 10

  • 1.
    Copyright © Up-frontier,Inc. All rights reserved. New Notification API
 in iOS 10 1
  • 2.
    Copyright © Up-frontier,Inc. All rights reserved. アジェンダ • 新 API でローカル通知 • Media Attachment • Notification Content Extension • Notification Service Extension 2
  • 3.
    Copyright © Up-frontier,Inc. All rights reserved. アジェンダ • 新 API でローカル通知 • Media Attachment • Notification Content Extension • Notification Service Extension 3
  • 4.
    Copyright © Up-frontier,Inc. All rights reserved. ローカル通知⽅法 1. コンテンツを作って 2. トリガーを設定して 3. センターにリクエスト 4
  • 5.
    Copyright © Up-frontier,Inc. All rights reserved. ローカル通知⽅法 1. コンテンツを作って 2. トリガーを設定して 3. センターにリクエスト 5
  • 6.
    Copyright © Up-frontier,Inc. All rights reserved. コンテンツ let content = UNMutableNotificationContent() content.title = "Title" content.subtitle = "Subtitle" content.body = "Body" content.categoryIdentifier = "sample-category" 6
  • 7.
    Copyright © Up-frontier,Inc. All rights reserved. コンテンツ let content = UNMutableNotificationContent() content.title = "Title" content.subtitle = "Subtitle" content.body = "Body" content.categoryIdentifier = "sample-category" 7
  • 8.
    Copyright © Up-frontier,Inc. All rights reserved. コンテンツ let content = UNMutableNotificationContent() content.title = "Title" content.subtitle = "Subtitle" content.body = "Body" content.categoryIdentifier = "sample-category" 8 Action や、カスタム UI など 識別に使⽤
  • 9.
    Copyright © Up-frontier,Inc. All rights reserved. ローカル通知⽅法 1. コンテンツを作って 2. トリガーを設定して 3. センターにリクエスト 9
  • 10.
    Copyright © Up-frontier,Inc. All rights reserved. トリガー UNNotificationTrigger 10
  • 11.
    Copyright © Up-frontier,Inc. All rights reserved. トリガー UNNotificationTrigger 11 let intervalTrigger = UNTimeIntervalNotificationTrigger( timeInterval: 5, repeats: false)
  • 12.
    Copyright © Up-frontier,Inc. All rights reserved. リクエスト let id = "sample-(Date().timeIntervalSince1970)" let request = UNNotificationRequest( identifier: id, content: content, trigger: intervalTrigger ) center.add(request) { error in if let error = error { print("Error on requesting notification: (error)") } print("Finish requesting notification: (id)") } 12
  • 13.
    Copyright © Up-frontier,Inc. All rights reserved. リクエスト let id = "sample-(Date().timeIntervalSince1970)" let request = UNNotificationRequest( identifier: id, content: content, trigger: intervalTrigger ) center.add(request) { error in if let error = error { print("Error on requesting notification: (error)") } print("Finish requesting notification: (id)") } 13 トリガのセット
  • 14.
    Copyright © Up-frontier,Inc. All rights reserved. リクエスト let id = "sample-(Date().timeIntervalSince1970)" let request = UNNotificationRequest( identifier: id, content: content, trigger: intervalTrigger ) center.add(request) { error in if let error = error { print("Error on requesting notification: (error)") } print("Finish requesting notification: (id)") } 14 identifier: 識別⼦、更新・削除に利⽤ contents: 通知本体 trigger: 発⽕タイミング
  • 15.
    Copyright © Up-frontier,Inc. All rights reserved. リクエスト let id = "sample-(Date().timeIntervalSince1970)" let request = UNNotificationRequest( identifier: id, content: content, trigger: intervalTrigger ) center.add(request) { error in if let error = error { print("Error on requesting notification: (error)") } print("Finish requesting notification: (id)") } 15 Notification Centerに追加で完了
  • 16.
    Copyright © Up-frontier,Inc. All rights reserved. アジェンダ • 新 API でローカル通知 • Media Attachment • Notification Content Extension • Notification Service Extension 16
  • 17.
    Copyright © Up-frontier,Inc. All rights reserved. Media Attachment • 通知領域で様々なメディアファイルを表⽰する 17
  • 18.
    Copyright © Up-frontier,Inc. All rights reserved. メディア 18 画像
 jpg, png, gif ⾳声
 mp3, mp4 動画
 mp4, avi

  • 19.
    Copyright © Up-frontier,Inc. All rights reserved. メディア 1. 通知内容を作って 2. トリガーを設定して 3. センターにリクエスト 19
  • 20.
    Copyright © Up-frontier,Inc. All rights reserved. メディア 1. 通知内容を作って 2. トリガーを設定して 3. センターにリクエスト 20
  • 21.
    Copyright © Up-frontier,Inc. All rights reserved. メディア 1. 通知内容を作って 2. トリガーを設定して 3. センターにリクエスト 21 ここで設定する
  • 22.
    Copyright © Up-frontier,Inc. All rights reserved. UNNotificationAttachment guard let imageURL = R.file.sampleJpg() else { print("Could not instantiate file URL!”) return } let content = defaultContent do { let attachment = try UNNotificationAttachment( identifier: "sample-cat", url: imageURL, options: nil ) content.attachments = [attachment] } catch(let error) { print("Could not instantiate attachment: (error)") } 22
  • 23.
    Copyright © Up-frontier,Inc. All rights reserved. UNNotificationAttachment guard let imageURL = R.file.sampleJpg() else { print("Could not instantiate file URL!”) return } let content = defaultContent do { let attachment = try UNNotificationAttachment( identifier: "sample-cat", url: imageURL, options: nil ) content.attachments = [attachment] } catch(let error) { print("Could not instantiate attachment: (error)") } 23 画像の設定 UNNotificationAttachmentを使う
  • 24.
    Copyright © Up-frontier,Inc. All rights reserved. UNNotificationAttachment guard let imageURL = R.file.sampleJpg() else { print("Could not instantiate file URL!”) return } let content = defaultContent do { let attachment = try UNNotificationAttachment( identifier: "sample-cat", url: imageURL, options: nil ) content.attachments = [attachment] } catch(let error) { print("Could not instantiate attachment: (error)") } 24 コンテンツに指定
  • 25.
    Copyright © Up-frontier,Inc. All rights reserved. アジェンダ • 新 API でローカル通知 • Media Attachment • Notification Content Extension • Notification Service Extension 25
  • 26.
    Copyright © Up-frontier,Inc. All rights reserved. Notification Content Extension • オリジナルUIの通知を作成する 26
  • 27.
    Copyright © Up-frontier,Inc. All rights reserved. ターゲット • Notification Content Extension 27
  • 28.
    Copyright © Up-frontier,Inc. All rights reserved. カスタム UI • Storyboard を使って UI を構築 28
  • 29.
    Copyright © Up-frontier,Inc. All rights reserved. NotificationViewController class NotificationViewController : UIViewController , UNNotificationContentExtension { @IBOutlet weak var mapView: MKMapView! override func viewDidLoad() { super.viewDidLoad() } func didReceive(_ notification: UNNotification) { let content = notification.request.content if let lat = content.userInfo["latitude"] as? Double, let lon = content.userInfo["longitude"] as? Double { putPin(into: CLLocationCoordinate2D(latitude: lat, longitude: lon)) } } } 29
  • 30.
    Copyright © Up-frontier,Inc. All rights reserved. NotificationViewController class NotificationViewController : UIViewController , UNNotificationContentExtension { @IBOutlet weak var mapView: MKMapView! override func viewDidLoad() { super.viewDidLoad() } func didReceive(_ notification: UNNotification) { let content = notification.request.content if let lat = content.userInfo["latitude"] as? Double, let lon = content.userInfo["longitude"] as? Double { putPin(into: CLLocationCoordinate2D(latitude: lat, longitude: lon)) } } } 30 MapViewを持った通知
  • 31.
    Copyright © Up-frontier,Inc. All rights reserved. NotificationViewController class NotificationViewController : UIViewController , UNNotificationContentExtension { @IBOutlet weak var mapView: MKMapView! override func viewDidLoad() { super.viewDidLoad() } func didReceive(_ notification: UNNotification) { let content = notification.request.content if let lat = content.userInfo["latitude"] as? Double, let lon = content.userInfo["longitude"] as? Double { putPin(into: CLLocationCoordinate2D(latitude: lat, longitude: lon)) } } } 31 通知が発⽕した時の処理
  • 32.
    Copyright © Up-frontier,Inc. All rights reserved. NotificationViewController class NotificationViewController : UIViewController , UNNotificationContentExtension { @IBOutlet weak var mapView: MKMapView! override func viewDidLoad() { super.viewDidLoad() } func didReceive(_ notification: UNNotification) { let content = notification.request.content if let lat = content.userInfo["latitude"] as? Double, let lon = content.userInfo["longitude"] as? Double { putPin(into: CLLocationCoordinate2D(latitude: lat, longitude: lon)) } } } 32 通知が発⽕した時の処理 通知内容を反映
  • 33.
    Copyright © Up-frontier,Inc. All rights reserved. Info.plist <dict> <key>NSExtensionAttributes</key> <dict> <key>UNNotificationExtensionCategory</key> <array> <string>customUI</string> </array> <key>UNNotificationExtensionInitialContentSizeRatio</key> <real>1</real> <key>UNNotificationExtensionDefaultContentHidden</key> <true/> </dict> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> <key>NSExtensionPointIdentifier</key> <string>com.apple.usernotifications.content-extension</ string> </dict> 33
  • 34.
    Copyright © Up-frontier,Inc. All rights reserved. Info.plist <dict> <key>NSExtensionAttributes</key> <dict> <key>UNNotificationExtensionCategory</key> <array> <string>customUI</string> </array> <key>UNNotificationExtensionInitialContentSizeRatio</key> <real>1</real> <key>UNNotificationExtensionDefaultContentHidden</key> <true/> </dict> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> <key>NSExtensionPointIdentifier</key> <string>com.apple.usernotifications.content-extension</ string> </dict> 34 categoryIdentifierと ⼀致している必要あり
  • 35.
    Copyright © Up-frontier,Inc. All rights reserved. Info.plist <dict> <key>NSExtensionAttributes</key> <dict> <key>UNNotificationExtensionCategory</key> <array> <string>customUI</string> </array> <key>UNNotificationExtensionInitialContentSizeRatio</key> <real>1</real> <key>UNNotificationExtensionDefaultContentHidden</key> <true/> </dict> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> <key>NSExtensionPointIdentifier</key> <string>com.apple.usernotifications.content-extension</ string> </dict> 35 UIの縦横⽐
  • 36.
    Copyright © Up-frontier,Inc. All rights reserved. Info.plist <dict> <key>NSExtensionAttributes</key> <dict> <key>UNNotificationExtensionCategory</key> <array> <string>customUI</string> </array> <key>UNNotificationExtensionInitialContentSizeRatio</key> <real>1</real> <key>UNNotificationExtensionDefaultContentHidden</key> <true/> </dict> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> <key>NSExtensionPointIdentifier</key> <string>com.apple.usernotifications.content-extension</ string> </dict> 36 デフォルトのタイトル、サブタイ トル、コンテンツ
  • 37.
    Copyright © Up-frontier,Inc. All rights reserved. アジェンダ • 新 API でローカル通知 • Media Attachment • Notification Content Extension • Notification Service Extension 37
  • 38.
    Copyright © Up-frontier,Inc. All rights reserved. Notification Service Extension • オリジナルUIの通知を作成する 38
  • 39.
    Copyright © Up-frontier,Inc. All rights reserved. ターゲット • Notification Service Extension 39
  • 40.
    Copyright © Up-frontier,Inc. All rights reserved. NotificationService class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) if let bestAttemptContent = bestAttemptContent { // Modify the notification content here... bestAttemptContent.title = "(bestAttemptContent.title) [modified]" contentHandler(bestAttemptContent) } } override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, // otherwise the original push payload will be used. if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { bestAttemptContent.title = "(bestAttemptContent.title) [Time Expired]" contentHandler(bestAttemptContent) } } } 40
  • 41.
    Copyright © Up-frontier,Inc. All rights reserved. NotificationService class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) if let bestAttemptContent = bestAttemptContent { // Modify the notification content here... bestAttemptContent.title = "(bestAttemptContent.title) [modified]" contentHandler(bestAttemptContent) } } override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, // otherwise the original push payload will be used. if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { bestAttemptContent.title = "(bestAttemptContent.title) [Time Expired]" contentHandler(bestAttemptContent) } } } 41 通知内容を編集
  • 42.
    Copyright © Up-frontier,Inc. All rights reserved. NotificationService class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) if let bestAttemptContent = bestAttemptContent { // Modify the notification content here... bestAttemptContent.title = "(bestAttemptContent.title) [modified]" contentHandler(bestAttemptContent) } } override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, // otherwise the original push payload will be used. if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { bestAttemptContent.title = "(bestAttemptContent.title) [Time Expired]" contentHandler(bestAttemptContent) } } } 42 処理が既定秒数(MAX30秒)を超えると呼ばれる
  • 43.
    Copyright © Up-frontier,Inc. All rights reserved. NotificationService class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) if let bestAttemptContent = bestAttemptContent { // Modify the notification content here... bestAttemptContent.title = "(bestAttemptContent.title) [modified]" contentHandler(bestAttemptContent) } } override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, // otherwise the original push payload will be used. if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { bestAttemptContent.title = "(bestAttemptContent.title) [Time Expired]" contentHandler(bestAttemptContent) } } } 43 サーバからのリモートプッシュを変更する場合 mutable-contetの設定忘れないように
  • 44.
    Copyright © Up-frontier,Inc. All rights reserved. NotificationService class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) if let bestAttemptContent = bestAttemptContent { // Modify the notification content here... bestAttemptContent.title = "(bestAttemptContent.title) [modified]" contentHandler(bestAttemptContent) } } override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, // otherwise the original push payload will be used. if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { bestAttemptContent.title = "(bestAttemptContent.title) [Time Expired]" contentHandler(bestAttemptContent) } } } 44 処理が終わったら速やかに呼ぶ
  • 45.
    Copyright © Up-frontier,Inc. All rights reserved. ありそうな使い⽅ • ペイロードにサーバにあるメディアの URL • ダウンロードして • Media Attachment 45
  • 46.
    Copyright © Up-frontier,Inc. All rights reserved. リモートコンテンツ 貼り付け if let bestAttemptContent = bestAttemptContent { guard let urlString = bestAttemptContent.userInfo["remote-url"] as? String, let remoteURL = URL(string: urlString) else { contentHandler(bestAttemptContent) return } download(remoteURL) { url, error in guard let fileURL = url else { print(error) contentHandler(bestAttemptContent) return } do { let attachment = try UNNotificationAttachment( identifier: "remote", url: fileURL ) bestAttemptContent.attachments = [attachment] contentHandler(bestAttemptContent) } catch(let e) { print(e) contentHandler(bestAttemptContent) } } } 46
  • 47.
    Copyright © Up-frontier,Inc. All rights reserved. リモートコンテンツ 貼り付け if let bestAttemptContent = bestAttemptContent { guard let urlString = bestAttemptContent.userInfo["remote-url"] as? String, let remoteURL = URL(string: urlString) else { contentHandler(bestAttemptContent) return } download(remoteURL) { url, error in guard let fileURL = url else { print(error) contentHandler(bestAttemptContent) return } do { let attachment = try UNNotificationAttachment( identifier: "remote", url: fileURL ) bestAttemptContent.attachments = [attachment] contentHandler(bestAttemptContent) } catch(let e) { print(e) contentHandler(bestAttemptContent) } } } 47 画像のダウンロード 30秒超えるかも
  • 48.
    Copyright © Up-frontier,Inc. All rights reserved. serviceExtensionTimeWillExpire override func serviceExtensionTimeWillExpire() { if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { bestAttemptContent.title += " [頑張ったけれどダメだっ たよ]" contentHandler(bestAttemptContent) } } 48
  • 49.
    Copyright © Up-frontier,Inc. All rights reserved. serviceExtensionTimeWillExpire override func serviceExtensionTimeWillExpire() { if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { bestAttemptContent.title += " [頑張ったけれどダメだっ たよ]" contentHandler(bestAttemptContent) } } 49 サーバからのリモートプッシュの内容を 変更しない場合 もとのペイロードのまま表⽰されてしまうので注意
  • 50.
    Copyright © Up-frontier,Inc. All rights reserved. まとめ • 新 API でローカル通知 • Media Attachment • Notification Content Extension • Notification Service Extension 50