October 31, 2014

Interactive Notification ใน iOS 8

iOS8 มาพร้อมกับฟีเจอร์หลายๆ อย่างที่น่าสนใจ หนึ่งในนั้นก็คือ Notification Actions

ฟีเจอร์นี้ทำให้ผู้ใช้งานสามารถ interact กับ remote หรือ local push notification ได้โดยไม่จำเป็นต้องเปิดแอป ซึ่งแต่เดิมเวลามี notifcation เข้ามาแล้วจะทำอะไร เราจะทำได้แค่อ่านอย่างเดียว

ตัวอย่างง่ายๆ ของการนำไปประยุกต์ใช้ก็เช่น แอปประเภท todo list ที่จะแจ้งเตือนเราว่าถึงเวลาทำ task นี้แล้วนะ พร้อมกับแสดงปุ่มบน notification เลยว่า "ทำแล้ว", "ยังไม่ทำ", "ยกเลิก"

แผนการณ์

ขั้นตอนวิธีการ implement interactive notification มี 4 ขั้นตอนหลัก ดังนี้

  1. กำหนด action button ที่จะใช้แสดงใน notification alert
  2. กำหนด category ให้กับ action button เอาไว้ให้แอปตัดสินใจได้ว่า ถ้ามี notifcation ที่ถูกกำหนด category แบบนี้มา จะใช้ปุ่มชุดไหนมาแสดง
  3. เก็บ category ลงไปใน notification setting ของแอป
  4. เขียนโค้ดส่วนที่จะมา handle กับ action button ที่ผู้ใช้เลือก
วิธีการทดลอง สามารถทำได้โดยการ post notification ที่กำหนด category id ให้ตรงกับ category ที่เราเตรียมไว้ใน notifcation setting

โจทย์

ต้องการให้แสดงปุ่มบน Notification Message ไว้ 3 ปุ่ม ว่า "ยกเลิก", "ทำแล้ว", "ทำกำลังทำ"

ลงมือทำ

เริ่มจากการสร้าง Action ขึ้นมา 3  อัน ดังนี้

// don Action
let doneIt = UIMutableUserNotificationAction()
doneIt.identifier = Actions.increment.rawValue
doneIt.title = "ทำแล้ว"
doneIt.activationMode = UIUserNotificationActivationMode.Background
doneIt.authenticationRequired = true
doneIt.destructive = false

// did't do it Action
let didtDoIt = UIMutableUserNotificationAction()
didtDoIt.identifier = Actions.decrement.rawValue
didtDoIt.title = "ยังไม่ทำ"
didtDoIt.activationMode = UIUserNotificationActivationMode.Background
didtDoIt.authenticationRequired = true
didtDoIt.destructive = false

// cancel Action
let cancelAction = UIMutableUserNotificationAction()
cancelAction.identifier = Actions.reset.rawValue
cancelAction.title = "ยกเลิก"
cancelAction.activationMode = UIUserNotificationActivationMode.Foreground

resetAction.destructive = true
  • identifier: เราไว้ช่วยให้เราสามารถระบุได้ว่า ผู้ใช้กดปุ่มไหน
  • activationMode: ถ้ากำหนดเป็น Background ก็คือเราสามารถทำ action นั้นๆ ได้เลยโดยที่ไม่จำเป็นต้องเปิดแอป แต่ถ้าเป็น Foreground เมื่อผู้ใช้เลือก action นั้นๆ แล้วแอปจะเปิดขึ้นมา
อ้างอิงจาก Document
@availability(iOS, introduced=8.0)
enum UIUserNotificationActivationMode : UInt {
    
    case Foreground // activates the application in the foreground
    case Background // activates the application in the background, unless it's already in the foreground

}
  • authenticationRequired: เป็นการกำหนดว่าจะทำ action นั้นๆ ผู้ใช้จำเป็นต้องใส่ passcode ก่อนหรือไม่ ปกติจะใช้เฉพาะกับ action แบบ Background activation mode (เพราะว่าแบบ Foreground นั่นผู้ใช้ก็กำลังเปิดแอปอยู่แล้ว)
  • destructive: กำหนดให้ปุ่ม action เป็นสีแดง
ขั้นตอนต่อไปเราจะจับ action ที่เราสร้างไว้มารวมกันอยู่ใน category แบบนี้

// Create category
let todoCategory = UIMutableUserNotificationCategory()
todoCategory.identifier"TODO_CATEGORY"

// A. Set actions for the default context
todoCategory.setActions([doneIt, didtDoIt, resetAction],
forContext: UIUserNotificationActionContext.Default)

