วิธีแก้ไขข้อผิดพลาด Bundle ID และ AASA App ID ของ iOS Universal Links ไม่ตรงกัน

opoinstall
2026-08-18
5 min read

ทำไมความไม่ตรงกันของ Bundle ID ถึงทำให้ iOS Universal Links ใช้การไม่ได้? ความไม่ตรงกันของ Bundle ID จะทำให้ Universal Links ล้มเหลวเมื่อ Application Identifier ที่ลงนามแล้วของแอปพลิเคชันไม่ตรงกับรายการ AASA appID/appIDs ที่สอดคล้องกันสำหรับโดเมนที่เกี่ยวข้อง ส่งผลให้การตรวจสอบสิทธิ์โดเมนที่เกี่ยวข้อง (associated-domain verification) ไม่ผ่าน

Bundle ID (CFBundleIdentifier) คือสตริงเฉพาะที่ใช้ระบุแอปพลิเคชัน iOS แต่ละตัวภายในระบบนิเวศของ Apple ในสถาปัตยกรรม Universal Link นั้น Bundle ID จะถูกนำมารวมกับ Application Identifier Prefix เพื่อสร้างเป็น Application Identifier ซึ่งระบบปฏิบัติการจะทำการตรวจสอบเทียบกับไฟล์ apple-app-site-association ที่โฮสต์อยู่ เพื่อให้สิทธิ์ในการจัดการ URL แบบเนทีฟ

คำศัพท์ คำจำกัดความ
Bundle ID ตัวระบุ reverse-DNS เฉพาะที่กำหนดให้กับเป้าหมายแอป iOS ใน Xcode (CFBundleIdentifier)
Application Identifier Prefix คำนำหน้า App ID ที่กำหนดในการตั้งค่าบัญชี Apple Developer (บ่อยครั้งแต่มักจะไม่ใช่เสมอไปที่จะเหมือนกับ Team ID)
Universal Links กลไกมาตรฐานของ Apple สำหรับการส่งต่อ URL เว็บ HTTPS ไปยังมุมมองแอปเนทีฟโดยตรง
Associated Domains สิทธิ์การใช้งาน (Entitlement) ใน Xcode ที่ประกาศว่าโดเมนเว็บใดบ้างที่แอปได้รับอนุญาตให้จัดการ (applinks:)
AASA File ไฟล์ JSON (apple-app-site-association) ที่โฮสต์บนโดเมนเพื่ออนุมัติการจัดการ URL ของแอป

ลำดับขั้นตอนการวินิจฉัยหลัก

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

Layer 1: Signed App Binary
       │
       ├── application-identifier (<Prefix>.<BundleID>)
       ├── com.apple.developer.team-identifier
       └── com.apple.developer.associated-domains (applinks:example.com)
                    │
                    ▼
Layer 2: AASA Delivery & CDN Ingestion
       │ (Apple-managed infrastructure retrieves origin AASA)
                    ▼
Layer 3: AASA Schema & Pattern Matching
       │ (Validates appIDs array and components/paths routing rules)
                    ▼
Layer 4: Device Association State
       │ (Operating system registers verified domains in local database)
                    ▼
Layer 5: Application Routing Execution
       │ (System routes matching URLs to application lifecycle handlers)
ไดอะแกรมสถาปัตยกรรมทางเทคนิค 5 ชั้นขั้นสูง แสดงห่วงโซ่การตรวจสอบ iOS Universal Links ตั้งแต่สิทธิ์ไบนารีที่มีลายเซ็นไปจนถึงการทำงานของแอปเนทีฟ บนพื้นหลังลายตารางโทนสีครีมละมุน

รายการตรวจสอบการแก้ไขด่วน: ขั้นตอนวินิจฉัยใน 30 วินาที

เมื่อ Universal Links ตกลงไปใช้การจัดการแบบเว็บโดยไม่คาดคิด ให้ตรวจสอบสิ่งเหล่านี้ตามลำดับ:

  • แยกตัวระบุที่มีลายเซ็น (Extract Signed Identifier): ตรวจสอบสิทธิ์ที่ฝังอยู่ในไบนารีที่คอมไพล์แล้วเพื่อให้ได้ค่า application-identifier ที่แน่นอน (<Prefix>.<BundleID>)
  • ตรวจสอบรูปแบบสิทธิ์ (Verify Entitlement Format): ยืนยันว่า com.apple.developer.associated-domains มีชื่อโฮสต์ที่แน่นอน (เช่น applinks:subdomain.domain.com) โดยไม่มีพาร์ทที่ไม่จำเป็น, คิวรีสตริง หรือเครื่องหมายทับปิดท้าย
  • ตรวจสอบ AASA ต้นทาง (Audit Origin AASA): ดึงข้อมูล https://subdomain.domain.com/.well-known/apple-app-site-association และตรวจสอบให้แน่ใจว่า Application Identifier ที่มีลายเซ็นแสดงอยู่ครบถ้วนใน appIDs
  • ตรวจสอบความถูกต้องของการจับคู่พาธ (Validate Path Matching): ยืนยันว่า URL เป้าหมายตรงกับรูปแบบ components หรือ paths ที่กำหนดไว้ในการกำหนดค่า AASA
  • ตรวจสอบขอบเขตโดเมน (Check Domain Scoping): ตรวจสอบให้แน่ใจว่าสิทธิ์ associated-domain ครอบคลุมโฮสต์เป้าหมายและมีการกำหนดค่า AASA ที่สอดคล้องกันสำหรับโฮสต์นั้น สำหรับซับโดเมน ให้ใช้ชื่อโฮสต์ที่ชัดเจนหรือรูปแบบ wildcard *. ที่รองรับตามความเหมาะสม
  • แยกโหมดการพัฒนา (Isolate Development Modes): ใช้ ?mode=developer บนบิวด์ที่เซ็นชื่อสำหรับการพัฒนา เพื่อข้ามการแคชของ Apple CDN ระหว่างการพัฒนา

เหตุผลที่ความถูกต้องของ Bundle ID และ Application Identifier จึงมีความสำคัญ

โครงสร้างของ Application Identifier

การตรวจสอบ Universal Link จะไม่ประเมินชื่อที่แสดงของแอปพลิเคชัน, URL scheme ภายใน หรือชื่อบันเดิล ตาม เอกสารของ Apple เกี่ยวกับ applinks.Details โมเดลความปลอดภัยจะอาศัย Application Identifier ที่สมบูรณ์อย่างเคร่งครัด โดยมีโครงสร้างดังนี้:

Application Identifier=ApplicationIdentifierPrefix  +  "."  +  CFBundleIdentifier\text{Application Identifier} = \text{ApplicationIdentifierPrefix} \;+\; \text{"."} \;+\; \text{CFBundleIdentifier}

โดยที่:

  • ApplicationIdentifierPrefix: คำนำหน้า App ID ที่กำหนดในการกำหนดค่าบัญชี Apple Developer ของคุณ (เช่น 9JA723G82S) สำหรับบัญชีนักพัฒนาซอฟต์แวร์สมัยใหม่จำนวนมาก ค่านี้จะตรงกับ Team ID แบบ 10 ตัวอักษร แต่ควรตรวจสอบคำนำหน้าจริงใน Apple Developer Portal แทนที่จะสมมติว่าทั้งสองอย่างใช้แทนกันได้
  • CFBundleIdentifier (Bundle ID): สตริง reverse-DNS ที่คำนึงถึงตัวพิมพ์เล็ก-ใหญ่ ซึ่งกำหนดไว้ในการตั้งค่าการคอมไพล์ของเป้าหมาย (เช่น com.example.mobileapp)

ในไฟล์ JSON apple-app-site-association (AASA) ที่โฮสต์อยู่ สตริงผสมนี้จะปรากฏอยู่ภายในอาร์เรย์ appIDs หรือรายการพจนานุกรม appID (เช่น 9JA723G82S.com.example.mobileapp) หากมีความแตกต่างของตัวอักษร ความแตกต่างของตัวพิมพ์เล็ก-ใหญ่ หรือมีช่องว่างต่อท้ายระหว่างสิทธิ์ที่ฝังอยู่ในไบนารีที่คอมไพล์กับรายการ AASA ที่โฮสต์ไว้ การตรวจสอบโดเมนจะล้มเหลว

ความไม่ตรงกันของ Bundle ID เป็นหนึ่งในสาเหตุที่มีลำดับความสำคัญสูงสุดในการตรวจสอบระหว่างการแก้ไขปัญหาการรวมระบบ แต่นั่นไม่ใช่เหตุผลเดียวที่ทำให้ Universal Link ตกลงไปใช้เว็บ

วิธีที่ Associated Domains และ AASA สร้างการเชื่อมโยงสองทาง

แตกต่างจาก URL scheme แบบกำหนดเองที่แอปพลิเคชันที่ติดตั้งใดๆ สามารถประกาศได้โดยไม่ต้องมีการตรวจสอบโดเมน Universal Links จะสร้างการเชื่อมโยงสองทางที่ปลอดภัย:

  • การประกาศจากแอปไปยังโดเมน (App-to-Domain Declaration): แอปพลิเคชัน iOS ที่คอมไพล์แล้วประกาศว่าเป็นเจ้าของโดเมนเว็บเฉพาะด้วยการรวมสิทธิ์ com.apple.developer.associated-domains ไว้ในลายเซ็นโค้ดของตน
  • การอนุญาตจากโดเมนไปยังแอป (Domain-to-App Authorization): โดเมนเว็บยืนยันว่าให้สิทธิ์การกำหนดเส้นทางแก่แอปพลิเคชันเฉพาะโดยการโฮสต์ไฟล์ AASA JSON ที่ https://<domain>/.well-known/apple-app-site-association หรือ https://<domain>/apple-app-site-association

ในระหว่างการติดตั้งหรืออัปเดตแอป ระบบปฏิบัติการจะตรวจสอบสิทธิ์ Associated Domains ที่เซ็นชื่อของแอปเทียบกับการกำหนดค่า AASA ที่ดึงมาสำหรับโดเมนนั้น Application Identifier ที่ใช้สำหรับการเชื่อมโยงแอปจะต้องตรงกับตัวระบุที่สอดคล้องกันซึ่งประกาศไว้ในการกำหนดค่า AASA หลังจากตัวระบุตรงกันแล้ว URL ที่ร้องขอจะต้องเป็นไปตามกฎ components หรือ paths ที่กำหนดค่าไว้อีกด้วย

อาการที่เกิดขึ้น: ทำไมตัวระบุที่ไม่ตรงกันจึงบังคับให้ตกลงไปใช้เว็บฟอลแบ็ก

เมื่อเกิดความไม่ตรงกันของ Application Identifier ระบบ iOS จะไม่แสดงข้อผิดพลาดเกี่ยวกับ Application Identifier ที่ไม่ตรงกันว่าเป็นข้อผิดพลาดรันไทม์ที่ร้ายแรงในทันที แต่ความล้มเหลวจะสะท้อนให้เห็นในสถานะการตรวจสอบ associated-domain การวินิจฉัยอุปกรณ์ หรือพฤติกรรมเว็บฟอลแบ็กที่ตามมา:

  • การจัดการของระบบ: เมื่อการเชื่อมโยงโดเมนล้มเหลว ระบบจะไม่เรียกใช้งานแอปผ่านเส้นทาง Universal Link ที่ตรวจสอบแล้ว ขึ้นอยู่กับวิธีที่เปิด URL และบริบทเบราว์เซอร์รอบข้าง URL จะยังคงอยู่หรือตกลงไปใช้การจัดการเว็บแทนที่จะถูกส่งไปยังแอปพลิเคชันเนทีฟ
  • ผลกระทบต่อประสบการณ์ผู้ใช้: เมื่อผู้ใช้แตะเว็บลิงก์ที่ตรงกันใน Messages, Mail หรือ Safari ระบบจะไม่รู้จักการแมปแอปเนทีฟที่ได้รับอนุญาตและจะเปิด URL เว็บในเบราว์เซอร์แทน

ดูเพิ่มเติม: Bundle ID ──> สถาปัตยกรรม Universal Links

วิธีที่ CDN ของ Apple ดึงข้อมูลและแคชไฟล์ AASA

ขั้นตอนการติดตั้งและการทำงานของ Apple CDN

เมื่อติดตั้งหรืออัปเดตแอปพลิเคชันที่มีสิทธิ์ com.apple.developer.associated-domains ระบบจะสร้างหรือรีเฟรชความสัมพันธ์ของโดเมนที่เกี่ยวข้อง:

  • เครื่องมือดึงข้อมูลผ่าน CDN (CDN-Mediated Scraper): เมื่อระบบสร้างหรือรีเฟรชความสัมพันธ์ของโดเมนที่เกี่ยวข้อง ระบบจะดึงข้อมูล AASA ของโดเมนผ่านโครงสร้างพื้นฐาน associated-domains ของ Apple และใช้ข้อมูลนั้นเพื่อตรวจสอบความสัมพันธ์
  • วงจรการแคชที่เป็นอิสระ (Independent Caching Lifecycle): CDN ที่จัดการโดย Apple จะควบคุมวงจรการรีเฟรชและการแคชของตนเอง ดังนั้นจึงไม่ควรคาดว่าการอัปเดตที่ต้นทางจะปรากฏให้เห็นผ่าน CDN ทันที เมื่อทดสอบการเปลี่ยนแปลง ให้ใช้โหมดสำรองสำหรับการพัฒนาตามเอกสารที่ระบุเมื่อเหมาะสม และตรวจสอบสถานะการเชื่อมโยงของอุปกรณ์
  • ข้อกำหนดของเซิร์ฟเวอร์ต้นทาง (Origin Server Requirements): เซิร์ฟเวอร์เว็บต้นทางต้องให้บริการไฟล์ AASA ผ่าน HTTPS ด้วยใบรับรอง TLS ที่ถูกต้องและเชื่อถือได้ (ปฏิเสธใบรับรองที่ลงนามเอง) โดยใช้ MIME type เป็น application/json การโฮสต์ AASA ต้องไม่พึ่งพาการเปลี่ยนเส้นทาง HTTP (HTTP redirects) จุดสิ้นสุด AASA ควรส่งคืนไฟล์โดยตรงด้วยสถานะ HTTP 200 OK

ความสอดคล้องของรูปแบบ AASA JSON

iOS เวอร์ชันใหม่รองรับไวยากรณ์พจนานุกรม components แบบละเอียด ในขณะที่ยังคงรักษาความเข้ากันได้ย้อนหลังกับอาร์เรย์ paths แบบเดิม

ตาม Apple Developer Technote TN3155 เกี่ยวกับการดีบัก Universal Links ภายในรายการ details ที่กำหนด นักพัฒนาควรใช้โครงสร้าง appIDs + components สมัยใหม่ หรือโครงสร้าง appID + paths แบบเดิมอย่างใดอย่างหนึ่ง ห้ามผสมผสานทั้งสองโครงสร้างเข้าด้วยกันในรายการเดียวกัน เนื่องจากอาจทำให้เกิดพฤติกรรมการตรวจสอบที่ไม่คาดคิดได้

ตัวอย่าง AASA รุ่นเก่ามักจะมี "apps": [] สำหรับการปรับใช้ที่มุ่งเน้นไปที่ระบบปฏิบัติการ Apple ยุคใหม่ คีย์นี้ไม่จำเป็นต้องมี ให้เก็บไว้เฉพาะเมื่อต้องรองรับระบบปฏิบัติการรุ่นเก่าที่ต้องการคีย์ดังกล่าวโดยเฉพาะเท่านั้น

โปรโตคอลการวินิจฉัย: ขั้นตอนการแก้ไขทีละขั้นตอน

ขั้นตอนที่ 1: ตรวจสอบสิทธิ์แอปที่เซ็นชื่อด้วย codesign

หากต้องการพิจารณาว่าไฟล์ IPA หรือบิวด์ดีบักที่ส่งออกมี Application Identifier และ Associated Domains ที่คาดหวังไว้อย่างถูกต้องหรือไม่ ให้ตรวจสอบลายเซ็นโค้ดของไบนารีโดยตรงโดยใช้ยูทิลิตี้บรรทัดคำสั่ง codesign บน macOS ตรวจสอบ application-identifier, com.apple.developer.team-identifier และ com.apple.developer.associated-domains ร่วมกัน

โปรไฟล์การจัดเตรียม (Provisioning Profile) จะแสดงความสามารถและโดเมนที่โปรไฟล์อนุญาต ในขณะที่ไฟล์ปฏิบัติการที่เซ็นชื่อ (codesign) จะแสดงสิ่งที่ไบนารีที่จัดส่งมีอยู่จริง

ขั้นตอนที่ 2: ตรวจสอบสคีมา JSON ของ AASA ที่โฮสต์อยู่

ตรวจสอบว่าเซิร์ฟเวอร์ต้นทางโฮสต์ไฟล์ AASA ที่ถูกต้องซึ่งสามารถเข้าถึงได้แบบสาธารณะโดยไม่ต้องมีการตรวจสอบสิทธิ์หรือการเปลี่ยนเส้นทาง โปรดทราบว่าตัวอย่าง AASA รุ่นเก่ามักจะมี "apps": [] ในขณะที่การกำหนดค่าสมัยใหม่ที่กำหนดเป้าหมายไปยัง iOS ยุคใหม่จะละเว้นคีย์นี้

สคีมา JSON ของ AASA มาตรฐานด้านล่างแสดงการกำหนดเส้นทางพาธที่ถูกต้องโดยใช้โครงสร้าง appIDs และ components สมัยใหม่:


