Feat/deeplinkshare#387
Conversation
…ncies and Android/iOS configurations for universal links.
Confidence Score: 3/5Not safe to merge as-is: Universal Links will be broken in production iOS builds, and cold-start deep links can cause a double navigation for unauthenticated users. Two concrete defects are introduced:
Reviews (2): Last reviewed commit: "Add deep linking support in Android and ..." | Re-trigger Greptile |
| bool _isOpenLink(Uri uri) { | ||
| final isHttps = | ||
| (uri.scheme == 'https') && (uri.host == 'webuddhist.com') && (uri.path == '/open'); | ||
| final isCustom = | ||
| (uri.scheme == 'webuddhist') && (uri.host == 'open'); | ||
| return isHttps || isCustom; |
There was a problem hiding this comment.
Unreachable custom-scheme branch
The webuddhist://open custom URI scheme is checked here but is registered on neither platform — the Android manifest only declares an App Link for https://webuddhist.com/open, and the iOS entitlements only add applinks:webuddhist.com (Universal Links). The OS will never deliver a webuddhist:// URI to the app, so the isCustom branch can never evaluate to true. Leaving it in place misleads future developers into thinking the scheme is already wired up, or into adding a platform entry expecting the routing to be handled.
| /// Universal link — opens the app when installed, redirects to the | ||
| /// correct store (via your hosted /open page) when not installed. | ||
| static const String _deepLinkUrl = 'https://webuddhist.com/open'; |
There was a problem hiding this comment.
Web fallback assumed but not confirmed
The comment states that https://webuddhist.com/open "redirects to the correct store (via your hosted /open page) when not installed." If no web page actually lives at that path, any user without the app — on desktop browsers, unsupported platforms, or before the web-side is deployed — will land on a 404 rather than being sent to the App Store / Play Store. The previous implementation explicitly embedded both store URLs as a plain-text fallback, so this regression would silently break acquisition for users who don't already have the app installed. Is https://webuddhist.com/open already a live web page that sniffs the platform and redirects to the correct store, or is that still pending deployment?
| <array> | ||
| <string>webcredentials:dev-vz6o17motc18g45h.us.auth0.com</string> | ||
| <string>applinks:webuddhist.com</string> |
There was a problem hiding this comment.
Universal Links only wired for dev builds
The Xcode project uses Runner.entitlements for every non-dev configuration (Release, Profile, Staging). applinks:webuddhist.com was added only to Runner-dev.entitlements, so Universal Links will silently fail on all production and staging iOS builds — the OS will open https://webuddhist.com/open in Safari instead of launching the app. Runner.entitlements needs the same applinks:webuddhist.com entry.
| ref.watch(notificationSyncBootstrapProvider); | ||
| NotificationService.setRouter(router); | ||
| NotificationService().consumeLaunchNotification(); | ||
| DeepLinkService.instance.setRouter(router); |
There was a problem hiding this comment.
Double navigation on cold start
Both FlutterDeepLinkingEnabled = true (iOS) / flutter_deeplinking_enabled (Android) and DeepLinkService process the same incoming link independently. On cold start with https://webuddhist.com/open, go_router receives the URI as the initial location (routing /open → /home), and then setRouter fires a second _router!.go('/home') from the pending URI. For authenticated users this is a benign duplicate; for unauthenticated users the auth guard redirects to splash once from go_router's pass, and then immediately again from the DeepLinkService pass, causing a visible navigation flash or double-splash. Consider clearing _pendingUri in initialize() when FlutterDeepLinkingEnabled is active so the service is only used as a warm-start listener, leaving cold-start routing entirely to go_router.
Deep Link Implementation Documentation
Overview
This document describes the deep link functionality implemented for the WeBuddhist app, which enables users to share the app and have recipients directed to the app (if installed) or the appropriate app store (if not installed) via a universal link.
Purpose
The deep link implementation serves the following purposes:
https://webuddhist.com/open) that works across platformsArchitecture
Components
The implementation consists of several interconnected components:
/openroutehttps://webuddhist.com/openImplementation Details
1. User Interface Components
Home Share Prompt Widget
Location:
lib/features/home/presentation/widgets/home_share_prompt.dartA prominent call-to-action displayed at the bottom of the home screen feed that invites users to share the app.
Features:
Placement:
SliverToBoxAdapterhome_screen.dartat line 3592. Service Layer
App Share Service
Location:
lib/core/services/app_share/app_share_service.dartHandles the generation and sharing of the app invitation message.
Key Configuration:
Share Message Format:
Functionality:
generateShareMessage(): Creates a localized share message with the deep linkshareApp(): Triggers the native share sheet with the generated messageshare_pluspackage for cross-platform sharingProvider:
Deep Link Service
Location:
lib/core/deep_linking/deep_link_service.dartA singleton service that listens for and handles incoming Universal Links (iOS) and App Links (Android).
Entry Points:
Key Methods:
initialize()- Called inmain()beforerunApp()AppLinks.getInitialLink()setRouter(GoRouter)- Called fromMyApp.build()after router initialization_handleDeepLink(Uri)- Internal routing logic/homefor unhandled linksSupported Link Formats:
https://webuddhist.com/openwebuddhist://openCurrent Routes:
/open→ Navigates to/home(main entry point)3. Routing Configuration
App Router
Location:
lib/core/config/router/app_router.dartThe
/openroute is registered in the main GoRouter configuration:Purpose:
DeepLinkServicefor cold-start scenariosRoute Categories
Location:
lib/core/config/router/app_routes.dartThe
/openroute is categorized as a public route:This means:
4. Platform Configuration
iOS Configuration
Universal Links Setup:
Runner-dev.entitlements):Runner.entitlements):applinks:webuddhist.comentry. This should be added for production deep links to work properly.Required for Universal Links:
https://webuddhist.com/.well-known/apple-app-site-associationapplication/jsonAndroid Configuration
App Links Setup:
Location:
android/app/src/main/AndroidManifest.xmlKey Attributes:
android:autoVerify="true": Enables automatic verification of the App Linkandroid:launchMode="singleTask": Ensures only one instance of the activityRequired for App Links:
https://webuddhist.com/.well-known/assetlinks.jsonapplication/json5. Initialization Flow
Application Startup Sequence
Location:
lib/main.dartIn MyApp Widget:
Initialization Timeline:
DeepLinkService.initialize()captures cold-start linksUser Flow
Complete User Journey
Scenario A: Sharing the App
Scenario B: Recipient Has App Installed
https://webuddhist.com/openDeepLinkServiceintercepts the URI/homescreenScenario C: Recipient Without App
https://webuddhist.com/openwebuddhist.com/openis loadedTechnical Dependencies
Flutter Packages
Error Handling
Deep Link Service Error Handling
AppLogger/homeShare Service Error Handling
shareApp()Future Enhancements
The deep link system is designed to be extensible. Future link types could include:
Planned Routes
https://webuddhist.com/plans/{planId}/invitehttps://webuddhist.com/reader/{textId}?segment={segmentId}https://webuddhist.com/groups/{groupId}/joinhttps://webuddhist.com/verses/{verseId}Implementation Pattern
To add a new deep link route:
Known Issues & Limitations
iOS Production Configuration
Issue: The production entitlements file (
ios/Runner/Runner.entitlements) is missing theapplinks:webuddhist.comentry.Impact: Deep links will not work in production iOS builds.
Fix Required:
Universal Link Debugging
Challenge: Universal Links can be difficult to test and debug on iOS simulators.
Best Practices:
Custom Scheme Fallback
Limitation: Custom scheme (
webuddhist://open) requires the app to be installed.Behavior:
Recommendation: Always use HTTPS universal/app links as primary method.
Related Files
Core Implementation
lib/core/deep_linking/deep_link_service.dart- Deep link servicelib/core/services/app_share/app_share_service.dart- App share servicelib/core/config/router/app_router.dart- Router configurationlib/core/config/router/app_routes.dart- Route definitionslib/main.dart- App initializationUI Components
lib/features/home/presentation/widgets/home_share_prompt.dart- Share prompt widgetlib/features/home/presentation/widgets/verse_share_sheet.dart- Verse share bottom sheetlib/features/home/presentation/screens/home_screen.dart- Home screen integrationPlatform Configuration
android/app/src/main/AndroidManifest.xml- Android deep link configios/Runner/Runner.entitlements- iOS production entitlementsios/Runner/Runner-dev.entitlements- iOS development entitlementsInfrastructure Requirements (External)
https://webuddhist.com/open- Web landing pagehttps://webuddhist.com/.well-known/apple-app-site-association- iOS AASA filehttps://webuddhist.com/.well-known/assetlinks.json- Android Digital Asset LinksConclusion
This deep link implementation provides a seamless sharing and acquisition flow for WeBuddhist. Users can easily share the app with friends, and recipients are intelligently routed to either the app (if installed) or the appropriate app store (if not).
The architecture is extensible and ready for additional deep link routes as the app grows. Key areas for attention are: