09 มิ.ย. 2568·อ่าน 2 นาที

Deep links สำหรับแอปมือถือเนทีฟ: เส้นทาง โทเค็น และการเปิดในแอป

เรียนรู้การทำ deep link สำหรับแอปมือถือเนทีฟ: วางแผนเส้นทาง จัดการการเปิดในแอป และส่งมอบโค้ดชั่วคราวอย่างปลอดภัยสำหรับ Kotlin และ SwiftUI โดยไม่ต้องเขียน routing แบบกำหนดเองยุ่งเหยิง

Deep links สำหรับแอปมือถือเนทีฟ: เส้นทาง โทเค็น และการเปิดในแอป

เมื่อใครสักคนแตะลิงก์บนโทรศัพท์ พวกเขาคาดหวังผลลัพธ์หนึ่งอย่าง: พาไปยังที่ที่ถูกต้อง ทันที ไม่ใช่ที่ใกล้เคียง ไม่ใช่หน้าหลักที่มีแถบค้นหา หรือหน้าล็อกอินที่ลืมเหตุผลที่เขามา

ประสบการณ์ deep link ที่ดีควรเป็นแบบนี้:

  • ถ้าแอปติดตั้งอยู่ จะเปิดตรงไปยังหน้าจอที่ลิงก์ต้องการ
  • ถ้าแอปยังไม่ได้ติดตั้ง การแตะยังช่วยได้ (เช่น เปิดเว็บ fallback หรือหน้าร้านแอป และสามารถพาผู้ใช้กลับไปยังปลายทางเดิมหลังติดตั้ง)
  • ถ้าผู้ใช้ต้องล็อกอิน ให้ล็อกอินครั้งเดียวแล้วไปยังหน้าที่ตั้งใจไว้ ไม่ใช่จุดเริ่มต้นของแอป
  • ถ้าลิงก์มีการกระทำ (ยอมรับคำเชิญ ดูคำสั่ง ยืนยันอีเมล) การกระทำนั้นต้องชัดเจนและปลอดภัย

ความหงุดหงิดส่วนใหญ่เกิดจากลิงก์ที่ "ทำงานแบบพอใช้" แต่ทำให้การไหลแตก ผู้ใช้เห็นหน้าผิด สูญเสียสิ่งที่ทำ หรือวนลูป: แตะลิงก์ ล็อกอิน ไปแดชบอร์ด แตะอีกครั้ง ล็อกอินอีกครั้ง แม้แต่ก้าวเพิ่มเติมเดียวก็ทำให้ผู้ใช้ยอมแพ้ได้ โดยเฉพาะการกระทำครั้งเดียวเช่นคำเชิญหรือรีเซ็ตรหัสผ่าน

ก่อนเขียนโค้ด Kotlin หรือ SwiftUI ให้ตัดสินใจว่าคุณต้องการให้ลิงก์หมายถึงอะไร หน้าจอไหนเปิดจากภายนอกได้ อะไรเปลี่ยนถ้าแอปปิดเทียบกับกำลังรัน ควรเกิดอะไรขึ้นเมื่อผู้ใช้ออกจากระบบ

การวางแผนนี้ป้องกันความเจ็บปวดได้มาก: เส้นทางที่ชัดเจน พฤติกรรม "open in app" ที่คาดเดาได้ และวิธีส่งมอบโค้ดครั้งเดียวอย่างปลอดภัยโดยไม่ใส่ความลับลงใน URL

ไม่ใช่ทุก "ลิงก์ที่เปิดแอป" จะทำงานเหมือนกัน การปฏิบัติต่อพวกมันเหมือนกันจะทำให้เกิดความล้มเหลวคลาสสิก: ลิงก์เปิดผิดที่ เปิดเบราว์เซอร์แทนแอป หรือทำงานเฉพาะแพลตฟอร์มเดียว

สามกลุ่มทั่วไป:

  • Custom schemes (เช่น myapp:) ตั้งค่าง่าย แต่แอปและเบราว์เซอร์หลายตัวจัดการด้วยความระมัดระวัง
  • Universal Links (iOS) และ App Links (Android). ใช้ลิงก์เว็บปกติและสามารถเปิดแอปเมื่อมีการติดตั้ง หรือ fallback เป็นเว็บไซต์เมื่อไม่มี
  • ลิงก์ในเบราว์เซอร์ฝังตัวภายในแอป. ลิงก์ที่เปิดในแอปอีเมลหรือ messenger มักมีพฤติกรรมต่างจาก Safari หรือ Chrome

