feat(diagnostics): iOS Universal Links validation (apple-app-site-association)#35
Merged
Conversation
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
applinkssection —webcredentialsandappclipsare 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 schemasAasaValidation/AasaStatus/AasaIssue— mirror the Android assetlinks result shape so the UI scaffold is reusableInfrastructure (
infrastructure/network/AasaClient.kt)/.well-known/apple-app-site-associationfirst; falls back to the legacy root path only on 404 so a real SSL/network failure on the recommended path isn't maskedapplication/jsonandapplication/pkcs7-mimeas acceptable Content-Types (Apple historically signed AASAs)Data
AasaParser(hand-rolled onJsonObjectbecause iOS 14+ uses/,?,#keys that aren't valid Kotlin identifiers)appID+paths), iOS 14+ (appIDs+components), and mixed entries within the samedetailsarrayAasaRepositoryImplflags root-path fallback, redirects, missingapplinks, emptydetails, malformed app IDs, missing patterns, non-emptyappsarray, etc.UseCase + DI
ValidateAasaUseCase+ AppContainer wiringUI (
ui/screen/diagnostics/)DiagnosticsPlatformenum +SegmentedButtontoggle at the top of the AssetLinks tabvalidateAndroid/validateIos; switching platform clears the previous result so the panel never mixes apples (assetlinks.json) with oranges (AASA)webcredentials/appclipsappIDs,paths(with a "Legacy" badge per-detail), andcomponents(with/ ? # exclude caseSensitive percentEncodedformatted compactly). Excluded components are tinted to stand out.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 JSONTest plan
./gradlew :composeApp:compileKotlinJvm→ BUILD SUCCESSFUL./gradlew :composeApp:jvmTest→ all green (incl. 10 new AasaParserTest cases)apple.com,airbnb.com,notion.so) and confirm parsing/issue surfacesROOT_PATH_FALLBACKwarningNOT_FOUNDrendered cleanlyOut of scope (follow-up)
webcredentials/appclipsdeep validation