Skip to content

feat(diagnostics): iOS Universal Links validation (apple-app-site-association)#35

Merged
manjees merged 1 commit into
mainfrom
feature/issue-30
May 15, 2026
Merged

feat(diagnostics): iOS Universal Links validation (apple-app-site-association)#35
manjees merged 1 commit into
mainfrom
feature/issue-30

Conversation

@manjees

@manjees manjees commented May 15, 2026

Copy link
Copy Markdown
Owner

Closes #30.

Summary

Adds an Android/iOS toggle to the Diagnostics screen so the same domain can be validated for both platforms in-place. MVP scope is the applinks section — webcredentials and appclips are surfaced as presence flags only.

Why this matters: real-world deep-link debugging is almost always a pair problem ("works on Android, broken on iOS" or vice versa). LinkOps was previously Android-only, so the moment a user needed to check the iOS side they had to leave the tool. This PR keeps them in.

What's in

Domain (domain/model/AppleAppSiteAssociation.kt)

  • AppleAppSiteAssociation / AppLinksSection / AppLinkDetail / AasaPathComponent — normalize both AASA schemas
  • AasaValidation / AasaStatus / AasaIssue — mirror the Android assetlinks result shape so the UI scaffold is reusable

Infrastructure (infrastructure/network/AasaClient.kt)

  • Tries /.well-known/apple-app-site-association first; falls back to the legacy root path only on 404 so a real SSL/network failure on the recommended path isn't masked
  • Treats application/json and application/pkcs7-mime as acceptable Content-Types (Apple historically signed AASAs)
  • No automatic redirect following — Apple rejects redirected AASAs, so we want to surface them

Data

  • AasaParser (hand-rolled on JsonObject because iOS 14+ uses /, ?, # keys that aren't valid Kotlin identifiers)
  • Handles legacy (appID + paths), iOS 14+ (appIDs + components), and mixed entries within the same details array
  • AasaRepositoryImpl flags root-path fallback, redirects, missing applinks, empty details, malformed app IDs, missing patterns, non-empty apps array, etc.

UseCase + DI

  • ValidateAasaUseCase + AppContainer wiring

UI (ui/screen/diagnostics/)

  • DiagnosticsPlatform enum + SegmentedButton toggle at the top of the AssetLinks tab
  • ViewModel branches into validateAndroid / validateIos; switching platform clears the previous result so the panel never mixes apples (assetlinks.json) with oranges (AASA)
  • Capability chips on the iOS status card show whether the domain also ships webcredentials / appclips
  • iOS detail cards show appIDs, paths (with a "Legacy" badge per-detail), and components (with / ? # exclude caseSensitive percentEncoded formatted compactly). Excluded components are tinted to stand out.
  • History entries carry their platform and replay on the original platform when clicked

Tests

AasaParserTest — 10 cases covering iOS 14+ schema, legacy schema, mixed entries, missing applinks, empty details, non-empty apps, missing paths/components, malformed app ID, appclips presence, malformed JSON

Test plan

  • ./gradlew :composeApp:compileKotlinJvm → BUILD SUCCESSFUL
  • ./gradlew :composeApp:jvmTest → all green (incl. 10 new AasaParserTest cases)
  • Manual: hit a real AASA-shipping domain (e.g. apple.com, airbnb.com, notion.so) and confirm parsing/issue surfaces
  • Manual: hit a domain serving AASA from the root path only — confirm ROOT_PATH_FALLBACK warning
  • Manual: hit a domain with no AASA — confirm NOT_FOUND rendered cleanly
  • Manual: toggle Android ↔ iOS rapidly — confirm result panel clears each switch and history shows correct platform badge

Out of scope (follow-up)

  • Side-by-side Android+iOS pair view in Verification Deep Dive (the harder part of the original issue, intentionally split out)
  • AASA signing / pkcs7-mime payload extraction
  • webcredentials / appclips deep validation

…ociation)

Adds an Android/iOS toggle to the Diagnostics screen so the same domain can be
validated for both platforms back-to-back. MVP scope is the applinks section —
webcredentials and appclips are surfaced as presence flags so the developer
knows what else the domain ships without us promising to validate them yet.

Domain
- AppleAppSiteAssociation, AppLinksSection, AppLinkDetail, AasaPathComponent —
  shape the union of legacy (appID + paths) and iOS 14+ (appIDs + components)
  schemas, with `usesLegacySchema` preserved per detail
- AasaValidation / AasaStatus / AasaIssue mirror the Android assetlinks side
  so the UI renders both with the same scaffold

Infrastructure
- AasaClient tries `/.well-known/apple-app-site-association` first, falls back
  to the legacy root path only on 404 (any other failure on .well-known is a
  real signal we want to preserve)
- Treats `application/json` and `application/pkcs7-mime` as acceptable
  Content-Types (Apple historically signed AASAs)

Data
- AasaParser hand-rolls on JsonObject because the iOS 14+ component schema
  uses `/`, `?`, `#` keys that aren't valid Kotlin identifiers. Both schemas
  coexist in details and both round-trip through the same model
- AasaRepositoryImpl flags root-path fallback, redirects, missing applinks,
  empty details, malformed app IDs, and per-detail missing patterns

UI
- Diagnostics screen gains a Platform SegmentedButton at the top of the
  AssetLinks tab; switching platforms clears the previous result panel
- Capability chips (applinks / webcredentials / appclips) on the iOS status
  card make it obvious what the domain ships beyond Universal Links
- History entries carry their platform; clicking one replays the validation
  on the original platform

Tests
- AasaParser covered for: iOS 14+ schema, legacy schema, mixed entries in one
  details array, missing applinks, empty details, non-empty apps, missing
  paths/components, malformed app ID, appclips presence, malformed JSON

Closes #30.
@manjees manjees added enhancement New feature or request ui User interface and visual improvements labels May 15, 2026
@manjees manjees merged commit 15cf565 into main May 15, 2026
2 checks passed
@manjees manjees deleted the feature/issue-30 branch May 15, 2026 05:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request ui User interface and visual improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: iOS Universal Links 지원 — Android/iOS 페어 디버깅

1 participant