"Open in app" อาจหมายถึงสิ่งต่าง ๆ ขึ้นกับที่แตะ ลิงก์ที่แตะใน Safari อาจกระโดดตรงเข้าแอปได้ ขณะที่เดียวกันในอีเมลหรือ messenger อาจเปิด web view ฝังตัวก่อน และผู้ใช้ต้องกดปุ่ม "เปิด" เพิ่มอีกขั้น (หรือไม่เห็นเลย) บน Android Chrome อาจเคารพ App Links ขณะที่เบราว์เซอร์ในแอปโซเชียลอาจไม่สนใจ

Cold start vs แอปกำลังรันเป็นกับดักถัดไป

  • Cold start: OS เปิดแอปของคุณ แอปเริ่มต้น แล้วคุณได้รับ deep link ถ้า flow การเริ่มต้นแสดง splash, ตรวจ auth หรือโหลด config ระยะไกล ลิงก์อาจหายไป เว้นแต่คุณเก็บมันและเล่นซ้ำหลังแอปพร้อม
  • แอปกำลังรัน: คุณได้รับลิงก์ขณะที่ผู้ใช้กำลังใช้งาน stack การนำทางมีอยู่แล้ว ดังนั้นปลายทางเดียวกันอาจต้องการการจัดการต่างกัน (push หน้าจอ vs รีเซ็ต stack)

ตัวอย่างง่าย ๆ: ลิงก์คำเชิญที่แตะจาก Telegram มักเปิดในเบราว์เซอร์ฝังตัวก่อน ถ้าแอปของคุณสมมติว่า OS จะส่งต่อโดยตรง ผู้ใช้จะเห็นหน้าเว็บแทนและคิดว่าลิงก์เสีย แผนรองรับสภาพแวดล้อมเหล่านี้ล่วงหน้าจะทำให้คุณเขียน glue เฉพาะแพลตฟอร์มน้อยลง

วางแผนเส้นทางก่อนลงมือทำ

บั๊กส่วนใหญ่ของ deep link ไม่ใช่ปัญหา Kotlin หรือ SwiftUI แต่เป็นปัญหาการวางแผน ลิงก์ไม่แมปตรงไปยังหน้าจอเดียว หรือมีตัวเลือกแบบ "อาจจะ" มากเกินไป

เริ่มด้วยรูปแบบเส้นทางที่สอดคล้องกับวิธีที่ผู้คนคิดเกี่ยวกับแอป: รายการ, รายละเอียด, การตั้งค่า, เช็คเอาต์, คำเชิญ เก็บให้อ่านง่ายและคงที่ เพราะคุณจะใช้ซ้ำในอีเมล, รหัส QR, และหน้าเว็บ

ชุดเส้นทางตัวอย่าง:

  • หน้าแรก
  • รายการคำสั่งและรายละเอียดคำสั่ง (orderId)
  • การตั้งค่าบัญชี
  • การยอมรับคำเชิญ (inviteId)
  • ค้นหา (query, tab)

แล้วกำหนดพารามิเตอร์:

  • ใช้ ID สำหรับออบเจ็กต์เดี่ยว (orderId)
  • ใช้พารามิเตอร์ที่เป็นทางเลือกสำหรับสถานะ UI (tab, filter)
  • ตัดสินค่าเริ่มต้นเพื่อให้ทุกลิงก์มีปลายทางที่ดีที่สุดเดียว

ตัดสินใจด้วยว่าจะทำอย่างไรเมื่อลิงก์ผิด: ขาดข้อมูล, ID ไม่ถูกต้อง, หรือเนื้อหาที่ผู้ใช้เข้าถึงไม่ได้ ค่าเริ่มต้นที่ปลอดภัยที่สุดคือเปิดหน้าจอที่มั่นคงใกล้เคียง (เช่น หน้ารายการ) และแสดงข้อความสั้น ๆ หลีกเลี่ยงการโยนผู้ใช้ไปยังหน้าว่างหรือหน้าล็อกอินที่ไม่มีบริบท

สุดท้าย วางแผนตามแหล่งที่มา รหัส QR มักต้องการเส้นทางสั้นที่เปิดเร็วและทนต่อการเชื่อมต่อไม่ดี ลิงก์ในอีเมลอาจยาวกว่าและมีบริบทพิเศษ หน้าเว็บควรลดรูปอย่างสมเหตุผล: ถ้าแอปยังไม่ได้ติดตั้ง ผู้ใช้ยังควรไปถึงที่ที่อธิบายว่าทำอย่างไรต่อ

ถ้าคุณใช้แนวทางที่ขับเคลื่อนด้วย backend (เช่น สร้าง endpoint และหน้าจอด้วยแพลตฟอร์มอย่าง AppMaster) แผนเส้นทางนี้จะกลายเป็นสัญญาที่แชร์: แอปรู้ว่าจะไปที่ไหน และ backend รู้ว่า ID และสถานะใดที่ถูกต้อง

