Notification Payload คือโครงสร้าง JSON ที่เซิร์ฟเวอร์ส่งผ่าน APNS ไปยังอุปกรณ์ iOS เพื่อกำหนดเนื้อหาของการแจ้งเตือน push และพฤติกรรมเมื่อได้รับ Payload ประกอบด้วยคีย์ที่จำเป็นและเลือกได้ซึ่งควบคุมข้อความ เสียง เบจ สื่อแนบและการประมวลผลพื้นหลัง ตาม Apple Developer Documentation, 2026 ขนาด payload สูงสุด คือ 4096 ไบต์สำหรับการแจ้งเตือนทั่วไปและ 5120 ไบต์สำหรับ VoIP push ซึ่งกำหนดข้อจำกัดที่เข้มงวดต่อปริมาณข้อมูลที่โอนเทรน
ข้อสำคัญ
Notification Payload คือวัตถุ JSON ที่เซิร์ฟเวอร์ส่งไปยัง APNS (Apple Push Notification Service) เพื่อจัดส่งไปยังอุปกรณ์ iOS Payload ประกอบด้วยข้อมูลทั้งหมดที่ระบบต้องการเพื่อแสดงการแจ้งเตือน: ชื่อเรื่อง ข้อความ เสียง เบจ และข้อมูลเมตาสำหรับการประมวลผลพื้นหลัง โครงสร้าง payload ถูกควบคุมอย่างเข้มงวดโดย Apple และรวมคีย์ที่จำเป็นสำหรับการประมวลผลที่ถูกต้องโดยระบบ
เมื่อเซิร์ฟเวอร์ส่งการแจ้งเตือน push ผ่าน APNS HTTP/2 API คำขอจะมีส่วนหัวการอนุญาตและเนื้อหา JSON — payload APNS ตรวจสอบความถูกต้อง ของ payload: ถ้า JSON ไม่ถูกต้องหรือเกินขีดจำกัดขนาด เซิร์ฟเวอร์ Apple จะส่งคืนข้อผิดพลาด 400 Bad Request หลังจากการตรวจสอบ APNS จะจัดส่ง payload ไปยังอุปกรณ์ ที่ iOS จะแยกวิเคราะห์และกำหนดวิธีจัดการกับการแจ้งเตือน — แสดงแบนเนอร์ เรียกใช้งานพื้นหลัง หรือเล่นเสียง
รูปแบบ payload APNS ได้พัฒนาจาก payload ข้อความง่ายๆ ใน iOS 2 ไปเป็นโครงสร้าง JSON หลายองค์ประกอบในเวอร์ชันที่ทันสมัย iOS 10 นำเสนอการรองรับสื่อแนบผ่าน mutable-content iOS 12 เพิ่มการจัดกลุ่มการแจ้งเตือนผ่าน thread-id และ iOS 15 นำเสนอ supports-live-activities สำหรับ Live Activities ปัจจุบัน payload สามารถมีได้ถึง 15 คีย์ที่แตกต่างกันขึ้นอยู่กับพฤติกรรมการแจ้งเตือนที่ต้องการ
วัตถุราก ของ payload ประกอบด้วยพจนานุกรม aps และฟิลด์แบบกำหนดเองที่เลือกได้ในระดับบนสุด พจนานุกรม aps เป็นองค์ประกอบที่จำเป็นเพียงอย่างเดียว แต่ภายในนั้นสามารถมีชุดค่าผสมของคีย์ต่างๆ ขึ้นอยู่กับประเภทการแจ้งเตือน: alert, badge, sound, content-available, mutable-content, interruption-level และอื่นๆ
| คีย์ aps | ชนิด | วัตถุประสงค์ |
|---|---|---|
| alert | String หรือ Dictionary | ข้อความการแจ้งเตือนหรือวัตถุที่มี title, subtitle, body และการแปลภาษา |
| badge | Number | ตัวเลขบนไอคอนแอป; 0 ลบเบจ |
| sound | String | ชื่อไฟล์เสียงหรือ default สำหรับเสียงระบบ |
| content-available | Number (1) | แฟลกการเรียกใช้งานพื้นหลัง; 1 = silent push |
| mutable-content | Number (1) | แฟลกการเรียกใช้งาน Service Extension เพื่อแก้ไขเนื้อหา |
| category | String | ตัวระบุหมวดหมู่สำหรับปุ่มและ Content Extension |
| thread-id | String | ตัวระบุกลุ่มสำหรับการจัดกลุ่มการแจ้งเตือน |
| interruption-level | String | ระดับการขัดจังหวะ: passive, active, time-sensitive, critical |
| relevance-score | Number (0–1) | ลำดับความสำคัญของการแจ้งเตือนสำหรับระบบการจัดอันดับอัจฉริยะ |
คีย์ alert สามารถเป็นสตริงที่ง่าย (ซึ่งกลายเป็นเนื้อหาของการแจ้งเตือน) หรือพจนานุกรมที่มีฟิลด์ title, subtitle และ body รูปแบบพจนานุกรม ช่วยให้สามารถตั้งชื่อเรื่องและชื่อเรื่องย่อยแยกจากข้อความหลัก สำหรับการแจ้งเตือนที่แปลภาษา จะใช้คีย์ title-loc-key, title-loc-args, loc-key และ loc-args ซึ่งอ้างอิงถึง Localizable.strings ของแอป ซึ่งช่วยให้สามารถส่ง payload โดยไม่มีข้อความในภาษาเฉพาะ — แอปจะแทนที่คำแปล
เริ่มต้นจาก iOS 15 Apple ได้แนะนำกลไก Focus Mode ซึ่งต้องการให้ผู้พัฒนาระบุระดับการขัดจังหวะของการแจ้งเตือน interruption-level รับค่า: passive (ไม่มีเสียง ไม่ปลุกหน้าจอ), active (พฤติกรรมมาตรฐาน), time-sensitive (ทะลุ Focus ต้องการสิทธิ์พิเศษ) และ critical (สถานการณ์ทางการแพทย์/ฉุกเฉิน) คีย์ relevance-score (0–1) ช่วยระบบ Focus ในการจัดลำดับความสำคัญของการแจ้งเตือนภายในหมวดหมู่เดียวกัน
คีย์ thread-id จัดกลุ่มการแจ้งเตือนในศูนย์การแจ้งเตือน การแจ้งเตือนทั้งหมด ที่มี thread-id เดียวกันจะแสดงเป็นกลุ่มเดียวที่ผู้ใช้สามารถขยายได้ ซึ่งมีประโยชน์โดยเฉพาะสำหรับแอปส่งข้อความที่ข้อความจากผู้ติดต่อรายเดียวกันถูกจัดกลุ่มเข้าด้วยกัน หรือสำหรับแอปที่ส่งการแจ้งเตือนจำนวนมากประเภทเดียวกัน
ฟิลด์แบบกำหนดเอง คือคีย์ใดๆ นอกพจนานุกรม aps ที่ผู้พัฒนาเพิ่มเพื่อส่งข้อมูลเพิ่มเติมไปยังอุปกรณ์ เซิร์ฟเวอร์จะรวมไว้ในวัตถุ JSON รากของ payload และแอปจะดึงข้อมูลผ่าน userInfo ใน UNNotificationContent ฟิลด์แบบกำหนดเองต้องไม่ซ้ำชื่อคีย์ของ aps เพื่อหลีกเลี่ยงการขัดแย้งในการแยกวิเคราะห์
ข้อจำกัดหลักคือขนาดรวมของ payload ต้องไม่เกิน 4096 ไบต์ ฟิลด์แบบกำหนดเอง แข่งขันขีดจำกัดนี้กับคีย์ aps ที่จำเป็น ดังนั้นการลดขนาดของข้อมูลที่ส่งจึงเป็นสิ่งสำคัญ ใช้ชื่อคีย์ที่สั้น (เช่น “uid” แทนที่ “user-id”) หลีกเลี่ยงโครงสร้าง JSON ขนาดใหญ่และส่งเฉพาะตัวระบุแทนที่วัตถุข้อมูลที่สมบูรณ์
ฟิลด์แบบกำหนดเองมาจากเซิร์ฟเวอร์และไม่ควรเชื่อถือโดยไม่มีการตรวจสอบ ตรวจสอบเสมอ ชนิดและค่าของฟิลด์แบบกำหนดเองเมื่อแยกวิเคราะห์: ตรวจสอบการมีอยู่ของคีย์ผ่าน optional binding แปลงเป็นชนิดที่คาดหวังโดยใช้ as? String/Int/Dictionary และจัดการกรณีที่ไม่มีค่า อย่าใช้ force unwrap (!) สำหรับข้อมูลจาก payload — เซิร์ฟเวอร์อาจส่งข้อมูลที่ไม่ถูกต้อง ทำให้แอปขัดข้อง
{
"aps": {
"alert": {
"title": "ข้อความใหม่",
"body": "สวัสดี! สบายดีไหม?"
},
"badge": 5,
"sound": "default",
"category": "message",
"thread-id": "chat_4521",
"mutable-content": 1
},
"sender-id": "user_789",
"chat-id": "chat_4521",
"message-type": "text",
"image-url": "https://cdn.example.com/img.jpg"
}
ใช้รูปแบบการตั้งชื่อที่สม่ำเสมอสำหรับฟิลด์แบบกำหนดเองในทุก payload ของโครงการ kebab-case (message-type) หรือ camelCase (messageType) — ทั้งสองวิธีเป็นที่ยอมรับ แต่ความสม่ำเสมอภายในโครงการเป็นสิ่งสำคัญ หลีกเลี่ยงชื่อที่ยาว: “uid” แทนที่ “user-identifier” “img” แทนที่ “profile-image-url” แต่ละอักขระในชื่อคีย์ใช้หนึ่งไบต์ของขีดจำกัด 4096
สถานการณ์ที่แตกต่างกัน ของการแจ้งเตือน push ต้องการชุดค่าผสมของคีย์ที่แตกต่างกันใน payload มาดูตัวอย่างทั่วไปหลายๆ ตัวอย่าง: การแจ้งเตือนข้อความง่ายๆ การแจ้งเตือนที่แปลภาษา Silent Push และการแจ้งเตือน Rich พร้อมสื่อแนบ
payload พื้นฐานที่มีข้อความและเสียง — การกำหนดค่าขั้นต่ำสำหรับแสดงการแจ้งเตือนให้ผู้ใช้ Alert เป็นสตริง ให้ข้อความสั้น sound default เล่นเสียงระบบมาตรฐาน Badge เป็นตัวเลือกและตั้งตัวนับบนไอคอนแอป category และ thread-id ถูกเพิ่มสำหรับการจัดกลุ่มและการโต้ตอบ
{
"aps": {
"alert": "เตือนความจำ: ประชุมใน 15 นาที",
"badge": 3,
"sound": "default"
}
}
เพื่อส่งการแจ้งเตือนไปยังอุปกรณ์ที่มีภาษาต่างกัน ให้ใช้คีย์การแปลภาษาแทนที่ข้อความตายตัว title-loc-key อ้างอิงถึงคีย์ใน Localizable.strings ของแอป และ title-loc-args ให้อาร์กิวเมนต์ ซึ่งช่วยให้สามารถส่ง payload เดียวไปยังอุปกรณ์ทั้งหมดและแอปจะแสดงข้อความในภาษาที่เหมาะสม
{
"aps": {
"alert": {
"title-loc-key": "NEW_MESSAGE_TITLE",
"title-loc-args": ["อันนา"],
"loc-key": "NEW_MESSAGE_BODY",
"loc-args": ["สวัสดี!"]
},
"sound": "message.caf"
}
}
สำหรับการซิงค์พื้นหลังโดยไม่แสดงการแจ้งเตือน ให้ใช้ content-available: 1 โดยไม่มี alert ฟิลด์แบบกำหนดเอง ระบุประเภทการดำเนินการและข้อมูลสำหรับการประมวลผล ระบบจะเรียกใช้แอปในพื้นหลัง เรียก didReceiveRemoteNotification พร้อม fetchCompletionHandler และแอปจะดำเนินการซิงค์
{
"aps": {
"content-available": 1
},
"sync-type": "invalidate-cache",
"timestamp": "2026-07-03T12:00:00Z"
}
เพื่อแสดงสื่อแนบ จำเป็นต้องใช้ mutable-content: 1 เพื่อเรียกใช้ Service Extension พร้อมกับ URL ของรูปภาพในฟิลด์แบบกำหนดเอง mutable-content: 1 สัญญาณให้ระบบเริ่ม UNNotificationServiceExtension ซึ่งจะดาวน์โหลดรูปภาพจาก URL และเพิ่มเป็น UNNotificationAttachment คีย์ category ระบุหมวดหมู่ที่ลงทะเบียนไว้สำหรับแสดงปุ่มการดำเนินการ
{
"aps": {
"alert": {
"title": "สินค้าใหม่",
"body": "ดูคอลเลกชันใหม่"
},
"category": "product",
"mutable-content": 1
},
"media-url": "https://cdn.example.com/product.jpg"
}
UNNotificationContent.userInfo ประกอบด้วยพจนานุกรมที่สมบูรณ์ของ payload ที่ได้รับหลังจากการประมวลผลของระบบ แอปเข้าถึง payload ในตัวแทน UNUserNotificationCenter เมื่อได้รับการแจ้งเตือน (ในเบอร์หน้า) เมื่อแตะการแจ้งเตือน รวมถึงใน Service Extension และ Content Extension การแยกวิเคราะห์ที่ถูกต้องเป็นสิ่งจำเป็นสำหรับการดึงข้อมูลแบบกำหนดเองและการกำหนดการกระทำต่อไป
เมื่อผู้ใช้แตะการแจ้งเตือน ระบบจะเรียกเมธอด didReceive response ใน UNUserNotificationCenterDelegate response.notification.request.content.userInfo ประกอบด้วย payload ที่สมบูรณ์ ผู้พัฒนาดึงฟิลด์แบบกำหนดเอง กำหนดประเภทการกระทำ (เช่น เปิดแชท นำทางไปยังสินค้า) และเรียกใช้การนำทางที่เหมาะสมในแอป
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification
.request.content.userInfo
guard let chatId = userInfo["chat-id"] as? String
else {
completionHandler()
return
}
let messageType = userInfo["message-type"]
as? String ?? "text"
NavigationRouter.shared.navigate(
to: .chat(chatId: chatId,
messageType: messageType))
completionHandler()
}
Service Extension ได้รับ payload ก่อนที่การแจ้งเตือนจะแสดงและสามารถแก้ไขได้ การตรวจสอบ payload เป็นขั้นตอนแรกใน didReceive: ตรวจสอบฟิลด์แบบกำหนดเองที่จำเป็น ตรวจสอบ URL สื่อแนบ และตรวจสอบชนิดข้อมูล ถ้า payload ไม่ถูกต้อง ให้เรียก completion handler ด้วยเนื้อหาต้นฉบับทันทีเพื่อหลีกเลี่ยงการเสียเวลาในการประมวลผลที่ไม่จำเป็น
สำหรับการดีบักการแจ้งเตือน push ในการผลิต ให้ใช้การบันทึก payload ที่มีโครงสร้าง OSLog ช่วยให้สามารถบันทึก payload ด้วยหมวดหมู่ “notifications” ที่ระดับ debug ในด้านเซิร์ฟเวอร์ ให้ติดตามการตอบกลับ APNS: การตอบกลับที่สำเร็จจะมี apns-id สำหรับจับคู่กับ payload ที่ส่งไป ในขณะที่ข้อผิดพลาด 400 บ่งชี้ถึง JSON ที่ไม่ถูกต้องหรือเกินขนาด
คำถามที่พบบ่อย
ขนาด payload สูงสุดคือ 4096 ไบต์ สำหรับการแจ้งเตือน push ทั่วไปและ 5120 ไบต์สำหรับ VoIP push (PushKit) เมื่อเกินขีดจำกัดนี้ APNS จะส่งคืนข้อผิดพลาด 400 Bad Request ขนาดจะถูกนับในไบต์ ไม่ใช่อักขระ — โปรดพิจารณาถึงการเข้ารหัส UTF-8
ใช้คีย์ loc-key, title-loc-key, loc-args และ title-loc-args ภายใน alert แอปจะแทนที่คำแปลจาก Localizable.strings ของตัวเองตามภาษาของอุปกรณ์ ซึ่งช่วยให้สามารถส่ง payload เดียวไปยังอุปกรณ์ทั้งหมดโดยไม่คำนึงถึงภาษา
content-available เรียกใช้แอปในพื้นหลังสำหรับการประมวลผลข้อมูล (silent push) โดยไม่แสดงการแจ้งเตือน mutable-content เรียกใช้ Service Extension เพื่อแก้ไขเนื้อหาก่อนแสดง ทั้งสองคีย์สามารถใช้ร่วมกันสำหรับการประมวลผลพื้นหลังและการแก้ไขการแจ้งเตือนในภายหลัง
ใช้ APNS Sandbox สำหรับการทดสอบและตรวจสอบการตอบกลับ HTTP ของเซิร์ฟเวอร์ Apple: 200 OK หมายถึงการจัดส่งสำเร็จ สำหรับการตรวจสอบโครงสร้าง ให้ใช้ JSON schema ในไปป์ไลน์ CI/CD ของคุณ ใน Xcode ส่งการแจ้งเตือนทดสอบผ่านซิมูเลเตอร์โดยใช้ xcrun simctl push
apns-id คือตัวระบุการแจ้งเตือน push ที่ไม่ซ้ำกันในระบบ APNS ซึ่งถูกส่งคืนในการตอบกลับเมื่อจัดส่งสำเร็จ ใช้สำหรับติดตามการจัดส่งผ่าน Logs API และสำหรับการดีบัก เซิร์ฟเวอร์ควรเก็บ apns-id สำหรับทุกการแจ้งเตือนที่ส่งไป
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม