Petnow LogoPetnow

SDK v1.3 → v1.4 Migration

A change-by-change guide to upgrading an existing v1.3.x app to v1.4.0.

This guide lists the code changes needed to upgrade an existing v1.3.x integration to v1.4.0, per platform.

Coming from v1.2.x? Skip this page and use the direct SDK v1.2 → v1.4 Migration instead — it covers every change in one pass, with no intermediate v1.3-era code.

iOS: most old public symbols are kept as deprecated aliases, so typical apps still build — with warnings. A handful of v1.3 symbols were removed or hidden without an alias (see Removed without an alias below); apps that used them need the listed replacements.
Android: the global PetnowApiClient entry point (the apiClient module) is no longer shipped, so code changes are required.

The v1.4 changes serve two goals — ① unifying the iOS/Android public API (CameraController, DetectionConfiguration/DetectionPurpose, DetectionStatus/CameraResult, etc. now have the same shape on both platforms), and ② controlling the session lifecycle through clearer APIs (separate verbs for start/pause/resume/finalize). The Why under each item below points to one of these.


iOS

1. CameraViewModelCameraController

// v1.3.x (deprecated)
@StateObject private var viewModel: CameraViewModel
CameraView(viewModel: viewModel)

// v1.4.0
@StateObject private var controller: CameraController 
CameraView(controller: controller)                    

CameraViewModel is a deprecated typealias of CameraController. Change CameraView(viewModel:) to CameraView(controller:) as well.

Why: This type isn't a mere view model — it's the controller that owns the license and detection commands. The rename clarifies the role and aligns the name with Android's CameraController. → Basic Usage

2. Move the license to the constructor

Passing the license via initializeCamera(licenseInfo:) is deprecated. Inject licenseInfo into the constructor and call initializeCamera without the license argument.

// v1.3.x (deprecated)
let controller = CameraController(species: .dog, cameraPurpose: .forRegisterFromProfile)
try await controller.initializeCamera(
    licenseInfo: LicenseInfo(apiKey: "YOUR_API_KEY"),
    initialPosition: .back,
    captureSessionId: sessionId
)

// v1.4.0
let controller = CameraController( 
    configuration: DetectionConfiguration(species: .dog, purpose: .petProfileRegistration), 
    licenseInfo: LicenseInfo(apiKey: "YOUR_API_KEY") 
)
try await controller.initializeCamera(initialPosition: .back, captureSessionId: sessionId) 
controller.startDetection() 

initializeCamera does not start detection — call startDetection() explicitly once it returns (same as Android).

Why: Injecting the license once for the controller's lifetime lets initializeCamera focus solely on connecting the camera/session — no re-passing the key on every call, and license validation runs once. (Android works the same way.) → Getting Started

3. Teardown: stopDetection()finalizeCamera()

// v1.3.x (deprecated)
controller.stopDetection()

// v1.4.0
controller.finalizeCamera() 

Why: The capture/detection session lifecycle is now controlled through clearer APIs. The old stopDetection() conflated stopping detection with closing the camera; v1.4 separates the verbs — start startDetection(), pause/resume pauseDetection(), full teardown finalizeCamera() — so each stage is explicit and you avoid an accidental full teardown. → Basic Usage

4. Removed without an alias

Most of the surface migrates via deprecated aliases, but these v1.3 members are gone or hidden and need the listed replacement:

Removed in v1.4Replacement
captureSession (raw AVCaptureSession getter)No getter — CameraView renders the preview; inject your own session via CameraController(configuration:licenseInfo:captureSession:) for a custom preview → Customization
cameraPermissionStatusPermission handling is host-owned — check/request AVCaptureDevice.authorizationStatus(for: .video) yourself
currentCameraPositionInternal switch state — drive the UI from your own switchCamera() calls
LicenseInfo(apiKey:isDebugMode:)isDebugMode removed — the SDK targets production unconditionally. Use LicenseInfo(apiKey:). Callers passing isDebugMode: must drop the argument.

iOS change summary

v1.3.xv1.4.0
CameraViewModelCameraController
CameraView(viewModel:)CameraView(controller:)
initializeCamera(licenseInfo:…)constructor CameraController(configuration:licenseInfo:) + initializeCamera(initialPosition:captureSessionId:)
stopDetection()finalizeCamera() (detection starts via explicit startDetection())
captureSession / cameraPermissionStatus / currentCameraPositionremoved/hidden — see Removed without an alias

See Basic Usage for details.


Android

In v1.4 the global PetnowApiClient entry point (the apiClient module) is no longer part of the SDK. The license and detection configuration move to per-session passing, and the module's server-API helpers are gone — code changes are required.

1. Global PetnowApiClient init/config → per-session passing