การส่งมอบโทเค็นอย่างปลอดภัยโดยไม่ใส่ความลับใน URL

ลิงก์มักถูกคิดว่าเป็นซองปลอดภัย แต่ไม่ใช่ ทุกอย่างใน URL อาจจบลงในประวัติการท่องเว็บ สกรีนช็อต พรีวิวที่ถูกแชร์ บันทึกวิเคราะห์ หรือคัดลอกไปยังแชท

หลีกเลี่ยงการใส่ความลับในลิงก์ รวมถึง access token ระยะยาว, refresh token, รหัสผ่าน, ข้อมูลส่วนบุคคล หรือสิ่งที่จะทำให้คนอื่นทำตัวเป็นผู้ใช้ถ้าลิงก์ถูกส่งต่อ

รูปแบบที่ปลอดภัยกว่าคือโค้ดสั้นที่ใช้ได้ครั้งเดียว ลิงก์มีแค่โค้ดนั้น แอปแลกมันเป็น session จริงหลังเปิด หากคนหาประโยชน์จากลิงก์ โค้ดควรไร้ประโยชน์หลังหนึ่งนาทีสองนาที หรือหลังแลกครั้งแรก

โฟลว์การส่งมอบอย่างง่าย:

  • ลิงก์มีโค้ดครั้งเดียว ไม่ใช่ session token
  • แอปเปิดแล้วเรียก backend เพื่อแลกโค้ด
  • Backend ตรวจสอบวันหมดอายุ ตรวจสอบว่าไม่เคยถูกใช้ แล้วทำเครื่องหมายว่าใช้แล้ว
  • Backend คืน session ที่ยืนยันแล้วให้แอป
  • แอปล้างโค้ดออกจากหน่วยความจำเมื่อแลกเสร็จ

แม้หลังแลกสำเร็จ ยืนยันตัวตนภายในแอปก่อนทำสิ่งที่ละเอียดอ่อน ถ้าลิงก์ใช้อนุมัติการจ่ายเงิน เปลี่ยนอีเมล หรือส่งออกข้อมูล ให้ขอการยืนยันรวดเร็วเช่น biometric หรือการล็อกอินใหม่

เก็บ session ที่ได้อย่างปลอดภัย บน iOS ปกติจะใช้ Keychain บน Android ให้ใช้ที่เก็บที่สนับสนุน Keystore เก็บแค่ที่จำเป็น และล้างเมื่อ logout, ลบบัญชี, หรือเมื่อพบการใช้งานที่น่าสงสัย

ตัวอย่างปฏิบัติ: ส่งลิงก์คำเชิญที่มีโค้ดครั้งเดียวหมดอายุใน 10 นาที แอปแลกโค้ด แล้วแสดงหน้าจอที่ระบุชัดว่าจะเกิดอะไรขึ้นต่อ (เข้าร่วม workspace ใด) ก่อนผู้ใช้ยืนยัน แอปจึงดำเนินการเข้าร่วม

ถ้าคุณสร้างด้วย AppMaster แนวทางนี้แมปได้ตรงไปยัง endpoint ที่แลกโค้ดและคืน session ขณะที่ UI มือถือจัดการขั้นตอนยืนยันก่อนการกระทำที่มีผลกระทบสูง

การยืนยันตัวตนและ “กลับไปยังที่คุณค้างไว้”

จัดการ deep link ใน cold start
สร้างแอป Kotlin และ SwiftUI ที่จัดการ cold start และ warm start อย่างถูกต้อง
สร้างมือถือ

Deep link มักชี้ไปยังหน้าที่มีข้อมูลส่วนตัว เริ่มจากการตัดสินใจว่าหน้าไหนเปิดได้โดยทุกคน (public) และหน้าไหนต้องล็อกอิน (protected) การตัดสินใจนี้ป้องกันความประหลาดใจว่า "ในการทดสอบมันใช้งานได้" ส่วนใหญ่

กฎง่าย ๆ: เปิดไปยังสถานะลงจอดที่ปลอดภัยก่อน แล้วนำทางไปยังหน้าที่ต้องล็อกอินหลังยืนยันตัวตน

ตัดสินใจว่าอะไรสาธารณะ vs ป้องกัน

ปฏิบัติต่อ deep link เหมือนอาจถูกส่งต่อให้คนผิดคน

  • สาธารณะ: หน้าการตลาด บทความช่วยเหลือ เริ่มรีเซ็ตรหัสผ่าน เริ่มการยอมรับคำเชิญ (ยังไม่แสดงข้อมูล)
  • ป้องกัน: รายละเอียดคำสั่ง ข้อความ การตั้งค่าบัญชี หน้าผู้ดูแล
  • ผสม: หน้าพรีวิวใช้ได้ แต่แสดงเฉพาะข้อมูลไม่ละเอียดจนกว่าจะล็อกอิน

“ต่อหลังล็อกอิน” ที่พาผู้ใช้กลับไปยังที่ถูกต้อง

แนวทางที่เชื่อถือได้คือ: แยกลิงก์ เก็บเป้าหมายที่ตั้งใจไว้ แล้วนำทางตามสถานะ auth

ตัวอย่าง: ผู้ใช้แตะลิงก์ "เปิดในแอป" ไปยังตั๋วซัพพอร์ตเฉพาะขณะที่ยังล็อกเอาต์ แอปควรเปิดไปยังหน้ากลาง ขอให้ล็อกอิน แล้วพาไปยังตั๋วนั้นโดยอัตโนมัติ

เพื่อให้เชื่อถือได้ ให้เก็บ "return target" เล็ก ๆ ท้องถิ่น (ชื่อเส้นทาง + ID) พร้อมวันหมดอายุสั้น หลังล็อกอินอ่านครั้งเดียว นำทาง แล้วลบ ถ้าการล็อกอินล้มเหลวหรือเป้าหมายหมดอายุ ให้ย้อนกลับไปยังหน้าโฮมที่ปลอดภัย

จัดการ edge cases ด้วยความเคารพ:

  • เซสชันหมดอายุ: แสดงข้อความสั้น ๆ ขอการยืนยันตัวใหม่ แล้วดำเนินต่อ
  • สิทธิ์ถูกเพิกถอน: เปิดเปลือกปลายทาง แล้วแสดงว่า "คุณไม่สามารถเข้าถึงได้อีกต่อไป" พร้อมตัวเลือกถัดไป

นอกจากนี้ หลีกเลี่ยงการแสดงข้อมูลส่วนตัวในพรีวิวหน้าจอล็อก, สลับแอป, หรือพรีวิวการแจ้งเตือน ให้หน้าจอสำคัญว่างจนกว่าจะโหลดข้อมูลและยืนยันเซสชัน

แนวทาง routing ที่หลีกเลี่ยงการพันกันของ navigation

แก้ปัญหา continue after login
ใช้โมดูลการยืนยันตัวตนในตัวเพื่อให้การเข้าสู่ระบบนำผู้ใช้กลับไปยังหน้าที่ถูกต้อง
สร้างระบบยืนยันตัวตน

Deep link กลายเป็นเรื่องยุ่งเมื่อทุกหน้าจอแยกย่อยการแยก URL ของตัวเอง การตัดสินใจเล็ก ๆ น้อย ๆ กระจายไปทั่วแอป ทำให้ยากต่อการเปลี่ยนอย่างปลอดภัย

ปฏิบัติตัว routing เหมือนงานประปาที่แชร์ เก็บตารางเส้นทางและ parser หนึ่งชุด และให้ UI รับอินพุตที่สะอาด

ใช้ตารางเส้นทางที่แชร์เดียว

ให้ iOS และ Android ตกลงกันบนรายการเส้นทางเดียวที่อ่านได้สำหรับมนุษย์ มองมันเป็นสัญญา

แต่ละเส้นทางแมปไปยัง:

  1. หน้าจอ และ
  2. โมเดลอินพุตขนาดเล็ก

เช่น “Order details” แมปไปยังหน้าจอ Order ที่รับอินพุต OrderRouteInput(id) ถ้าเส้นทางต้องการค่าพิเศษ (เช่น ref source) ให้เก็บไว้ในโมเดลอินพุต ไม่ใช่กระจายอยู่ในโค้ดมุมมอง

รวมการแยกวิเคราะห์และการตรวจสอบไว้ที่เดียว

เก็บการแยก การถอดรหัส และการตรวจสอบไว้ที่เดียว UI ไม่ควรถามว่า "โทเค็นนี้มีไหม?" หรือ "ID นี้ถูกไหม?" ควรได้รับอินพุตเส้นทางที่ถูกต้อง หรือสถานะข้อผิดพลาดที่ชัดเจน

โฟลว์ปฏิบัติได้:

  • รับ URL (แตะ สแกน แชร์ชีท)
  • แยกเป็นเส้นทางที่รู้จัก
  • ตรวจสอบฟิลด์จำเป็นและรูปแบบที่ยอมรับได้
  • ผลิตเป้าหมายหน้าจอพร้อมโมเดลอินพุต
  • นำทางผ่านจุดเข้าเดียว

เพิ่มหน้าฟอลล์แบ็ก "ลิงก์ไม่รู้จัก" ให้มีประโยชน์ ไม่ใช่ทางตัน: แสดงสิ่งที่เปิดไม่ได, อธิบายสาเหตุเป็นภาษาง่าย ๆ, และเสนอการกระทำต่อเช่นกลับบ้าน ค้นหา หรือเข้าสู่ระบบ

