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
สร้างแอปเนทีฟ iOS และ Android ด้วยเส้นทางร่วมและโฟลว์ deep link ที่ปลอดภัยยิ่งขึ้น
ลองใช้ AppMaster

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 มากนัก

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

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 ลิงก์เดียวนี้สามารถสำเร็จในทุกสถานการณ์โดยไม่เปิดเผยความลับ

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

ส่งคำเชิญที่เชื่อถือได้
สร้างการยอมรับคำเชิญด้วยขั้นตอนยืนยันที่ชัดเจนและการตรวจสอบจากเซิร์ฟเวอร์
สร้างฟลอว์คำเชิญ

คำเชิญเป็นคลาสสิก: ใครสักคนส่งลิงก์ใน 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 ด้วยแผนฟรี
เมื่อคุณพร้อม คุณสามารถเลือกการสมัครที่เหมาะสมได้

เริ่ม