|
Novarum DX Ltd
|
Document ID: IFU004
|
1. Overview
This release introduces improvements to visualization, error reporting, and platform compatibility across Android and iOS.
The update also includes dependency upgrades and small API changes that may require adjustments in your integration.
2. New Features (Shared)
2.1. SVG Visualization Support
-
Description: Reader overlay now supports SVG path-based visualizations.
-
Default Behavior: Existing configurations continue using DotMatrix visualization mode automatically.
-
Migration Note:
To use the new SVG visualization, request an updated reader configuration file containingpathData.
No code changes are required for apps that continue using the default mode.
2.2. New Exposure Strip Error Icon
-
Description: A new, dedicated icon is displayed for exposure-related errors, replacing the generic alert triangle.
-
Developer Impact: No code changes required. The updated icon appears automatically when exposure errors are detected.
3. Breaking vs. Non-breaking Summary
|
Change Type |
Impact |
Platforms |
|---|---|---|
|
Visualization Mode (SVG) |
Additive – no API changes required unless adopting new configuration |
Android, iOS, Expo |
|
Exposure Strip Error Icon |
Non-breaking visual enhancement |
Android, iOS, Expo |
|
PMF Loader Parameter Change |
Breaking (method signature and parameter label/type changed) |
Android, iOS |
|
Dependency Upgrades (Java 17, AGP, NDK, Expo) |
Potentially breaking for projects on older toolchains |
Android, Expo |
|
OpenCV Library Update |
Non-breaking (binary compatible) |
Android, iOS, Expo |
4. When to Upgrade
Recommended For:
-
Integrators needing SVG overlay support for richer visualization control.
-
Teams maintaining Play Store–published Android apps (Java 17, AGP 8.9, 16KB page size, SDK 35 compliance).
-
Projects aiming for improved readability of exposure error feedback in UI.
Not Required For:
-
Integrations that do not require SVG path visualization or already use stable DotMatrix rendering.
-
Internal builds not targeting recent Play Store SDK/NDK compliance deadlines.
5. Compatibility Notes
-
Android: Requires Gradle Plugin 8.0+ and JDK 17 or later.
ConfirmcompileSdk = 35andtargetSdk = 35in your project settings. -
iOS: no build tool changes.
-
React Native: Minimum version of Expo is now 54.
-
Reader Configuration Files: Existing configurations remain valid; SVG mode only activates when
pathDatais included in the configuration.
6. Summary of Developer Actions
|
Platform |
Area |
Action Required |
|---|---|---|
|
Android |
Java/Kotlin target |
Migrate build settings to Java 17 |
|
Android |
Named params in PMF loader |
Rename parameter if used |
|
Android |
Build environment |
Update AGP, SDK 35, NDK 29, CMake 3.22 |
|
iOS |
PMF loader |
Update to |
|
Both |
OpenCV |
Ensure binary compatibility after upgrade |
|
Expo |
Expo Dependency |
Update to expo version 54. This is required for AAR dependency bundling. |
|
Expo |
Reader Control callbacks |
Update onComplete, and onFrameCaptured callbacks to match extended interface. Optionally implement onAbort handling to capture partial results. |
7. Platform-Specific Notes
See detailed examples and API notes
7.1. Android - Migration Guide
7.1.1. Java and Kotlin Target Version
-
Changed:
-
sourceCompatibility: 1.8 → 17 -
targetCompatibility: 1.8 → 17 -
kotlinOptions.jvmTarget: 1.8 → 17
-
-
Developer Action:
Ensure your app module also compiles with Java 17 and uses KotlinjvmTarget = "17".
Example:compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = "17" } -
Impact Analysis:
-
Minimum Gradle/AGP requirements: Android Gradle Plugin 8.0+ is compatible with Java 17, so most modern builds will already be compliant.
-
Potential Issues: Projects still using older AGP (<8.0) or Java 1.8 syntax tools may fail to compile.
-
Third-party dependencies compiled for Java 8 remain usable but may trigger warnings.
-
7.1.2. OpenCV Upgrade
-
Changed:
4.8.0 → 4.12.0 -
Impact:
-
Binary compatibility is maintained.
-
Performance improvements and bug fixes.
-
If you reference OpenCV classes directly (e.g.,
org.opencv.core.Mat), re-run lint to ensure imports resolve correctly.
-
7.1.3. Android 16KB Page Size Support
-
Description: Required for Play Store releases targeting November 2025+ compliance.
-
Developer Impact: No API changes, but upgrading ensures continued store acceptance.
7.1.4. Gradle & NDK Toolchain Updates
|
Component |
Old |
New |
|---|---|---|
|
AGP |
8.4.2 |
8.9.0 |
|
compileSdk |
34 |
35 |
|
NDK |
22.0.7026061 |
29.0.14033849 rc4 |
|
CMake |
3.10.2 |
3.22.1 |
-
Developer Action:
-
Update your local Android SDK components to match these versions.
-
Validate your CI build environment (NDK and CMake versions).
-
Clean rebuild is recommended due to ABI and toolchain changes.
-
7.1.5. PMF Loader API Change
// Old
fun loadAnalyserConfiguration(jsonString: String): AnalyserConfiguration
// New
fun loadAnalyserConfiguration(json: String): AnalyserConfiguration
-
Change: Parameter name only (
jsonString→json) — no signature or behavior change. -
Developer Impact: None, unless using named parameters in Kotlin calls:
Kotlin// Update this loadAnalyserConfiguration(jsonString = myConfig) // To this loadAnalyserConfiguration(json = myConfig)
7.2. iOS - Migration Guide
7.2.1. PMF Loader API Change
// Old
public static func loadAnalyserConfiguration(jsonString: String) throws -> AnalyserConfiguration
// New
public static func loadAnalyserConfiguration(from data: Data) throws -> AnalyserConfiguration
-
Change:
-
Parameter type:
String→Data -
Parameter label:
jsonString→from -
Decoder reused via internal instance (
decoder) instead of constructing a new one.
-
-
Developer Action:
Update your calls accordingly:// Old let config = try Loader.loadAnalyserConfiguration(jsonString: json) // Newt let config = try Loader.loadAnalyserConfiguration(from: Data(json.utf8)) -
Impact:
-
Breaking change for integrations passing
String. -
Provides better flexibility for binary or file-based configuration sources.
-
7.2.2. OpenCV Upgrade
-
Changed:
4.8.0 → 4.12.0 -
Impact:
No API changes. You may need to clean derived data to avoid linker warnings.
7.3. Expo Migration Guide
7.3.1. Migration Guide: NdxImagingExpoView (1.0.1 → 3.0.0)
-
This guide focuses on NdxImagingExpoView API changes.
-
It summarizes breaking changes, new types, and how to update callbacks and result handling.
7.3.2. Result model shape has changed
-
Removed fields: fullImageBase64, integrityHash, hFactorOverride (top-level)
-
Added fields: pmfStory: FrameDataRecord[], testConfiguration: TestConfigurationRecord
-
TestStrip fields renamed/updated:
• lines -> tLines
• profile -> meanProfile
• stripImageBase64 removed
7.3.3. Event/callback changes on NdxImagingExpoView
-
onProgress has been removed
-
onFrameCaptured payload changed
• Old: nativeEvent.resultPMF
• New: nativeEvent.frame with { progress, resultPMF, stripStatuses, orientation, lux }
7.3.4. ResultPMF type changed
-
Old: string union type ("None" | "Fit" | "MarginalFit" | "NoFit") used directly as strings
-
New: exported as const object ResultPMF with string members; typical usage: ResultPMF.Fit
7.3.5. Additional Camera permission helpers
-
New exports: CameraPermissionErrors, isCameraPermissionError, getCameraPermissionErrorDescription
-
No breaking changes to the hook/useCameraPermissions signature
Details and migration steps
7.3.6. NdxImagingExpoView callbacks
7.3.6.1. Replace onProgress with onFrameCaptured.frame.progress
Old (1.0.1):
<NdxImagingExpo.NdxImagingExpoView
onProgress={({ nativeEvent: { progress } }) => {
if (progress < 0.8 && progress > 0.5) {
console.log("🟠");
} else if (progress > 0.8) {
console.log("🟢");
}
}}
/>
New (3.0.0):
<NdxImagingExpo.NdxImagingExpoView
onFrameCaptured={({ nativeEvent: { frame } }) => {
const { progress } = frame;
if (progress !== undefined) {
if (progress < 0.8 && progress > 0.5) {
console.log("🟠 Progress mid-range:", progress);
} else if (progress > 0.8) {
console.log("🟢 Progress high:", progress);
}
}
}}
/>
7.3.6.2. Update onFrameCaptured signature usage
Old (1.0.1):
<NdxImagingExpo.NdxImagingExpoView
onFrameCaptured={({ nativeEvent: { resultPMF } }) => {
if (resultPMF === "Fit") {
console.log("Fit detected");
}
}}
/>
New (3.0.0):
import { ResultPMF } from "ndx-imaging-expo/NdxImagingExpo.types";
...
<NdxImagingExpo.NdxImagingExpoView
onFrameCaptured={({ nativeEvent: { frame } }) => {
const { resultPMF, progress, lux, orientation, stripStatuses } = frame;
if (resultPMF === ResultPMF.Fit) {
console.log("Fit detected");
}
console.log("---- Frame Captured ----");
console.log("Progress:", progress);
console.log("Lux:", lux);
console.log("Orientation:", orientation);
console.log("Strip Statuses:", stripStatuses);
console.log("Result PMF:", resultPMF);t
console.log("------------------------");
}}
/>
7.3.6.3. onComplete result access changes
-
Test lines moved: lines -> tLines
-
tcRatio still available, but access path changes
-
hFactorOverride moved into testConfiguration.pmfInitialHScaleFactor
-
integrityHash removed; use pmfStory/testConfiguration or other fields as applicable
Old (1.0.1):
onComplete={({ nativeEvent: { resultModel } }) => {
let message = TC Ratio: ${resultModel.testStrips[0].lines[0].tcRatio}\\n;
message += Hash: ${resultModel.integrityHash}\\n;
message += Hfactor Override: ${resultModel.hFactorOverride};
...
}}
New (3.0.0):
onComplete={({ nativeEvent: { resultModel } }) => {
let message = TC Ratio: ${resultModel.testStrips[0].tLines[0].tcRatio}\\n;
message += Hfactor Override: ${resultModel.testConfiguration.pmfInitialHScaleFactor};
...
}}
7.3.7. Result model shape changes
7.3.7.1. ResultModel
Old (1.0.1):
type ResultModel = {
testStrips: TestStrip[];
fullImageBase64: string;
integrityHash: string;
hFactorOverride: number;
}
New (3.0.0):
type ResultModel = {
testStrips: TestStrip[];
pmfStory: FrameDataRecord[];ty
testConfiguration: TestConfigurationRecord;
}
7.3.7.2. TestStrip
Old (1.0.1):
type TestStrip = {
lines: TLine[];
cLine: CLine;
baseline: number[];
profile: number[];
stripImageBase64: string;
}
New (3.0.0):
type TestStrip = {
tLines: TLine[];
cLine: CLine;
baseline: number[];
meanProfile: number[];
}
7.3.7.3. Frame data
-
New: FrameDataRecord represents each captured frame with rich telemetry:
{ timestamp, pmfStatus, lux, orientation, homography, stripBaselines, stripProfiles, stripStatuses, stripImageUris, imageUri? } -
New: FrameCapturedCallback (emitted via onFrameCaptured) with fields:
{ progress, resultPMF, stripStatuses, orientation, lux }
7.3.7.4. Strip status and PMF constants
-
New: StripStatus const object (None, ExposureError, BaselineError, Good, Done)
-
ResultPMF exported as const object (None, Fit, MarginalFit, NoFit)
Example usage:
import { ResultPMF } from "ndx-imaging-expo/NdxImagingExpo.types";
if (resultPMF === ResultPMF.Fit) { ... }
7.3.8. Scanner screen usage updates (example)
7.3.8.1. Passing test configuration
-
Continue passing testInfoJson as a JSON string.
New (3.0.0):
const { testConfiguration } = route.params;
const testInfoJson = JSON.stringify(testConfiguration);
<NdxImagingExpo.NdxImagingExpoView
testInfoJson={testInfoJson}
...
/>
Example:
onComplete={({ nativeEvent: { resultModel } }) => {
navigation.navigate("ResultDetail", resultModel);
}}
7.3.9. Camera permission UX enhancements (optional but recommended)
-
New helpers exported from the module:
• CameraPermissionErrors
• isCameraPermissionError(error)
• getCameraPermissionErrorDescription(error)
Use these to provide robust error handling when calling request/get camera permissions.
Example:
try {
await requestCameraPermissionsAsync();
} catch (error) {
if (isCameraPermissionError(error)) {
switch (error.code) {
case CameraPermissionErrors.CAMERA_UNAVAILABLE:
// Inform the user their device has no camera
break;
// handle other codes...
}
}
}
7.3.10. Native dependency packaging and minimum Expo version
Android now uses AAR bundling via gradleAarProjects declared in expo-module.config.json. This requires Expo SDK 52+.
• Benefit: No secondary authentication with private Maven is needed — all required AARs are distributed inside the package (android/libs/*.aar) and registered as local Gradle projects.
• Action: Ensure your project is on Expo 54+ before upgrading. No maven or git credentials setup is required for this module.
iOS now bundles required XCFrameworks in the repo and references them from the Podspec as vendored frameworks.
• Benefit: No secondary authentication with a private CocoaPods spec repo is required.
• Implication: Since CocoaPods is still used only to integrate ExpoModulesCore and the vendored frameworks are local, there is no need for git-lfs to fetch binary pods for this module.
7.3.11. Abort support and imperative control (new in 3.0.0)
-
1.0.1: No dedicated abort callback or imperative cancel API. Typical fallback was to unmount the view.
-
3.0.0: New onAbort callback and an imperative view.abort() method.
Old (1.0.1):
// No onAbort and no abort(view)
<NdxImagingExpo.NdxImagingExpoView
testInfoJson={testInfoJson}
onComplete={handleComplete}
onFrameCaptured={handleFrame}
/>
New (3.0.0):
import React, { useRef } from 'react';
import * as NdxImagingExpo from 'ndx-imaging-expo';
const cameraRef = useRef<any>(null);
<NdxImagingExpo.NdxImagingExpoView
ref={cameraRef}
testInfoJson={testInfoJson}
onComplete={({ nativeEvent: { resultModel } }) => {
// normal completion
}}
onAbort={({ nativeEvent: { resultModel } }) => {
// user-aborted or programmatic abort; handle partial results if needed
}}
/>
// Trigger an abort (e.g., on back/close):
try {
const view = ndxViewRef.current;
if (!view) {
console.warn("🔴 ndxViewRef.current is null");
return;
}
console.log("🔴 Calling ref.abort()...");
await (view as any).abort();
console.log("🔴 ref.abort() completed");
} catch (e) {
console.warn('Abort failed:', e);
}
Notes:
-
onAbort receives { nativeEvent: { resultModel } } similar to onComplete, enabling graceful teardown and optional partial result handling.
-
onAbort and onComplete are mutually exclusive for a single run.