Composite Specs¶
DeepMatch supports both:
- Multiple deeplink specs in one module.
- Multiple Android modules, each with its own DeepMatch-generated processor.
How to Structure Specs¶
Each module can define:
- Module root sources:
.deeplinks.ymland*.deeplinks.yml - Variant sources:
src/<variant>/.deeplinks.ymlandsrc/<variant>/*.deeplinks.yml
Discovery and merge rules:
- Root files are loaded first (sorted by file name).
- Variant files are loaded next (sorted by file name).
- If two sources define the same spec
name, the later source overrides the earlier one.
Multi-Module Setup¶
Example project:
Apply the plugin in each participating module:
plugins {
id("com.android.library") // or com.android.application
// AGP 9+: Kotlin is built into AGP (do not apply org.jetbrains.kotlin.android)
// AGP 8.x and below: add id("org.jetbrains.kotlin.android")
id("com.aouledissa.deepmatch.gradle")
}
If :app depends on :feature-profile and :feature-series, the app's generated processor
automatically composes their generated processors.
No extra plugin configuration is required.
What Gets Generated¶
For each module:
<ModuleName>DeeplinkParams(sealed interface)<ModuleName>DeeplinkProcessor(generated processor object)- One
*DeeplinkSpecs+ one*DeeplinkParamsper spec
For app modules with DeepMatch-enabled dependencies, <ModuleName>DeeplinkProcessor is generated
as a CompositeDeeplinkProcessor.
Resolution Order¶
When you call:
DeepMatch resolves in this order:
- Local module specs are evaluated first.
- Local specs come from merged discovered files (root first, then variant with override-by-name).
- If local specs do not match, composed dependency processors are tried (deterministic order).
- The first non-null match wins.
- If nothing matches, result is
null.
Practical Implication¶
If two modules can match the same URI shape, precedence is determined by composition order. Dependency processors are composed in deterministic FQCN order. Keep specs unique across modules whenever possible to avoid ambiguous ownership.
Example¶
when (val params = AppDeeplinkProcessor.match(uri)) {
is OpenProfileDeeplinkParams -> { /* app module flow */ }
is com.example.feature.profile.deeplinks.OpenFeatureProfileDeeplinkParams -> { /* feature flow */ }
null -> { /* no match */ }
}
In composed mode, result type is DeeplinkParams? and can come from dependent modules too.
Use concrete generated params types from each participating module, or call module processors
directly when you want module-local exhaustive when.
Collision Validation¶
DeepMatch also validates composed URI-shape collisions per variant.
- Task name:
validate<Variant>CompositeSpecsCollisions(for example,validateDebugCompositeSpecsCollisions). - It compares normalized spec shapes across app + composed dependency modules.
- If two modules declare the same shape, build fails with a detailed collision report.
This protects multi-module projects from accidental first-match routing conflicts.