```json
{
  "applinks": {
    "details": [
      {
        "appIDs": [
          "9JA723G82S.com.example.mobileapp",
          "9JA723G82S.com.example.mobileapp.staging"
        ],
        "components": [
          {
            "/": "/product/*",
            "comment": "Matches product detail routes"
          },
          {
            "/": "/invite/*",
            "?": { "ref": "?*" },
            "comment": "Matches referral links with custom query parameters"
          },
          {
            "/": "/help/*",
            "exclude": true,
            "comment": "Excludes customer support URLs from native routing"
          }
        ]
      }
    ]
  }
}

ขั้นตอนที่ 3: เรียกใช้เครื่องมือ CLI สำหรับวินิจฉัย (codesign, swcutil, curl)

บน macOS เวอร์ชันที่รองรับการวินิจฉัยด้วย swcutil ให้ใช้เครื่องมือนี้เพื่อตรวจสอบหรือตรวจสอบความถูกต้องของข้อมูลโดเมนที่เกี่ยวข้อง เนื่องจากตัวเลือกคำสั่งอาจแตกต่างกันไปตามระบบปฏิบัติการและรุ่นของชุดเครื่องมือ โปรดตรวจสอบตัวเลือกที่มีด้วยคำสั่ง swcutil --help ก่อนเรียกใช้ขั้นตอนการวินิจฉัยด้านล่าง:

# 0. ยืนยันตัวเลือกที่มี (ไวยากรณ์อาจแตกต่างกันไปตาม OS และรุ่นของ toolchain)
swcutil --help

# 1. แตกไฟล์ IPA ที่ส่งออก
unzip -q YourApp.ipa -d UnpackedApp

# 2. แยกและตรวจสอบสิทธิ์ที่มีลายเซ็นโดยตรงจากไฟล์ไบนารีปฏิบัติการ
codesign -d --entitlements :- "UnpackedApp/Payload/YourApp.app" > signed-entitlements.plist 2>/dev/null
/usr/libexec/PlistBuddy -c "Print" signed-entitlements.plist

# 3. ตรวจสอบว่าสามารถดาวน์โหลดข้อมูล AASA สำหรับโดเมนโดยใช้ swcutil ได้หรือไม่ (เครื่องมือวินิจฉัยของ macOS)
sudo swcutil dl -d custom.opwakeup.com

# 4. ตรวจสอบการจับคู่แพทเทิร์น AASA กับ URL เฉพาะโดยใช้ swcutil
sudo swcutil verify -d custom.opwakeup.com -j ./apple-app-site-association -u https://custom.opwakeup.com/product/123

# 5. ค้นหาข้อมูลจากจุดสิ้นสุดการวินิจฉัย Associated Domains CDN ที่จัดการโดย Apple โดยตรง
curl -i https://app-site-association.cdn-apple.com/a/v1/custom.opwakeup.com

ตรวจสอบจุดสิ้นสุด Associated Domains CDN ที่จัดการโดย Apple เมื่อแก้ไขปัญหาข้อมูล AASA ที่ส่งมอบผ่าน edge ให้ถือว่าจุดสิ้นสุดนี้เป็นโครงสร้างพื้นฐานสำหรับการวินิจฉัย มากกว่าที่จะเป็นสัญญา API สาธารณะ

ขั้นตอนที่ 4: ใช้ Associated Domains Developer Mode สำหรับการทดสอบ AASA

ตาม เอกสารของ Apple เกี่ยวกับการกำหนดค่า Associated Domains ทาง Apple มีโหมดสำรองสำหรับการพัฒนา (alternate mode for development) โหมด developer (?mode=developer) ช่วยให้อุปกรณ์สำหรับการพัฒนาที่มีคุณสมบัติเหมาะสมสามารถข้าม Apple-managed CDN และดึงไฟล์ AASA จากเซิร์ฟเวอร์เว็บต้นทางผ่าน HTTPS ได้โดยตรง

การกำหนดค่าด้านล่างแสดงวิธีการประกาศ Developer Mode ในการกำหนดค่าสิทธิ์ Xcode แยกต่างหาก:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.associated-domains</key>
    <array>
        <string>applinks:custom.opwakeup.com</string>
    </array>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.associated-domains</key>
    <array>
        <string>applinks:custom.opwakeup.com?mode=developer</string>
    </array>
</dict>
</plist>

เมื่อระบบปฏิบัติการสร้างการเชื่อมโยงโดเมนแล้ว การกำหนดเส้นทางระดับแอปพลิเคชันจะจัดการข้อมูล URL ที่เข้ามาโดยใช้ตัวแทนวงจรชีวิต UIKit หรือ SwiftUI มาตรฐาน:

import UIKit

// ----------------------------------------------------------------------------
// 1. การใช้งาน UIKit AppDelegate
// ----------------------------------------------------------------------------
@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        return true
    }

    // Callback มาตรฐานสำหรับการต่อเนื่องของ Apple Universal Link
    func application(
        _ application: UIApplication,
        continue userActivity: NSUserActivity,
        restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
    ) -> Bool {
        
        guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
              let incomingURL = userActivity.webpageURL else {
            return false
        }
        
        print("กำลังจัดการ Universal Link ที่ตรวจสอบแล้ว: \(incomingURL.absoluteString)")
        
        // ส่งต่อ incomingURL ไปยังเราเตอร์ภายในหรือเลเยอร์ SDK เพื่อแยกพารามิเตอร์
        return handleIncomingRoute(incomingURL)
    }

    private func handleIncomingRoute(_ url: URL) -> Bool {
        // ตรรกะการกำหนดเส้นทางปลายทางระดับแอปพลิเคชัน
        // หมายเหตุ: การคืนค่า true หมายความว่าแอปจัดการกิจกรรมนั้นแล้ว ไม่ได้แปลว่าการแยกวิเคราะห์ URL สำเร็จ
        return true
    }
}

// ----------------------------------------------------------------------------
// 2. การใช้งาน SceneDelegate Lifecycle (iOS 13+)
// ----------------------------------------------------------------------------
class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        if let userActivity = connectionOptions.userActivities.first(where: { $0.activityType == NSUserActivityTypeBrowsingWeb }),
           let incomingURL = userActivity.webpageURL {
            print("เปิดแอปแบบ Cold-launch ด้วย Universal Link: \(incomingURL.absoluteString)")
        }
    }

    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
           let incomingURL = userActivity.webpageURL {
            print("เปิดแอปแบบ Foreground ด้วย Universal Link: \(incomingURL.absoluteString)")
        }
    }
}

วิธีเปิดใช้งาน Developer Mode ฝั่งไคลเอ็นต์บนฮาร์ดแวร์จริง:

  1. บน iOS 16 ขึ้นไป ให้ไปที่ การตั้งค่า > ความเป็นส่วนตัวและความปลอดภัย > โหมดนักพัฒนา (Settings > Privacy & Security > Developer Mode) และเปิดสวิตช์เป็น ON (ต้องรีสตาร์ทอุปกรณ์)
  2. ไปที่ การตั้งค่า > นักพัฒนาซอฟต์แวร์ > การพัฒนาโดเมนที่เกี่ยวข้อง (Settings > Developer > Associated Domains Development) และเปิดสวิตช์เป็น ON
  3. ติดตั้งบิวด์สำหรับการพัฒนาที่เซ็นชื่อด้วยโปรไฟล์การจัดเตรียมสำหรับการพัฒนาซึ่งมีสิทธิ์ ?mode=developer
  4. หมายเหตุสำหรับการผลิต (Production Note): จำกัดการใช้ ?mode=developer ไว้เฉพาะสำหรับการพัฒนาและการกำหนดค่าการทดสอบภายในเท่านั้น และห้ามรวมไว้ในสิทธิ์ associated-domains สำหรับการใช้งานจริง เว้นแต่การปรับของคุณจะต้องการและรองรับการกำหนดค่าดังกล่าวอย่างชัดเจน

แผนผังการตัดสินใจเพื่อหาสาเหตุหลัก (Root Cause Decision Tree)

Universal Link falls back to web handling
        │
        ├── Does signed application-identifier match AASA appIDs?
        │       ├── NO ──> Correct App ID Prefix or Bundle ID in AASA
        │       └── YES
        │
        ├── Does associated-domains entitlement list the exact domain?
        │       ├── NO ──> Add applinks:<domain> to target entitlements
        │       └── YES
        │
        ├── Does sudo swcutil dl -d <domain> succeed?
        │       ├── NO ──> Fix origin HTTPS, TLS certificates, or 301/302 redirects
        │       └── YES
        │
        ├── Does sudo swcutil verify match the target URL path?
        │       ├── NO ──> Correct components or paths syntax in AASA
        │       └── YES
        │
        └── Check device association state and internal application routing handlers
แผนผังการตัดสินใจฟลอชาร์ตทางเทคนิคสำหรับการวินิจฉัยสาเหตุหลักที่ทำให้ iOS Universal Link ตกลงไปใช้เว็บ ครอบคลุมตั้งแต่สิทธิ์ไบนารี สคีมา AASA และการแคช CDN บนพื้นหลังลายตารางสีครีมอุ่น

ตารางวินิจฉัย: สาเหตุหลักของความล้มเหลวใน Universal Link

รูปแบบความล้มเหลว สาเหตุหลักที่แท้จริง พฤติกรรมระบบที่สังเกตพบ การแก้ไขที่แนะนำ
พิมพ์ Bundle ID ผิด ความไวต่อตัวพิมพ์เล็ก-ใหญ่ หรือตัวอักษรไม่ตรงกันใน AASA appIDs ลิงก์เปิดเบราว์เซอร์แทนที่จะเป็นแอปเนทีฟ แก้ไขสตริงใน AASA JSON และdeploy ไปยังต้นทางใหม่
App ID Prefix ไม่ตรงกัน ใช้คำนำหน้าผิดแทนที่จะเป็น Developer App ID Prefix ที่แท้จริง การเชื่อมโยงโดเมนล้มเหลวระหว่างการติดตั้ง ตรวจสอบ Application Identifier Prefix ใน Apple Member Center
Subdomain ไม่ตรงกัน สิทธิ์ชี้ไปที่ www.example.com ในขณะที่ AASA อยู่บน example.com แอปไม่สามารถเคลมลิงก์จากซับโดเมนได้ โฮสต์ไฟล์ AASA เฉพาะบนแต่ละซับโดเมนที่เคลม หรือกำหนดค่า wildcard
HTTP Redirect บน Endpoint เซิร์ฟเวอร์ต้นทางส่งคืนการเปลี่ยนเส้นทาง 301 หรือ 302 สำหรับ AASA URL Apple CDN scraper ปฏิเสธไฟล์ AASA กำหนดค่าเว็บเซิร์ฟเวอร์ให้คืนค่า 200 OK โดยตรง
รูปแบบ AASA ไม่สอดคล้องกัน ผสมผสาน appID/paths แบบเดิมเข้ากับ appIDs/components สมัยใหม่ การจับคู่พาธไม่สอดคล้องกันหรือไม่สมบูรณ์ ใช้มาตรฐานไวยากรณ์ appIDs + components แบบสมัยใหม่
รูปแบบ URL ไม่ตรงกัน ดาวน์โหลด AASA สำเร็จแต่ URL ที่ร้องขอไม่ตรงกับแพทเทิร์น ลิงก์เปิดในเว็บเบราว์เซอร์ ตรวจสอบไวยากรณ์พาธและ components โดยใช้ swcutil verify
ทิ้ง Developer Mode ไว้ในบิวด์ใช้งานจริง บิวด์สำหรับการจัดจำหน่ายยังคงมีโหมดสำรองสำหรับการพัฒนา มีสิทธิ์ที่ไม่ใช่มาตรฐานในบิวด์สำหรับการจัดจำหน่าย ลบ ?mode=developer ออกจากการกำหนดค่าบิวด์ Release

แผนภูมิเมทริกซ์เปรียบเทียบระดับองค์กรระหว่างประเทศ แสดงรูปแบบความล้มเหลวของ iOS Universal Link สาเหตุหลัก พฤติกรรมของระบบ และขั้นตอนการแก้ไข พร้อมป้ายสถานะที่ชัดเจนบนพื้นหลังลายตารางสีครีมละมุน

การใช้งานการกำหนดค่าแบบสองสภาพแวดล้อม (Dual-Environment) ใน Xcode

การจัดการการกำหนดค่าการสร้างหลายรายการ (Debug, Staging, Production)

ไปป์ไลน์การพัฒนาขององค์กรมักจะจัดการ Bundle ID ที่แตกต่างกันไปตามสภาพแวดล้อมการสร้าง (เช่น com.example.app.debug, com.example.app.staging, com.example.app)

เพื่อรักษาการทำงานของ Universal Links ให้ใช้งานได้ในทุกสภาพแวดล้อมการสร้าง:

  • การประกาศ AASA อย่างชัดเจน (Explicit AASA Declarations): ไฟล์ AASA ที่โฮสต์อยู่ต้องแสดง Application Identifier ที่สมบูรณ์ของแต่ละสภาพแวดล้อมไว้อย่างชัดเจนในอาร์เรย์ appIDs:

    "appIDs": [
      "9JA723G82S.com.example.app",
      "9JA723G82S.com.example.app.staging",
      "9JA723G82S.com.example.app.debug"
    ]
    
    
  • สิทธิ์เฉพาะเป้าหมาย (Target-Specific Entitlements): ใช้การตั้งค่าการกำหนดค่าการสร้างของ Xcode เพื่อเชื่อมโยงไฟล์ .entitlements ที่แตกต่างกันต่อการกำหนดค่าการสร้างแต่ละรายการ เพื่อให้มั่นใจว่าบิวด์ดีบักภายในจะไม่ทำการคิวรีโดเมนสำหรับใช้งานจริง (production)

การจัดการตัวระบุเป้าหมาย (Target Identifiers)

สำหรับการแก้ไขปัญหา Universal Links ให้ใช้ Bundle ID และ Application Identifier Prefix ที่แน่นอนจากบิวด์ที่เซ็นชื่อแล้ว แทนที่จะพึ่งพาตัวระบุแบบ wildcard จัดการกับแต่ละชื่อโฮสต์อย่างชัดเจน: หากแอปเคลม example.com และ www.example.com ให้กำหนดค่ารายการ associated-domain ที่สอดคล้องกัน และตรวจสอบให้แน่ใจว่าแต่ละชื่อโฮสต์ให้บริการข้อมูล AASA ที่เหมาะสม ตรวจสอบให้แน่ใจว่าได้กำหนดค่าสิทธิ์บนเป้าหมายที่จัดการ Universal Links จริงๆ และตรวจสอบเป้าหมาย app-extension หรือ watchOS แยกต่างหากเมื่อมีใช้งาน

การตรวจสอบความถูกต้องของ Provisioning Profiles ที่ฝังอยู่และไบนารีที่เซ็นชื่อใน CI/CD

ทำให้กระบวนการตรวจสอบสิทธิ์และ Application Identifier เป็นแบบอัตโนมัติภายในสคริปต์การสร้างแบบต่อเนื่อง (CI) ก่อนอัปโหลดไบนารีไปยัง TestFlight:

# สคริปต์ตรวจสอบความถูกต้องอัตโนมัติสำหรับ CI
security cms -D -i /path/to/embedded.mobileprovision > provision.plist

# 1. ตรวจสอบสิทธิ์ของโปรไฟล์สำหรับ Associated Domains ที่ได้รับอนุญาต
/usr/libexec/PlistBuddy -c "Print :Entitlements:com.apple.developer.associated-domains" provision.plist

# 2. แยกสิทธิ์ที่มีลายเซ็นจริงจากไฟล์ไบนารีปฏิบัติการที่คอมไพล์แล้ว
codesign -d --entitlements :- "UnpackedApp/Payload/YourApp.app" > signed-entitlements.plist 2>/dev/null
SIGNED_APP_ID=$(/usr/libexec/PlistBuddy -c "Print :application-identifier" signed-entitlements.plist)
echo "ดึงข้อมูล Application Identifier ที่มีลายเซ็นแล้ว: $SIGNED_APP_ID"

# 3. ตรวจสอบว่า Associated Domains ที่เซ็นชื่อตรงกับโดเมนเป้าหมาย
/usr/libexec/PlistBuddy -c "Print :com.apple.developer.associated-domains" signed-entitlements.plist

# 4. ตรวจสอบว่า App ID ที่มีลายเซ็นมีอยู่ในไฟล์ AASA ที่โฮสต์ผ่าน Python
python3 -c "
import json, sys
signed_id = sys.argv[1]
data = json.load(open('apple-app-site-association'))
app_ids = [app for detail in data.get('applinks', {}).get('details', []) for app in detail.get('appIDs', [])]
if signed_id not in app_ids:
    print(f'AASA mismatch: {signed_id} not found in AASA appIDs: {app_ids}')
    sys.exit(1)
print(f'AASA consistency check passed: {signed_id} registered')
" "$SIGNED_APP_ID"

หากสคริปต์การตรวจสอบความถูกต้องออกด้วยรหัสข้อผิดพลาด ให้ยกเลิกไปป์ไลน์การสร้างเพื่อป้องกันการส่งมอบไบนารี deep linking ที่ใช้งานไม่ได้ไปยังระบบผลิตจริง (production)

ฟลอชาร์ตเวิร์กโฟลว์นักพัฒนา 4 ขั้นตอนสากลสำหรับการทำงานอัตโนมัติในการตรวจสอบ iOS Universal Link App ID และ AASA ในไปป์ไลน์การสร้าง CI/CD บนพื้นหลังลายตารางสีครีมอุ่นละมุน

เกณฑ์การจับคู่ Universal Link

เพื่อให้แน่ใจว่าการกำหนดเส้นทางมีความน่าเชื่อถือ จะต้องเป็นไปตามเงื่อนไขต่อไปนี้พร้อมๆ กัน:

Signed App Configuration:
application-identifier = <ApplicationIdentifierPrefix>.<CFBundleIdentifier>
com.apple.developer.associated-domains = applinks:<hostname>

AASA Configuration:
appIDs = [..., "<ApplicationIdentifierPrefix>.<CFBundleIdentifier>", ...]
components / paths = Matching target URL paths and query parameters

System Eligibility:
1. Associated Domains entitlement explicitly contains the target hostname.
2. The signed Application Identifier matches an authorized entry in the domain's AASA appIDs.
3. The incoming URL satisfies the AASA routing patterns.
4. Device association state and user/browser context permit native application delegation.

แม้ว่าสิทธิ์ (entitlement) การเชื่อมโยง AASA และรูปแบบ URL จะตรงกันทั้งหมด แต่พฤติกรรมการกำหนดเส้นทางที่สังเกตได้ก็ยังคงขึ้นอยู่กับสถานะของอุปกรณ์และบริบทของผู้ใช้หรือเบราว์เซอร์ ตัวอย่างเช่น เมื่อผู้ใช้แตะยูนิเวอร์แซลลิงก์ในขณะที่กำลังเรียกดูโดเมนเดียวกันนั้นอยู่ใน Safari ระบบปฏิบัติการอาจเคารพเจตนาของผู้ใช้ที่จะอยู่ใน Safari ต่อไป

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

รูปแบบที่แน่นอนของ application identifier ในไฟล์ AASA คืออะไร?
Application identifier จะต้องจัดรูปแบบตามโครงสร้าง `<ApplicationIdentifierPrefix>.<CFBundleIdentifier>` อย่างเคร่งครัด โดยที่ `<ApplicationIdentifierPrefix>` คือคำนำหน้า App ID ที่เชื่อมโยงกับแอปพลิเคชันในบัญชี Apple Developer ของคุณ (เช่น `9JA723G82S`) และ `<CFBundleIdentifier>` คือ Bundle ID (เช่น `com.example.app`) ซึ่งจะส่งผลให้ได้ค่าเป็น `9JA723G82S.com.example.app` อย่าสมมติว่าคำนำหน้าจะเหมือนกับ Team ID เสมอไป ให้ตรวจสอบค่าดังกล่าวจาก provisioning profile ของแอป
ทำไม Universal Link ของฉันถึงทำงานใน Developer Mode ได้ แต่ล้มเหลวในการใช้งานจริง (Production)?
Developer Mode (`?mode=developer`) ช่วยให้อุปกรณ์สำหรับการพัฒนาที่มีคุณสมบัติเหมาะสมสามารถข้าม Apple-managed CDN และดึงไฟล์ AASA จากเซิร์ฟเวอร์เว็บต้นทางผ่าน HTTPS ได้โดยตรง หาก Universal Links ล้มเหลวในการใช้งานจริง สาเหตุทั่วไปได้แก่ ใบรับรอง TLS บนเซิร์ฟเวอร์ต้นทางไม่ถูกต้อง มีการเปลี่ยนเส้นทาง HTTP (HTTP redirect) บน endpoint ของ AASA หรือข้อมูล AASA ในการใช้งานจริงมีข้อผิดพลาดในการจัดรูปแบบซึ่งถูกปฏิเสธโดย CDN scraper ของ Apple
ฉันสามารถใช้ออกแบบ wildcard (เครื่องหมายดอกจัน) ในอาร์เรย์ appIDs ของ AASA ได้หรือไม่?
สำหรับการแก้ไขปัญหา Universal Link ให้ใช้ Application Identifier ที่ชัดเจนจากแอปที่เซ็นชื่อแล้ว (`<App ID Prefix>.<Bundle ID>`) และประกาศตัวระบุนั้นในการกำหนดค่า AASA ห้ามใช้ wildcard เป็นตัวแทนของตัวระบุจริงของแอปพลิเคชัน

สรุปและกรอบการตัดสินใจ

ความน่าเชื่อถือในการกำหนดเส้นทางของ Universal Link ขึ้นอยู่กับการตรงกันในระดับตัวอักษรอย่างแม่นยำใน 3 จุด ได้แก่ การกำหนดค่า App ID ใน Apple Developer Portal, สิทธิ์ com.apple.developer.associated-domains ใน Xcode และไฟล์ JSON apple-app-site-association ที่โฮสต์อยู่ SDK ของบุคคลที่สามหรือเฟรมเวิร์กการกำหนดเส้นทางไม่สามารถซ่อมแซมการเชื่อมโยงโดเมนของระบบปฏิบัติการที่ล้มเหลวได้ ทำได้เพียงประมวลผล URL หลังจากที่ iOS ส่งมอบ Universal Link ให้กับแอปพลิเคชันสำเร็จแล้วเท่านั้น หาก Universal Links เชื่อมโยงได้อย่างถูกต้องในระดับระบบปฏิบัติการแต่การแยกพารามิเตอร์ล้มเหลว ให้ตรวจสอบเลเยอร์การกำหนดเส้นทางระดับแอปพลิเคชันแยกต่างหากจากเลเยอร์การเชื่อมโยงโดเมน

หากแอปพลิเคชันของคุณต้องการการกู้คืนพารามิเตอร์ไดนามิกส์ลิงก์และการกำหนดเส้นทางต้อนรับ (onboarding routing) หลังจากเชื่อมโยง Universal Link สำเร็จ OpoInstall มีเลเยอร์ SDK เสริมสำหรับเวิร์กโฟลว์ระดับแอปพลิเคชันนั้นๆ

หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับรูปแบบการกำหนดค่าโดเมนและการรวม deep linking โปรดตรวจสอบ เอกสารประกอบ deep linking ของ OpoInstall

วัสดุและข้อมูลอ้างอิงที่เกี่ยวข้อง

  • แนวคิด (Concepts): การตรวจสอบ Application Identifier, การตรวจสอบความถูกต้องของสคีมา AASA, การแคชผ่าน Apple CDN, การดึงข้อมูลสิทธิ์ (Entitlement Extraction)

  • เทคโนโลยี (Technologies): iOS Universal Links, สิทธิ์การใช้งาน Xcode, Apple Developer Portal, Shared Web Credentials

  • มาตรฐาน (Standards): IETF RFC 8259 (การแลกเปลี่ยนข้อมูล JSON), ข้อกำหนด TLS 1.3

  • เครื่องมือวินิจฉัย (Diagnostic Tools): เครื่องมือ CLI Apple codesign, เครื่องมือ macOS swcutil, การคิวรีแคช CDN ที่จัดการโดย Apple

เอกสารประกอบอย่างเป็นทางการ

Share this article

Keep Discovering

OpenAI เพิ่มฟีเจอร์ควบคุม Apple Messages ให้กับ ChatGPT บน Mac

OpenAI เพิ่มฟีเจอร์ควบคุม Apple Messages ให้กับ ChatGPT บน Mac

OpenAI เพิ่มการรองรับ Apple Messages บน ChatGPT สำหรับ Mac เจาะลึกว่าสิทธิ์การใช้งานใหม่ การอนุมัติการส่งข้อความเริ่มต้น และระบบอัตโนมัติของ macOS ส่งผลต่อความปลอดภัยของระบบเอเจนต์อย่างไร

Alibaba Qwen UI Agent ควบคุมโทรศัพท์จริงได้ครอบคลุมกว่า 150 แอปพลิเคชัน

Alibaba Qwen UI Agent ควบคุมโทรศัพท์จริงได้ครอบคลุมกว่า 150 แอปพลิเคชัน

Alibaba Qwen UI Agent ควบคุมโทรศัพท์จริงได้ครอบคลุมกว่า 150 แอปพลิเคชัน อ่านบทวิเคราะห์ทางเทคนิคของโมเดลรากฐาน GUI นี้และผลการทดสอบมาตรฐาน

วิธีใช้งาน PrivacyInfo.xcprivacy สำหรับแอปพลิเคชันและ SDK บน iOS

วิธีใช้งาน PrivacyInfo.xcprivacy สำหรับแอปพลิเคชันและ SDK บน iOS

เรียนรู้วิธีการใช้งาน PrivacyInfo.xcprivacy สำหรับแอปและ SDK บน iOS: การประกาศ API ที่ต้องระบุเหตุผล (Required Reason APIs), การสร้างรายงานความเป็นส่วนตัว (Privacy Reports) และการแก้ไขข้อผิดพลาดการลงนาม CocoaPods