Deep link ที่ดีรู้สึกน่าเบื่อในความหมายที่ดี: ผู้ใช้แตะแล้วไปยังหน้าที่ถูกต้อง ไม่ว่าจะแอปติดตั้งหรือไม่

ขั้นตอน 1: เลือกจุดเข้าที่สำคัญ

จดลิงก์ประเภทแรก ๆ ที่คนใช้จริง ๆ สัก 10 แบบ: คำเชิญ, รีเซ็ตรหัสผ่าน, ใบเสร็จคำสั่ง, ดูตั๋ว, โปรโมชัน จำกัดให้น้อยโดยตั้งใจ

ขั้นตอน 2: เขียนแพทเทิร์นเป็นสัญญา

สำหรับแต่ละจุดเข้ากำหนดแพทเทิร์นหลักหนึ่งแบบและข้อมูลขั้นต่ำที่ต้องมีเพื่อเปิดหน้าที่ถูกต้อง ใช้ ID ที่มั่นคงแทนชื่อ กำหนดอะไรจำเป็นหรือไม่จำเป็น

กฎช่วยเหลือ:

  • หนึ่งจุดประสงค์ต่อเส้นทาง
  • พารามิเตอร์ที่จำเป็นต้องอยู่เสมอ; ตัวเลือกมีค่าเริ่มต้นที่ปลอดภัย
  • ใช้แพทเทิร์นเดียวกันระหว่าง iOS (SwiftUI) และ Android (Kotlin)
  • ถ้าคาดจะเปลี่ยน ให้สำรองเวอร์ชันง่าย ๆ (เช่น v1)
  • กำหนดพฤติกรรมเมื่อพารามิเตอร์ขาด (แสดงหน้าข้อผิดพลาด ไม่ใช่หน้าว่าง)

ขั้นตอน 3: ตัดสินใจพฤติกรรมล็อกอินและเป้าหมายหลังล็อกอิน

เขียนลงว่าแต่ละลิงก์ต้องล็อกอินหรือไม่ ถ้าต้อง ให้จดเป้าหมายแล้วดำเนินหลังล็อกอิน

ตัวอย่าง: ลิงก์ใบเสร็จอาจแสดงพรีวิวได้โดยไม่ต้องล็อกอิน แต่ว่ากด "ดาวน์โหลดใบแจ้งหนี้" อาจต้องล็อกอินและควรพาผู้ใช้กลับไปยังใบเสร็จนั้น

ขั้นตอน 4: ตั้งกฎการส่งมอบโทเค็น (อย่าใส่ความลับใน URL)

ถ้าลิงก์ต้องมีโทเค็นครั้งเดียว ให้กำหนดระยะเวลาคงอยู่และวิธีใช้งาน วิธีปฏิบัติ: URL มีโค้ดสั้น-ครั้งเดียว แอปแลกกับ backend เพื่อรับ session จริง

ขั้นตอน 5: ทดสอบสามสถานะในโลกความเป็นจริง

Deep link พังที่ขอบ ทดสอบแต่ละประเภทลิงก์ใน:

  • Cold start (แอปปิด)
  • Warm start (แอปอยู่ในหน่วยความจำ)
  • ไม่มีแอปติดตั้ง (ลิงก์ยังพาไปที่มีความหมาย)

ถ้าคุณเก็บเส้นทาง การตรวจสอบ auth และกฎแลกโค้ดในที่เดียว คุณจะไม่กระจายโลจิกการ routing เฉพาะ Kotlin และ SwiftUI มากนัก

ใช้ deep link ในเวิร์กโฟลว์จริง
สร้างพอร์ทัลลูกค้าหรือเครื่องมือภายในที่ deep link นำตรงไปยังเรคอร์ดที่ต้องการ
สร้างพอร์ทัล

Deep link มักพังด้วยเหตุผลน่าเบื่อ: สมมติฐานเล็ก ๆ การเปลี่ยนชื่อหน้าจอ หรือโทเค็น "ชั่วคราว" ที่ไปทั่ว

