|
Novarum DX Ltd
|
Document ID: IFU008
|
1. Overview
The Novarum Reader SDK for iOS enables iOS applications to capture lateral flow test images using the device camera, analyse captured frames, perform PMF fitting, report strip status, and return structured results through completion, abort, and frame-captured callbacks.
This guide describes the supported customer integration path for the current v4.x Swift Package Manager based SDK. It is intended to remain mostly version agnostic; version-specific fixes, verification evidence, and detailed release differences are maintained in the versioned release notes.
Swift Package Manager is the supported customer integration path. Add the novarumdx.ndx-imaging-swift package and link the Imaging product rather than manually embedding XCFrameworks.
The SDK supports UIKit and SwiftUI applications running on iOS 14.2 or later.
2. Project setup
2.1. Minimum requirements
|
Item |
Minimum |
|---|---|
|
iOS version |
14.2 |
|
Swift tools version |
5.9 |
|
Xcode |
Xcode version compatible with Swift 5.9 and iOS 14.2 deployment targets |
|
Swift package product |
|
2.2. Registry authentication
The SDK is distributed as a private Swift package in NovarumDX AWS CodeArtifact. Authenticate SwiftPM before adding or resolving the package.
aws codeartifact login \
--tool swift \
--domain novarumdx \
--namespace novarumdx \
--region eu-west-2 \
--domain-owner 945969778369 \
--repository novarum.client.swift \
--profile <your-aws-profile>
For customer-facing integrations, use novarum.client.swift when the release has been promoted for client access. Internal development and pre-promotion validation may use novarum.swift. If your credentials were issued for a different repository, confirm the expected repository with Novarum support.
The login command configures the novarumdx SwiftPM registry namespace and stores a temporary CodeArtifact token. Re-run the command when the token expires or when Xcode/SwiftPM starts returning authentication errors.
2.3. Add the SDK dependency
In Xcode, add a package dependency using the Swift package identity:
.package(
id: "novarumdx.ndx-imaging-swift",
from: "4.1.1"
)
Then link your app target to the Imaging product:
.product(
name: "Imaging",
package: "novarumdx.ndx-imaging-swift"
)
When integrated through SwiftPM, the Imaging product resolves its native dependency on novarumdx.ndx-imaging-core and ndx_imaging_ios transitively. Customer apps should not add an OpenCV2 pod or a separate native core package unless Novarum provides a special integration path.
2.4. Platform configuration
The SDK requires camera access for scanning. Add NSCameraUsageDescription to the app target Info.plist:
<key>NSCameraUsageDescription</key>
<string>We will use this for scans</string>
The reader will request camera permission if it has not already been granted. For best user experience, request and explain camera permission before presenting the reader view.
3. Using the reader view
The reader view displays the live camera feed, applies the analyser configuration, tracks PMF fitting progress, reports strip status, and returns result data to the host application.
3.1. Loading the analyser configuration
Load the analyser configuration JSON as Data. The current public PMFLoader API exposes data-based decoding methods:
let configURL = Bundle.main.url(forResource: "reader-config", withExtension: "json")!
let configData = try Data(contentsOf: configURL)
let analyserConfiguration = try PMFLoader.loadAnalyserConfiguration(from: configData)
Additional current loader methods are:
let testConfiguration = try PMFLoader.loadTestConfiguration(from: data)
let testConfigurations = try PMFLoader.loadTestConfigurations(from: data)
Do not use older examples such as PMFLoader.loadAnalyserConfiguration(json: ...) or PMFLoader.loadAnalyserConfiguration(jsonString: ...). Those methods are not present in the current public source.
3.2. SwiftUI integration
Use ReaderViewWrapper to embed the reader in SwiftUI. The current initialiser requires an AnalyserConfiguration, optional PreviewMode, three callbacks, and an abort binding.
import SwiftUI
import Imaging
struct ReaderScreen: View {
let analyserConfiguration: AnalyserConfiguration
@State private var progress: Float = 0
@State private var isAborted = false
@State private var lastLux: Float = -1
@State private var lastOrientation: [Double] = []
@State private var lastStripStatuses: [StripStatus] = []
@State private var analysisModel: AnalysisModel?
var body: some View {
ReaderViewWrapper(
analyserConfiguration: analyserConfiguration,
previewMode: .fill,
onFrameCaptured: { callback in
progress = callback.progress
lastLux = callback.lux
lastOrientation = callback.orientation
lastStripStatuses = callback.stripStatuses
},
onComplete: { model in
analysisModel = model
},
onAbort: { partialModel in
analysisModel = partialModel
},
isAborted: $isAborted
)
}
}
3.3. UIKit integration
UIKit apps can use ReaderViewController directly and implement ReaderViewControllerDelegate.
import UIKit
import Imaging
final class ScanHostViewController: UIViewController, ReaderViewControllerDelegate {
private let analyserConfiguration: AnalyserConfiguration
private var readerViewController: ReaderViewController?
init(analyserConfiguration: AnalyserConfiguration) {
self.analyserConfiguration = analyserConfiguration
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
let reader = ReaderViewController(
readerParams: analyserConfiguration,
previewMode: .fill
)
reader.delegate = self
addChild(reader)
view.addSubview(reader.view)
reader.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
reader.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
reader.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
reader.view.topAnchor.constraint(equalTo: view.topAnchor),
reader.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
reader.didMove(toParent: self)
readerViewController = reader
}
func didCaptureFrame(frameCaptured: FrameCapturedCallback) {
// Update progress UI, lux/orientation indicators, or strip-status UI.
}
func didComplete(analysisModel: AnalysisModel) {
// Handle completed results.
}
func didAbort(analysisModel: AnalysisModel) {
// Handle partial results after abort.
}
func abortScan() {
readerViewController?.abort()
}
}
3.4. Parameter overview
|
Parameter |
Type |
Required |
Description |
|---|---|---|---|
|
analyserConfiguration |
|
Yes |
Configuration decoded from the analyser JSON. Contains |
|
previewMode |
|
No |
Controls camera preview scaling. |
|
onFrameCaptured |
|
Yes |
Called on processed frames with progress, PMF result, strip statuses, orientation, and lux. |
|
onComplete |
|
Yes |
Called when analysis completes successfully. |
|
onAbort |
|
Yes |
Called when the scan is aborted. The model contains partial frame data collected up to that point. |
|
isAborted |
|
SwiftUI only |
Set to |
3.5. Callback data structures
3.5.1. ReaderViewControllerDelegate
public protocol ReaderViewControllerDelegate: AnyObject {
func didCaptureFrame(frameCaptured: FrameCapturedCallback)
func didComplete(analysisModel: AnalysisModel)
func didAbort(analysisModel: AnalysisModel)
}
3.5.2. FrameCapturedCallback
FrameCapturedCallback provides frame-by-frame progress and capture state during scanning.
|
Property |
Type |
Description |
|---|---|---|
|
|
|
Scan progress for the current analysis session. |
|
|
|
PMF fitting result for the current frame. |
|
|
|
Per-strip status values for the current frame. |
|
|
|
Device orientation values captured with the frame. |
|
|
|
Ambient light value captured with the frame. |
3.5.3. PMFResult
|
Case |
Raw value |
Meaning |
|---|---|---|
|
|
|
The PMF model did not fit the current frame. |
|
|
|
The PMF model fitted marginally; the frame is reported but may not contribute as a stored profile. |
|
|
|
The PMF model fitted the current frame. |
3.5.4. StripStatus
|
Case |
Raw value |
Error? |
|---|---|---|
|
|
|
No |
|
|
|
No |
|
|
|
Yes |
|
|
|
Yes |
|
|
|
Yes |
|
|
|
No |
3.5.5. AnalysisModel
public struct AnalysisModel: Encodable {
public let testConfig: TestConfiguration
public let testStrips: [TestStrip]
public let pmfStory: [FrameData]
}
AnalysisModel is returned by both completion and abort callbacks. On abort, testStrips may be empty and pmfStory contains the frame history collected before abort.
3.5.6. FrameData
public struct FrameData: Encodable {
public let timestamp: Date
public let pmfStatus: PMFResult
public var lux: Float
public var orientation: [Double]
public var homography: [Double]
public var stripBaselines: [[Double]?]
public var stripProfiles: [[Double]?]
public var stripStatuses: [StripStatus]
public var stripImages: [UIImage?]
public var image: UIImage?
}
When encoded, FrameData serialises timestamp, PMF status, homography, orientation, lux, strip baselines, strip profiles, and strip statuses. Images are intentionally not encoded.
3.6. Related data types
|
Type |
Kind |
Description |
|---|---|---|
|
|
|
Receives frame, completion, and abort events from UIKit integrations. |
|
|
|
Controls live camera preview scaling using |
|
|
|
Configuration parameters used by the analyser and returned in |
|
|
|
Contains analysed strip data, including control and test line information. |
|
|
|
iOS image object used for frame and strip images. Images are not serialised when |
3.7. Behaviour notes
-
Preview mode —
.filluses aspect fill and may crop the camera feed;.fituses aspect fit. -
Abort —
ReaderViewController.abort()stops scanning and returns partial frame data through the abort callback. -
Screen keep-awake — The SDK disables the idle timer during scanning and restores the previous idle-timer state when the reader disappears or deinitialises.
-
Backgrounding/foregrounding — When the app backgrounds, the reader pauses the camera session and resumes when the app returns to the foreground if the reader was running.
-
PMFInitialHScaleFactor— The SDK passesPMFInitialHScaleFactorfrom the test configuration to the native engine asinitHscale. This can affect wireframe fitting and result behaviour. -
Layout updates —
ReaderViewControllerupdates the preview layer and overlay layout inviewDidLayoutSubviews, so it respects container resizing.
3.8. SVG overlay visualisation
The SDK supports an optional SVG-based overlay rendering mode for higher-quality graphics and scalable test result visuals.
-
Default visualisation mode: dotMatrix.
-
To enable SVG overlays, request an updated analyser configuration file from Novarum support.
-
No code changes are required. The overlay mode is controlled via configuration.
4. Version compatibility summary
|
Component |
Current value |
Notes |
|---|---|---|
|
Novarum iOS Reader SDK |
4.1.1 |
Use package identity |
|
Minimum iOS version |
14.2 |
Verified from current |
|
Swift tools |
5.9 |
Verified from current |
|
Native core |
|
Resolved transitively by SwiftPM. |
5. Support & troubleshooting
|
Symptom |
Cause |
Resolution |
|---|---|---|
|
SwiftPM cannot resolve |
Not authenticated to the CodeArtifact Swift registry, wrong repository, or missing package access. |
Run |
|
Resolution previously worked but now returns 401/403 |
CodeArtifact token expired. |
Re-run the CodeArtifact login command. Tokens are temporary and must be refreshed periodically. |
|
Package resolves internally but not for a customer account |
The release may not have been promoted to |
Confirm the release version exists in the client registry and that the AWS principal has read access. |
|
Camera does not start |
Camera permission is missing or denied. |
Add |
|
Abort callback repeats when rebuilding the SwiftUI view |
The app leaves the |
Reset your app state after handling abort if the reader can be shown again. |
For access credentials, configuration files, or integration assistance, contact your Novarum support representative or technical account manager. The support team uses an intelligent triage agent to route requests efficiently — provide your SDK version, platform, Xcode version, and a clear description of the issue for fastest resolution.
For support requests and issue tracking, use the SDK Service Desk.
6. Document History
| Revision | Summary | Date |
|---|---|---|
| 01 | Initial document revision | Nov 13, 2025 |
| 02 | Revision for version 4.0.1 iOS SDK layer using c++ core 1.0.0 | Jul 16, 2026 |
| 03 | Revison for SDKIOS v4.1.1 | Aug 5, 2026 |