// B. Set actions for the minimal context
todoCategory.setActions([doneIt, didtDoIt],
forContext: UIUserNotificationActionContext.Minimal)

เราจะสังเกตเห็นว่ามีการกำหนด action ให้ 2 ครั้ง ซึ่งแต่ละครั้งจะใช้ context ต่างกัน โดย context ที่ว่านี่ก็คือสถานที่ที่จะแสด notifcation ว่าจะแสดงแบบไหน ถ้าแสดงบน Default context ให้แสดงทั้ 3 ปุ่ม ส่วนถ้าแสดงบน Minimal context ให้แสดงแค่ 2 ปุ่มก็พอ

โดยที่ Default context จะเป็นการแสดง notification ในกรณีที่ผู้ใช้เลือกให้แสดงในรูปแบบ alert (กำหนดได้จากใน setting)
ตั้งค่าการแสดง notification แบบ Alerts


และ Minimal context จะเป็นการแสดง notification แบบ banner ด้านบน, หน้าจอ lock screen และใน notification center (ลากหน้าจอลงมาจากด้านบน)

มาถึงตรงนี้ เราได้ action ทั้ง 3 ตามต้องการ และยังกำหนดการแสดง notification ตาม context ที่ต้องการแล้ว ต่อไปก็ register setting ต่างๆ ที่ได้สร้างมาดังนี้

let types = UIUserNotificationType.Alert | UIUserNotificationType.Sound
let settings = UIUserNotificationSettings(forTypes: types, categories: NSSet(object: todoCategory))

UIApplication.sharedApplication().registerUserNotificationSettings(settings)

เรียบร้อยแล้ว :)

ทดสอบ Notification

หลังจากตั้งค่าเสร็จแล้ว ก็มาสร้าง notifcation กัน โดยในตัวอย่างนี้จะใช้ local notifcation ดังนี้

if UIApplication.sharedApplication().scheduledLocalNotifications.count == 0 {

    let notification = UILocalNotification()

    notification.alertBody = "ซักผ้า ล้างจาน ถูบ้าน"
    notification.soundName = UILocalNotificationDefaultSoundName
    notification.fireDate = NSDate()
    notification.category"TODO_CATEGORY"
    notification.repeatInterval = NSCalendarUnit.CalendarUnitMinute
    
    UIApplication.sharedApplication().scheduleLocalNotification(notification)

}

สังเกตุว่าเรากำหนดค่า category เป็น "TODO_CATEGORY" เพื่อให้ตรงกับ setting ด้านบนที่เราเตรียมไว้ เพื่อว่า เวลามี notification ตัวนี้มา เราจะได้เลือก setting ขึ้นมาใช้งานได้ถูกตัวนั่นเอง (สำหรับกรณีที่ 1 app มี notifcation มากกว่า 1 แบบ เราจะสามารถแยกแยะได้เลยว่า ถ้า notification แบบนี้มา จะแสดง notification category ไหนถึงจะเหมาะสม)

จากนั้นทดลองรันโปรแกรมดู ก็จะได้หน้าตาแบบนี้มา





จะเห็นว่าเราสามารถเลือก action ได้จากทั้งจากหน้าจอ lock screen และ notification center
และกรณีที่กำลังใช้ iPhone อยู่เมื่อมี notification banner แสดงขึ้นมา เราสามารถลากลงมาเพื่อดู action อื่นๆ ที่ซ่อนอยู่ได้ตามที่เราได้สร้างไว้ แบบนี้



และกรณีที่เลือกให้แสดง notification เป็น Alert ก็จะมีปุ่ม Options มาให้เลือก เมื่อเลือกแล้วก็จะแสดง action อื่นๆ ให้เลือกอีก แบบนี้



Handle Action

อ้างอิงจาก Document ของ UIApplication ดังนี้

    // Called when your app has been activated by the user selecting an action from a local notification.
    // A nil action identifier indicates the default action.
    // You should call the completion handler as soon as you've finished handling the action.
    @availability(iOS, introduced=8.0)

    optional func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forLocalNotification notification: UILocalNotification, completionHandler: () -> Void)

ดังนั้นเราสามารถตรวจจับเหตุการณ์ที่ user เลือกได้ดังนี้

func application(application: UIApplication,
    handleActionWithIdentifier identifier: String?,
    forLocalNotification notification: UILocalNotification,
    completionHandler: () -> Void) {
    
    
    if notification.category == "TODO_CATEGORY" {

    // Handle notification action

    }
    
    completionHandler()

}

เท่านี้เราก็จะได้ notifcation ที่มีปุ่มมาให้กดทำอะไรสักอย่างแล้ว

:)