@anyline/tire-tread-react-native-module
v15.5.0
Published
React Native SDK for Anyline Tire Tread scanning and depth measurement
Readme
Anyline Tire Tread React Native SDK
Measure tire tread depth from a smartphone camera. The SDK captures frames on-device, uploads them to Anyline's servers, and returns per-region tread depth measurements.
Requirements
- React Native 0.75+
- Node 18+
- Camera with autofocus and 1080p capability
- Stable internet connection
- Android 6.0+ (API 23)
- iOS 13.4+
Installation
yarn add @anyline/tire-tread-react-native-module
# or
npm install @anyline/tire-tread-react-native-moduleAndroid
Add the Anyline Maven repository to your project-level build.gradle (or settings.gradle for newer projects):
allprojects {
repositories {
maven { url "https://europe-maven.pkg.dev/anyline-ttr-sdk/maven" }
}
}iOS
cd ios && pod installThe module ships the Anyline Tire Tread SDK: pod install downloads a prebuilt AnylineTireTreadSdk.xcframework from the Anyline CDN and checks it against a SHA-256 set in the module's podspec.
If your Podfile declares pod 'AnylineTireTreadSdk', remove that line, since the module itself would supply the SDK.
iOS installation troubleshooting
Two things can go wrong because the SDK arrives during pod install.
The framework is missing and pod install will not fetch it again.
The plugin downloads the SDK from its podspec's prepare_command, and CocoaPods
runs that step only when it installs the plugin. Usually it doesn't: when
Podfile.lock and ios/Pods/Manifest.lock agree, CocoaPods reuses what it
already has. The xcframework also sits next to the plugin in node_modules,
outside ios/Pods, so CocoaPods never checks whether it is still there.
Delete the framework, or let a cleaning script remove it, and the next
pod install reports success while the build fails for a missing SDK.
Look for the plugin's line in the pod install output:
Using anyline-ttr-react-native (15.3.3) skipped, nothing was fetched
Installing anyline-ttr-react-native (15.3.3) ran, the framework is presentCocoaPods hides the download step's own output, so that line is the only confirmation you get.
To force the reinstall, delete the manifest and run again:
rm -f ios/Pods/Manifest.lock
cd ios && pod installChanging the plugin's version does the same thing, for the same reason: it makes CocoaPods install the plugin instead of reusing it.
pod install fails with a checksum mismatch.
error: checksum mismatch for AnylineTireTreadSdk.xcframework <version>A corrupted or truncated download is the usual cause, so first do a retry. A proxy that rewrites HTTPS responses may also trigger this. If the same issue re-occurs on a clean network, contact Anyline support with the version and both checksums from the error. Do not edit the pinned checksum to get around it: that is the check guaranteeing that you are building against the exact binary we published.
Camera Permissions
The scan UI requires camera access. Configure permissions before calling scan.
Android — add to AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />iOS — add to Info.plist:
<key>NSCameraUsageDescription</key>
<string>Camera access is needed to scan tire tread depth.</string>Usage
See the
example/app for a complete working integration.
Every API call returns a result object — promises never reject. Check ok to distinguish success from failure.
import {
initialize,
scan,
getResult,
sendCommentFeedback,
sendTreadDepthResultFeedback,
sendTireIdFeedback,
type TireTreadConfig,
type ScanOutcome,
type TreadDepthResult,
} from '@anyline/tire-tread-react-native-module';1. Initialize
Call once at app startup. Requires a valid Anyline license key.
const init = await initialize('YOUR_LICENSE_KEY');
if (!init.ok) {
// init.error.type is the category (LICENSE_ERROR, CONFIG_ERROR, NETWORK_ERROR, ...)
// init.error.code is the specific detail (INVALID_LICENSE, NO_CONNECTION, ...)
console.error(init.error.type, init.error.code, init.error.message);
return;
}You can pass an optional InitOptions object as the second argument:
const init = await initialize('YOUR_LICENSE_KEY', {
customTag: 'warehouse-scanner-3', // optional tag to tell apart different devices (max 50 chars)
});2. Scan
Opens a full-screen camera UI. The user positions the phone near the tire and the SDK captures frames automatically.
const config: TireTreadConfig = {
uiConfig: {
measurementSystem: 'Metric',
},
};
const outcome: ScanOutcome = await scan(config);
switch (outcome.kind) {
case 'ScanCompleted':
console.log('Measurement UUID:', outcome.measurementUUID);
break;
case 'ScanAborted':
// User closed the scanner
return;
case 'ScanFailed':
console.error(outcome.error?.type, outcome.error?.code, outcome.error?.message);
return;
}Scan Outcomes
| kind | Meaning | Fields |
|--------|---------|--------|
| ScanCompleted | Frames captured and uploaded | measurementUUID |
| ScanAborted | User closed the scanner | measurementUUID? (present if session was created) |
| ScanFailed | Scan could not complete | error, measurementUUID? |
3. Get Results
Poll the backend for tread depth measurements. You can pass an optional timeout override in seconds if needed.
const result = await getResult(outcome.measurementUUID!);
if (!result.ok) {
console.error(result.error.type, result.error.code, result.error.message);
return;
}
const { global, regions } = result.value;
console.log(`Global depth: ${global.value_mm} mm`);
for (const region of regions) {
if (region.available) {
console.log(` Region: ${region.value_mm} mm (${region.value_inch_32nds}/32")`);
}
}4. Send Feedback
After reviewing results, you can submit corrections to improve future measurements.
// Attach a comment
await sendCommentFeedback(measurementUUID, 'Tire was wet during scan');
// Submit corrected tread depth values
await sendTreadDepthResultFeedback(measurementUUID, [
{ available: true, value_mm: 5.2 },
{ available: true, value_mm: 4.8 },
{ available: true, value_mm: 5.0 },
]);
// Submit a tire identifier
await sendTireIdFeedback(measurementUUID, 'FL-001');All feedback functions return SdkResult<MeasurementInfo>.
API Reference
All functions are fully typed. Return types and error shapes are available via TypeScript autocompletion.
Scan
initialize(licenseKey, options?)— Initialize the SDKscan(config?, options?)— Open scanner, returnsScanOutcomegetResult(measurementUUID, timeout?)— Fetch tread depth results
Feedback
sendCommentFeedback(measurementUUID, comment)— Attach a text comment to a measurementsendTreadDepthResultFeedback(measurementUUID, treadResultRegions)— Submit corrected tread depth valuessendTireIdFeedback(measurementUUID, tireId)— Submit a corrected tire identifier
Tire Sidewall (TSW)
TireSidewall.scan({ clientId, config? })— Open the sidewall scanner, returnsTswScanOutcomeTireSidewall.isSupported()— Check whether the device can run the sidewall scannerTireSidewall.resolvePlayServices()— Show the Play Services resolution dialog (Android only)
See Tire Sidewall (TSW) Scanner for details.
Utility
getSdkVersion()— Native SDK versiongetWrapperVersion()— Module version
Scan Configuration
Pass a TireTreadConfig object to scan. All fields are optional.
const config: TireTreadConfig = {
scanConfig: {
// Only set tireWidth if you know the exact width in mm beforehand.
// If omitted, the SDK will prompt the user to enter it before scanning.
// Passing an incorrect value will reduce measurement accuracy.
tireWidth: 225,
},
uiConfig: {
measurementSystem: 'Metric', // 'Metric' | 'Imperial'
},
additionalContext: {
correlationId: 'fleet-inspection-42',
tirePosition: { axle: 1, positionOnAxle: 1, side: 'Left' },
},
};Full configuration reference: Scan Configuration | Default UI
Error Handling
All errors follow a structured format with a type category and a specific code:
interface SdkError {
type: ErrorType;
code: ErrorCode;
message: string;
debug?: Record<string, string>;
}Error Types and Codes
| Code | When |
|------|------|
| INVALID_LICENSE | License key is malformed or expired |
| LICENSE_KEY_FORBIDDEN | License not authorized for this app/bundle ID |
| SDK_NOT_VERIFIED | License verification failed (network or server issue) |
| Code | When |
|------|------|
| INVALID_ARGUMENT | Invalid config value passed to an API call |
| CAMERA_PERMISSION_DENIED | Camera permission was denied at runtime |
| Code | When |
|------|------|
| NO_CONNECTION | Device is offline or cannot reach Anyline servers |
| UPLOAD_FAILED | Frame upload to backend failed |
| TIMEOUT | Server did not respond within the timeout period |
| Code | When |
|------|------|
| SDK_NOT_INITIALIZED | API called before initialize completed |
| INITIALIZATION_FAILED | SDK startup failed (missing permissions, resources) |
| SESSION_CREATION_FAILED | Backend rejected the scan session |
| MEASUREMENT_ERROR | Backend could not process the scan |
| ALREADY_RUNNING | A scan is already in progress |
| PLAY_SERVICES_UNAVAILABLE | Android: Google Play Services / on-device runtime unavailable (sidewall support check) |
| INTERNAL_ERROR | Unexpected native SDK error |
| UNKNOWN_ERROR | Unclassified error |
| Code | When |
|------|------|
| INVALID_UUID | Measurement UUID not found |
| RESULT_ERROR | Failed to retrieve tread depth results |
Result Structure
A successful getResult call returns a TreadDepthResult. Here's what you get back:
// result.value: TreadDepthResult
{
global: TreadResultRegion, // overall tire depth
regions: TreadResultRegion[], // per-region measurements (typically 3)
measurementInfo: {
measurementUUID: string,
status: MeasurementStatus, // see below
additionalContext?: { // echoes back what you passed to scan()
correlationId?: string,
tirePosition?: { axle, positionOnAxle, side },
},
},
measurementMetadata?: {
movementDirection?: 'LeftToRight' | 'RightToLeft' | 'Unknown',
},
}
// Each TreadResultRegion:
{
available: boolean, // false if this region couldn't be measured
value_mm: number, // depth in millimeters
value_inch: number, // depth in inches
value_inch_32nds: number, // depth in 32nds of an inch
}
// MeasurementStatus:
'Unknown' | 'WaitingForImages' | 'Processing'
| 'ResultReady' | 'ResultAndReportReady'
| 'Completed' | 'Aborted' | 'Failed'Tire Sidewall (TSW) Scanner
The Tire Sidewall scanner is a standalone scanner that captures a single tire sidewall image on-device, uploads it to the Anyline cloud, and returns the result synchronously. It is independent of tread-depth scanning:
- It does not require
initialize(). - It is authed by a separate cloud
clientId(provided by Anyline), not the TTR license key.
import { TireSidewall } from '@anyline/tire-tread-react-native-module';
// 1. (Optional) Check device support — does not require initialization.
const support = await TireSidewall.isSupported();
if (!support.supported) {
if (support.userResolvable) {
await TireSidewall.resolvePlayServices(); // Android only; no-op on iOS
}
return;
}
// 2. Scan.
const outcome = await TireSidewall.scan({
clientId: 'YOUR_SIDEWALL_CLIENT_ID',
config: {
correlationId: 'c0ffee00-c0ff-4ee0-b0ba-c0ffee0000ff', // optional, v4 UUID
texts: { alignTire: 'Align the tire' }, // optional UI overrides
},
});
// 3. Handle the outcome.
switch (outcome.kind) {
case 'completed':
// outcome.resultJson — raw cloud JSON (parse in your app)
// outcome.imageBase64 — captured JPEG, base64 (no data-URI prefix)
// outcome.lighting — 'Dark' | 'Bright' | 'Good' | null
break;
case 'aborted':
// user dismissed the scanner before capture
break;
case 'failed':
// outcome.error: SdkError
break;
}Render the captured image directly from the base64 payload:
<Image source={{ uri: `data:image/jpeg;base64,${outcome.imageBase64}` }} />Configuration (TireSidewallConfig)
| Field | Type | Notes |
|-------|------|-------|
| correlationId | string? | Correlates scans across Anyline products. Must be a v4 UUID when set; an invalid value fails the scan with INVALID_UUID. |
| texts | TireSidewallTexts? | Optional overrides for the scanner overlay strings. Omit a field to keep the SDK default. |
Outcome (TswScanOutcome)
| kind | Fields |
|--------|--------|
| 'completed' | resultJson: string, imageBase64: string, lighting: 'Dark' \| 'Bright' \| 'Good' \| null |
| 'aborted' | — |
| 'failed' | error?: SdkError |
Sidewall-specific error codes
In addition to the shared codes (see Error Handling), a sidewall scan can return:
| Code | When |
|------|------|
| INVALID_UUID | correlationId is not a valid v4 UUID |
| PLAY_SERVICES_UNAVAILABLE | Android: Google Play Services / on-device runtime is missing (may be user-resolvable) |
| CAMERA_PERMISSION_DENIED | Camera permission was denied at runtime |
Troubleshooting
Request camera permission before calling scan. On iOS, the first call triggers the system prompt. On Android, use PermissionsAndroid.request() or a library like react-native-permissions.
Ensure initialize completed with ok: true before scanning. Check that the device has autofocus — tablets and some low-end devices without autofocus are not supported.
The SDK uploads frames and retrieves results over HTTPS. Verify the device has a stable internet connection and can reach anyline.com endpoints. Increase the timeout if processing takes longer than expected.
License keys are bound to app identifiers. Verify the bundle ID (iOS) and application ID (Android) match what was configured in your Anyline account.
The Anyline Maven repository is not in your Gradle config. Add maven { url "https://europe-maven.pkg.dev/anyline-ttr-sdk/maven" } to your repositories block.
Run pod repo update then pod install again. Ensure your Podfile's platform is set to iOS 13.4 or higher.
Expo
This module includes an Expo config plugin that automatically:
- adds
NSCameraUsageDescriptionto your iOSInfo.plist - adds
CAMERApermission to your AndroidAndroidManifest.xml - adds the Anyline Android Maven repository to the project repositories
{
"plugins": [
"@anyline/tire-tread-react-native-module"
]
}Note: This module uses native code and is not compatible with Expo Go. You must use a development build (
npx expo run:android/npx expo run:ios).
Support
For issues or questions, open a support request at the Anyline Helpdesk.
License
See LICENSE.md for licensing information.