ความล้มเหลวที่พบและการแก้ไข

  • ใส่ access token ใน URL (แล้วรั่วไปรวมถึง logs). Query string ถูกคัดลอก แชร์ เก็บในประวัติ และจับโดย analytics และ crash logs แก้: ใส่แค่โค้ดครั้งเดียวในลิงก์ แลกในแอป แล้วหมดอายุเร็ว

  • สมมติว่าแอปติดตั้ง (ไม่มี fallback). ถ้าลิงก์เปิดไปหน้าข้อผิดพลาดหรือไม่ทำอะไร ผู้ใช้ยอมแพ้ แก้: ให้หน้า fallback บนเว็บที่อธิบายสิ่งที่จะเกิดขึ้นและเสนอเส้นทางติดตั้ง แม้แต่หน้า "เปิดแอปเพื่อดำเนินการต่อ" ง่าย ๆ ก็ยังดีกว่าไม่มีอะไร

  • ไม่จัดการหลายบัญชีบนอุปกรณ์เดียว. เปิดหน้าถูกแต่ภายใต้ผู้ใช้ผิดยิ่งกว่าเสีย ลอง: เมื่แอปได้รับลิงก์ ให้ตรวจบัญชีที่ใช้งาน ถามผู้ใช้ยืนยันหรือสลับ แล้วค่อยดำเนินการ หากการกระทำต้องการ workspace ระบุ workspace ID (ไม่ใช่ความลับ) แล้วตรวจสอบ

  • ลิงก์พังเมื่อหน้าหรือเส้นทางเปลี่ยนชื่อ. ถ้าเส้นทางผูกกับชื่อ UI เก่า ลิงก์เก่าจะตายเมื่อคุณเปลี่ยนชื่อแท็บ แก้: ออกแบบเส้นทางที่ยืนตามเจตนา (invite, ticket, order) และรักษาความเข้ากันได้ของเวอร์ชันเก่าไว้

  • ไม่มีการติดตามเมื่อเกิดความผิดพลาด. โดยไม่มีวิธีเล่นซ้ำที่เกิดขึ้น support จะเดา แก้: ใส่ request ID ที่ไม่ละเอียดอ่อนในลิงก์ ล็อกมันทั้งบนเซิร์ฟเวอร์และในแอป และแสดงข้อความผิดพลาดที่มี ID นั้น

เช็กลูกตุ้มความเป็นจริงอย่างรวดเร็ว: ลองนึกถึงลิงก์คำเชิญในแชทกลุ่ม ใครสักคนเปิดบนโทรศัพท์งานที่มีสองบัญชี แอปไม่ได้ติดตั้งบนแท็บเล็ต และลิงก์ถูกส่งต่อ หากลิงก์มีแค่โค้ดคำเชิญ รองรับ fallback ยืนยันบัญชี และล็อก request ID ลิงก์เดียวนี้สามารถสำเร็จในทุกสถานการณ์โดยไม่เปิดเผยความลับ

ตัวอย่าง: ลิงก์คำเชิญที่เปิดตรงหน้าจอทุกครั้ง

นำการส่งโทเค็นอย่างปลอดภัยไปใช้
ตั้งค่า endpoint แลกโค้ดแบบครั้งเดียวโดยไม่ต้องเขียนโค้ดบูตเทมเพลตเอง
สร้าง Backend

คำเชิญเป็นคลาสสิก: ใครสักคนส่งลิงก์ใน messenger และผู้รับคาดหวังแตะครั้งเดียวถึงหน้าคำเชิญ ไม่ใช่หน้าโฮมทั่วไป

สถานการณ์: ผู้จัดการเชิญเอเยนต์คนใหม่เข้าร่วม workspace "Support Team" ผู้รับแตะคำเชิญใน Telegram

ถ้าแอปติดตั้ง ระบบควรเปิดแอปและส่งรายละเอียดคำเชิญให้ ถ้าแอปไม่ได้ติดตั้ง ผู้ใช้ควรไปยังหน้าเว็บที่อธิบายคำเชิญและเสนอเส้นทางติดตั้ง หลังติดตั้งและเปิดครั้งแรก แอปควรยังสามารถทำฟลอว์คำเชิญให้เสร็จโดยไม่ต้องให้ผู้ใช้ค้นหาลิงก์อีก

ภายในแอป ฟลอว์เหมือนกันทั้ง Kotlin และ SwiftUI:

  • อ่านโค้ดคำเชิญจากลิงก์ขาเข้า
  • ตรวจสอบว่าผู้ใช้ล็อกอินหรือไม่
  • ยืนยันคำเชิญกับ backend แล้วนำทางไปยังหน้าที่ถูกต้อง

การยืนยันเป็นจุดสำคัญ ลิงก์ไม่ควรมีความลับเช่น session token ระยะยาว ควรมีโค้ดคำเชิญสั้นที่ใช้ได้หลังจากเซิร์ฟเวอร์ตรวจสอบมันแล้ว

ประสบการณ์ผู้ใช้ควรรู้สึกคาดเดาได้:

  • ยังไม่ได้ล็อกอิน: เห็นหน้าจอล็อกอิน แล้วกลับไปยอมรับคำเชิญหลังล็อกอิน
  • ล็อกอินแล้ว: เห็นการยืนยัน "เข้าร่วม workspace" หนึ่งครั้ง แล้วเข้าไปใน workspace ที่ถูกต้อง