// v1.3.x (the apiClient module is no longer shipped — compile error)
PetnowApiClient.init(key = "YOUR_API_KEY", isDebugMode = false)
PetnowApiClient.configureDetectionMode(
    purpose = DetectionPurpose.PET_PROFILE_REGISTRATION,
    species = PetSpecies.DOG,
    enableFakeDetection = true
)
  • Recommended (CameraView + CameraController): pass LicenseInfo(apiKey) to the CameraController(context, license, scope) constructor and DetectionConfiguration to initializeCamera(config, captureSessionId). → Basic Usage
  • Keep the existing PetnowCameraFragment: pass the license via Fragment args (ARG_API_KEY) or provideLicense(), and the configuration via ARG_DETECTION_CONFIGURATION. → Fragment (legacy)
  • The configuration types move packages: io.petnow.api.client.{DetectionConfiguration, DetectionPurpose, PetSpecies}io.petnow.ui.config.* (same shape).
  • PetnowApiClient.isSuccessInitialize has no replacement — initialization failures now surface as the structured PetnowUIError from initializeCamera() (item 4).
  • isDebugMode is gone on Android: the SDK targets production unconditionally.

If you called the Petnow Server API through PetnowApiClient (creating capture sessions, uploading fingerprints, registration): those helpers did not move — they were retired with the module. Call the Server API from your app server instead; the v1.4 client SDK handles capture only.

Why: A global singleton meant the whole app shared one mutable configuration — hard to use different settings per session, manage independent lifecycles, or test. Per-session passing removes the global state, and moving server calls out of the client puts the API key where it belongs (your server).

2. Listener import package changed

// v1.3.x
import io.petnow.ui.PetnowCameraDetectionListener

// v1.4.0
import io.petnow.callback.PetnowCameraDetectionListener      
// V2: import io.petnow.callback.PetnowCameraDetectionListenerV2

Why: The public callback API was simply moved into a dedicated io.petnow.callback package. No behavior change — only the import path. → Basic Usage

On the CameraController path, use setDetectionListenerV2 (V2). V2 delivers DetectionStatus (sealed: NoObject/Processing/Detected/Finished/Failed(reason)) and CameraResult (Success(fingerprintImageFiles, appearanceImageFiles)/Fail).

Why: V1 hands you legacy per-platform models, whereas V2 delivers the same result models as iOS — a type-safe sealed DetectionStatus and a CameraResult carrying the image file lists. You get cross-platform consistency and richer results. → Basic Usage

4. Initialization failures are now the structured PetnowUIError

In v1.3.x, initializeCamera() failures leaked raw platform exceptions (SecurityException on a missing permission, camera2 throwables on camera errors). v1.4.0 throws every initialization failure as the sealed class io.petnow.ui.PetnowUIError. Also new in v1.4.0: initializeCamera() validates the API key against the server (once per key per process), so a rejected key fails with PetnowUIError.InvalidLicense.

// v1.3.x — catch platform exceptions individually
try {
    controller.initializeCamera(configuration, captureSessionId)
} catch (e: SecurityException) { /* permission missing */ }
  catch (e: Exception) { /* other init errors */ }

// v1.4.0 — a single catch + sealed when
try {
    controller.initializeCamera(configuration, captureSessionId)
} catch (e: PetnowUIError) {                                
    when (e) {
        is PetnowUIError.InvalidLicense -> { /* API key rejected */ }
        is PetnowUIError.PermissionDenied -> { /* permission missing */ }
        is PetnowUIError.CameraOpenFailed -> { /* camera error — see e.cause */ }
    }
}

CancellationException still propagates untouched (normal lifecycle — rethrow it).

Why: one catch (e: PetnowUIError) covers every initialization failure, and the sealed when makes missing cases a compile-time error. The case names match iOS PetnowUIError (invalidLicense/permissionDenied), so error handling lines up across platforms. The original platform exception is preserved as cause. → Basic Usage — Error Handling

Android change summary

v1.3.xv1.4.0
PetnowApiClient.init()LicenseInfo + CameraController(context, license, scope) or Fragment args/provideLicense()
PetnowApiClient.configureDetectionMode()initializeCamera(config, captureSessionId) or ARG_DETECTION_CONFIGURATION
PetnowApiClient.isSuccessInitializeremoved — failures surface as PetnowUIError
PetnowApiClient server-API helpersretired — call the Server API from your app server
import io.petnow.ui.PetnowCameraDetectionListenerimport io.petnow.callback.PetnowCameraDetectionListener
initializeCamera() failures as SecurityException / raw platform exceptionssealed PetnowUIError (InvalidLicense/PermissionDenied/CameraOpenFailed)

See Basic Usage and Fragment (legacy) for details.


Migration checklist

iOS

  • CameraViewModelCameraController, CameraView(viewModel:)(controller:)
  • Move the license to the constructor (CameraController(configuration:licenseInfo:)); drop the license arg from initializeCamera
  • stopDetection()finalizeCamera(); call startDetection() after initializeCamera
  • Replace uses of captureSession/cameraPermissionStatus/currentCameraPosition (see Removed without an alias)

Android

  • Remove PetnowApiClient calls → pass license/config per session; move server-API calls to your app server
  • Change the listener import to io.petnow.callback
  • (Recommended) Move to CameraView + CameraController + the V2 listener
  • Replace SecurityException/generic catches around initializeCamera() with a PetnowUIError catch (+ when)

On this page