Kotlin FHIR Data Capture

A Kotlin Multiplatform library for collecting, validating, and processing structured healthcare data using HL7 FHIR Questionnaires.
Key features
Conformance
For the full conformance analysis, see the conformance doc.
FHIR Questionnaire specification
The library renders and processes
Questionnaire and
QuestionnaireResponse resources from
FHIR R4 (v4.0.1).
See FHIR Questionnaire specification conformance
for the implementation status of every item type, item control, form behavior element, and
standard extension.
Structured Data Capture specification
The library implements a subset of the
Structured Data Capture implementation guide STU4 (v4.0.0).
Advanced rendering, form behavior and calculation, and template-based extraction are implemented.
The SDC population module and the other extraction mechanisms are not.
See SDC conformance for
feature-by-feature status, supported expression languages, and FHIRPath environment variables.
Supported platforms
The library's support for different
target platforms
is listed in the following table:
The library also supports the following
Kotlin/Native targets:
| Gradle target | Artifact suffix | Tier |
|---|
Catalog app
The catalog module is a multiplatform demo application. To run the iOS variant see
catalog-iosApp/README.md.
User Guide
Adding the library dependency to your project
To use the Kotlin FHIR Data Capture library in your project, you need to add the library dependency
to your project. To do that, first make sure to include the mavenCentral()1 repository in the
build.gradle.kts file in your project root.
repositories {
mavenCentral()
}
Next, follow the instructions for your specific project type.
Kotlin Multiplatform Projects
For Kotlin Multiplatform projects, add the dependency to the shared commonMain source set within
the kotlin block of the module's build.gradle.kts file (e.g., composeApp/build.gradle.kts or
shared/build.gradle.kts). This makes the library available across all platforms in your project.
// e.g., composeApp/build.gradle.kts or shared/build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation("dev.ohs.fhir:fhir-data-capture:2.0.0-alpha02")
}
}
}
Android projects
For Android projects, add the dependency to the dependency block in the module's
build.gradle.kts file (e.g., app/build.gradle.kts).
dependencies {
implementation("dev.ohs.fhir:fhir-data-capture:2.0.0-alpha04")
}
Working with Questionnaires
Render a questionnaire using the Questionnaire composable.
val coroutineScope = rememberCoroutineScope()
Questionnaire(
questionnaireJson = myQuestionnaireJson,
questionnaireResponseJson = existingResponseJson,
config = QuestionnaireConfig(
showSubmitButton = true,
showCancelButton = true,
showReviewPage = false,
isReadOnly = false,
),
onSubmit = { getResponse ->
coroutineScope.launch {
val response = getResponse()
}
},
onCancel = {
navController.popBackStack()
},
)
See QuestionnaireConfig
for all display options (review page, read-only mode, required and optional labels, long-scroll
navigation, custom submit button text, and the "submit anyway" escape hatch).
To make launch context resources such as
%patient available to the questionnaire's FHIRPath expressions, pass them as JSON via
questionnaireLaunchContextMap, keyed by the launch context name declared in the questionnaire.
Questionnaire(
questionnaireJson = myQuestionnaireJson,
questionnaireLaunchContextMap = mapOf("patient" to patientJson),
...
)
Configuring the library
Optional integration hooks are supplied through
DataCaptureConfig
via a CompositionLocal.
CompositionLocalProvider(
LocalDataCaptureConfig provides
DataCaptureConfig(
valueSetResolverExternal = myValueSetResolver,
xFhirQueryResolver = myXFhirQueryResolver,
urlResolver = myUrlResolver,
),
) {
Questionnaire(...)
}
Without these hooks, external value sets resolve to no options and x-fhir-query expressions fail.
See the conformance doc for which features depend on which resolver.
Validating a QuestionnaireResponse
The Questionnaire composable validates answers as the user fills the form and on submit. To
validate a response outside the UI, use
QuestionnaireResponseValidator.
val results: Map<String, List<ValidationResult>> =
QuestionnaireResponseValidator.validateQuestionnaireResponse(
questionnaire = questionnaire,
questionnaireResponse = questionnaireResponse,
)
See validation conformance for the
supported constraints and their caveats.
Extracting FHIR resources
If the questionnaire is authored for
SDC template-based extraction, extract a transaction
Bundle of FHIR resources from the completed response with
TemplateExtractionEngine.
if (TemplateExtractionEngine.canExtract(questionnaire)) {
val bundle = TemplateExtractionEngine.extract(questionnaire, questionnaireResponse)
}
Extraction is not invoked automatically by the Questionnaire composable. Call it with the
response returned from onSubmit. Definition, StructureMap, and observation based extraction are
not supported (see extraction conformance).
Developer guide
Testing
Tests are located in the following source sets:
commonTest: Shared tests (logical validation rules and Compose UI rendering/flows) that run
across all targets.
jvmTest: JVM-specific tests verifying localized date, time, and datetime input
parsing/formatting using JVM Locales (java.util.Locale).
androidDeviceTest: Android-specific instrumentation tests verifying interactions with native
Android date, time, and datetime picker dialogs (requires a connected device or emulator).
CI Platform Coverage
The CI pipeline automatically runs checks on every push and pull
request. The table below details which test source sets (listed above) are executed by each target's
CI task:
Running Tests Locally
To run all CI-validated test suites locally:
./gradlew check
To run a specific test suite locally, run the corresponding Gradle task:
On-Device Android Tests
The platform-specific Android UI tests (located under androidDeviceTest) are not run
automatically on CI. To run them locally:
- Connect a physical Android device or start an emulator.
- Execute the connected test task:
./gradlew :datacapture:connectedAndroidDeviceTest
Publishing
To publish a new release, first update mavenVersion in gradle.properties to the new version.
Then follow one of the methods below:
Maven Local
To publish artifacts to your local Maven repository (~/.m2/repository) for local development and
testing, run:
./gradlew :datacapture:publishToMavenLocal
Maven Central
Publishing to Maven Central requires two sets of credentials:
- Maven Central credentials: your Sonatype portal username and password tokens.
- GPG signing: a GPG key and its passphrase, used to sign all published artifacts.
See the
Kotlin Multiplatform Publishing Guide
and the
Maven Central Publishing Guide for
more information on how to set up these credentials.
Publishing to Maven Central manually
For manual publishing, store the credentials in the global ~/.gradle/gradle.properties in your
environment (not the project's gradle.properties) so they are never committed to the repository:
# Maven Central Credentials
mavenCentralUsername=YOUR_USERNAME_TOKEN
mavenCentralPassword=YOUR_PASSWORD_TOKEN
# GPG Signing (file-based)
signing.keyId=YOUR_KEY_ID
signing.password=YOUR_KEY_PASSWORD
signing.secretKeyRingFile=/path/to/secring.gpg
Then run:
./gradlew :datacapture:publishToMavenCentral
Publishing to Maven Central using GitHub Actions
The project includes a GitHub Actions workflow that publishes to
Maven Central when a new GitHub release (or pre-release) is created.
The workflow requires the following GitHub organization or repository secrets (already set up):