ถ้าคำเชิญหมดอายุหรือถูกใช้แล้ว อย่าโยนผู้ใช้ไปหน้าข้อผิดพลาดว่าง ให้แสดงข้อความชัดเจนและตัวเลือกต่อไป: ขอคำเชิญใหม่ สลับบัญชี หรือติดต่อผู้ดูแล "คำเชิญนี้ถูกยอมรับแล้ว" ดีกว่า "โทเค็นไม่ถูกต้อง"

เช็กลิสต์ด่วนและขั้นตอนถัดไป

Deep link จะรู้สึกว่า "เสร็จ" เมื่อพวกมันทำงานเหมือนกันทุกที่: cold start, warm start, และเมื่อผู้ใช้ล็อกอินแล้ว

เช็กลิสต์ด่วน

ก่อนปล่อยทดสอบแต่ละหัวข้อบนอุปกรณ์จริงและเวอร์ชัน OS ต่าง ๆ:

  • ลิงก์เปิดหน้าที่ถูกต้องทั้งใน cold start และ warm start
  • ไม่มีข้อมูลละเอียดอ่อนไปอยู่ใน URL หากจำเป็นต้องส่งโทเค็น ให้เป็นโค้ดสั้นครั้งเดียว
  • ลิงก์ที่ไม่รู้จัก หมดอายุ หรือถูกใช้แล้ว fallback ไปยังหน้าชัดเจนที่มีข้อความและตัวเลือกต่อไป
  • ทำงานได้จากอีเมล เบราว์เซอร์ สแกนเนอร์ QR และพรีวิวใน messenger (บางตัวจะเปิดลิงก์ก่อน)
  • การล็อกจะบอกคุณว่าเกิดอะไรขึ้น (รับลิงก์ แยกเส้นทาง auth ต้องการ สำเร็จหรือเหตุผลล้มเหลว)

วิธีตรวจสอบพฤติกรรมง่าย ๆ คือเลือกชุดลิงก์ที่ต้องทำงาน (คำเชิญ, รีเซ็ตรหัสผ่าน, รายละเอียดคำสั่ง, ตั๋วซัพพอร์ต, โปรโมชัน) แล้วรันผ่าน flow การทดสอบเดียวกัน: แตะจากอีเมล แตะจากแชท สแกน QR เปิดหลังติดตั้งใหม่

ขั้นตอนถัดไป (ทำให้ง่ายต่อการดูแล)

ถ้า deep link เริ่มกระจายข้ามหน้าจอ ให้จัดการ routing และ auth เป็นงานพื้นฐานร่วม ไม่ใช่โค้ดเฉพาะหน้าจอ รวม parsing ของเส้นทางไว้ที่เดียว และทำให้แต่ละปลายทางรับพารามิเตอร์สะอาด (ไม่ใช่ URL ดิบ) ทำแบบเดียวกันสำหรับ auth: จุดตรวจหนึ่งที่ตัดสิน "ดำเนินเลย" หรือ "ล็อกอินก่อน แล้วค่อยดำเนิน"

ถ้าต้องการลดโค้ดส่วนเชื่อมต่อที่กำหนดเอง อาจช่วยได้ถ้าสร้าง backend, auth และแอปมือถือร่วมกัน AppMaster (appmaster.io) เป็นแพลตฟอร์ม no-code ที่สร้าง backend และแอปเนทีฟที่พร้อมใช้งาน production ซึ่งช่วยให้ชื่อเส้นทางและ endpoint แลกโค้ดครั้งเดียวสอดคล้องกันเมื่อต้องการเปลี่ยนแปลง

ถ้าจะทำอย่างใดอย่างหนึ่งสัปดาห์หน้า ให้ทำสิ่งนี้: เขียนเส้นทาง canonical ของคุณและพฤติกรรม fallback สำหรับแต่ละกรณีความล้มเหลว แล้วนำกฎเหล่านั้นไปใช้ในชั้น routing เดียว

คำถามที่พบบ่อย

What should a deep link do when someone taps it?

ลิงก์ควรเปิดไปยังหน้าจอที่ลิงก์ระบุอย่างชัดเจน ไม่ใช่หน้าโฮมหรือแดชบอร์ดแบบรวม ถ้าแอปไม่ได้ติดตั้ง ก็ควรยังช่วยผู้ใช้ด้วยการนำไปยังที่ที่สมเหตุสมผลและแนะนำวิธีกลับมาที่ปลายทางเดิมหลังติดตั้ง

Should I use Universal Links/App Links or a custom URL scheme?

Universal Links (iOS) และ App Links (Android) ใช้ URL แบบเว็บปกติและสามารถเปิดแอปเมื่อมีการติดตั้ง โดยจะมี fallback เป็นเว็บไซต์เมื่อไม่มี ส่วน custom scheme ตั้งค่าได้ง่ายแต่เบราว์เซอร์หรือแอปอื่นอาจจัดการไม่สม่ำเสมอ จึงควรใช้เป็นตัวเลือกสำรอง

Why does “open in app” work in Safari/Chrome but fail in email or messenger apps?

หลายแอปอีเมลและแอปส่งข้อความเปิดลิงก์ในเบราว์เซอร์ฝังตัวของตัวเอง ซึ่งอาจไม่ส่งต่อไปยังระบบปฏิบัติการเหมือน Safari หรือ Chrome ดังนั้นต้องเตรียม fallback บนเว็บให้ชัดเจนและรองรับกรณีที่ผู้ใช้เห็นหน้าเว็บก่อน

How do I prevent deep links from getting lost on cold start?

ใน cold start แอปอาจแสดง splash ทำการตรวจสอบ หรือโหลดคอนฟิกก่อนจะพร้อมนำทาง วิธีที่เชื่อถือได้คือเก็บเป้าหมายลิงก์ขาเข้าไว้ทันที ทำการเริ่มต้นให้เสร็จ แล้ว "เล่นซ้ำ" การนำทางเมื่อตัวแอปพร้อม

What data should I never put in a deep link URL?

อย่าใส่ access token ระยะยาว, refresh token, รหัสผ่าน หรือข้อมูลส่วนบุคคลใน URL เพราะ URL ถูกบันทึก แบ่งปัน และเก็บอยู่ในแคช ให้ใช้โค้ดแบบครั้งเดียวที่หมดอายุเร็ว และให้แอปแลกโค้ดกับ backend หลังเปิดแอปแล้ว

How can I make users land on the intended screen after they log in?

ให้แอปแยกลิงก์ออกมา แปลงเป็นเป้าหมายที่ตั้งใจไว้ แล้วนำทางตามสถานะการยืนยันตัวตน โดยเก็บ "return target" ขนาดเล็กไว้ชั่วคราวหลังจากนั้นเมื่อล็อกอินเสร็จให้ดึงหนึ่งครั้ง นำทาง แล้วลบ หากการล็อกอินล้มเหลวหรือเป้าหมายหมดอายุ ให้ fallback ไปหน้าโฮมที่ปลอดภัย

How do I avoid deep link handling turning into navigation spaghetti?

กำหนดเส้นทางเป็นสัญญาร่วมและรวมการแยกวิเคราะห์กับการตรวจสอบไว้ที่เดียว ส่งพารามิเตอร์สะอาดให้หน้าจอแทนที่จะส่ง URL ดิบ วิธีนี้จะป้องกันไม่ให้แต่ละหน้าจอเขียนกฎของตัวเองและทำให้การเปลี่ยนแปลงปลอดภัยขึ้น

How should deep links behave on devices with multiple accounts logged in?

เมื่อได้รับลิงก์ ให้ตรวจสอบบัญชีที่ใช้งานอยู่ว่าตรงกับ workspace หรือ tenant ที่ลิงก์ระบุหรือไม่ ถ้าไม่ตรงให้ถามผู้ใช้ให้ยืนยันหรือสลับบัญชีก่อนจะแสดงข้อมูลส่วนตัว ดีกว่าการเปิดหน้าภายใต้บัญชีที่ผิด

What should happen if a deep link is invalid, expired, or missing data?

กลับไปยังหน้าจอที่มั่นคงที่สุด เช่น หน้ารายการ แล้วแสดงข้อความสั้น ๆ อธิบายว่าไม่สามารถเปิดอะไรได้ หลีกเลี่ยงการแสดงหน้าว่าง ความล้มเหลวเงียบ หรือการโยนผู้ใช้ไปยังหน้าล็อกอินโดยไม่มีบริบท

What’s the minimum testing I should do before shipping deep links?

ทดสอบแต่ละลิงก์สำคัญในสามสถานะ: แอปปิดอยู่, แอปกำลังรัน, และไม่มีแอปติดตั้งจริง และทดสอบจากแหล่งจริงเช่น อีเมล แชท และสแกน QR ถ้าคุณใช้ AppMaster คุณจะรักษาชื่อเส้นทางและ endpoint แลกโค้ดให้สอดคล้องระหว่าง backend และแอปได้ง่ายขึ้น ซึ่งลดโค้ดเชื่อมต่อแบบกำหนดเองที่ต้องดูแล

ง่ายต่อการเริ่มต้น
สร้างบางสิ่งที่ น่าทึ่ง

ทดลองกับ AppMaster ด้วยแผนฟรี
เมื่อคุณพร้อม คุณสามารถเลือกการสมัครที่เหมาะสมได้

เริ่ม