

# Fingerprint

Enroll and remove fingerprints on iglooworks locks.

---

## addFingerprint

Opens a BLE fingerprint registration window and returns a Flow of scan events. The lock requires 3 good reads to enroll a fingerprint (60-second timeout).

On successful enrollment, the SDK registers the fingerprint on the server. If server registration fails, the SDK automatically removes the fingerprint from lock hardware.

### Signature

```kotlin
fun addFingerprint(
    deviceId: String,
    key: String,
    name: String,
    accessToken: String? = null,
): Flow<FingerprintScanEvent>
```

```swift
func addFingerprint(
    deviceId: String,
    key: String,
    name: String,
    accessToken token: String
) async -> AsyncStream<FingerprintScanEvent>
```

### Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deviceId` | `String` | Yes | Bluetooth device name of the lock. |
| `key` | `String` | Yes | eKey with `ADD_FINGERPRINT` permission. |
| `name` | `String` | Yes | User-assigned name for the fingerprint. |
| `accessToken` | `String?` | OAuth only | OAuth access token for server calls. |

### Return Type — FingerprintScanEvent

```kotlin
sealed class FingerprintScanEvent {
    /** Good read recorded. More reads needed. */
    data class ScanProgress(val attempt: Int, val total: Int) : FingerprintScanEvent()

    /** Bad read. Prompt user to try again. */
    class ScanError : FingerprintScanEvent()

    /** Enrollment completed and registered on server. */
    data class Completed(val accessId: String, val name: String) : FingerprintScanEvent()

    /** Enrollment failed. */
    data class Failed(val error: IglooWorksException) : FingerprintScanEvent()
}
```

```swift
public enum FingerprintScanEvent {
    case scanProgress(attempt: Int, total: Int)
    case scanError
    case completed(accessId: String, name: String)
    case failed(error: Error)
}
```

| Event | Description |
|-------|-------------|
| `ScanProgress(attempt, total)` | A good fingerprint scan. `attempt` = reads so far, `total` = 3. |
| `ScanError` | Bad read — finger not positioned correctly. Prompt to retry. |
| `Completed(accessId, name)` | All 3 reads successful. Fingerprint registered on server. |
| `Failed(error)` | Enrollment failed (timeout, disconnect, etc.). |

### Example

```kotlin
try {
    sdk.addFingerprint(
        deviceId = "IGM401abc123",
        key = guestKey,
        name = "Right Thumb",
    ).collect { event ->
        when (event) {
            is FingerprintScanEvent.ScanProgress ->
                showProgress("Scan ${event.attempt}/${event.total}")
            is FingerprintScanEvent.ScanError ->
                showWarning("Bad read — try again")
            is FingerprintScanEvent.Completed ->
                showSuccess("Fingerprint enrolled: ${event.accessId}")
            is FingerprintScanEvent.Failed -> {
                when (event.error) {
                    is IglooWorksException.TimeoutException ->
                        showError("Timed out — place finger on sensor")
                    is IglooWorksException.ConnectionException ->
                        showError("unexpected bluetooth connection issue")
                    else -> showError("Failed: ${event.error.message}")
                }
            }
        }
    }
} catch (e: IglooWorksException.BluetoothException) {
    showError("Please enable Bluetooth")
} catch (e: IglooWorksException) {
    showError("Fingerprint enrollment failed: ${e.message}")
}
```

```swift
let stream = await sdk.addFingerprint(
    deviceId: "IGM401abc123",
    key: guestKey,
    name: "Right Thumb",
    accessToken: accessToken)
for await event in stream {
    switch event {
    case let .scanProgress(attempt, total):
        showProgress(message: "Scanning \(attempt)/\(total) — place the finger on the reader")
    case .scanError:
        showWarning(message: "Bad read — try again")
    case let .completed(accessId, name):
        showSuccess(message: "Fingerprint '\(name)' enrolled: \(accessId)")
    case let .failed(error):
        showError(message: error.localizedDescription)
    }
}
```

### Notes

- The lock requires exactly 3 good reads to enroll a fingerprint.
- Bad reads don't count toward the total — the user just tries again.
- The entire flow times out after 60 seconds.
- Errors are emitted as `Failed` events rather than thrown as exceptions.

---

## deleteFingerprint

Removes a fingerprint from the lock and deletes the server record.

The SDK fetches the fingerprint UID from the server, removes it from lock hardware via BLE, then deletes the server record.

### Signature

```kotlin
suspend fun deleteFingerprint(
    deviceId: String,
    key: String,
    accessId: String,
    accessToken: String? = null,
)
```

```swift
func deleteFingerprint(
    deviceId: String,
    key: String,
    accessId: String,
    accessToken token: String
) async throws
```

### Parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `deviceId` | `String` | Yes | Bluetooth device name of the lock. |
| `key` | `String` | Yes | eKey with `DELETE_FINGERPRINT_UID` permission. |
| `accessId` | `String` | Yes | Server-assigned access ID of the fingerprint to delete. |
| `accessToken` | `String?` | OAuth only | OAuth access token for server calls. |

### Error Codes

| Exception | Code | Description |
|-----------|------|-------------|
| `BluetoothException` | 708 | Bluetooth is off or unavailable. |
| `ConnectionException` | 12 | Device disconnected during deletion. |
| `TimeoutException` | 703 | BLE operation exceeded the timeout. |
| `ApiException` | varies | Server deletion failed. |

### Example

```kotlin
try {
    sdk.deleteFingerprint(
        deviceId = "IGM401abc123",
        key = guestKey,
        accessId = "access-789",
    )
    showSuccess("Fingerprint removed")
} catch (e: IglooWorksException.ConnectionException) {
    showError("unexpected bluetooth connection issue")
} catch (e: IglooWorksException.TimeoutException) {
    showError("Timed out, try again")
} catch (e: IglooWorksException) {
    showError("Delete failed: ${e.message}")
}
```

```swift
do {
    try await sdk.deleteFingerprint(
        deviceId: "IGM401abc123",
        key: guestKey,
        accessId: "access-789",
        accessToken: accessToken)
    showSuccess(message: "Fingerprint removed")
} catch {
    showError(message: "Failed: \(error.localizedDescription)")
}
```

### Notes

- Server deletion retries up to 3 times with 1-second delays.
- `LockUidNotFoundException` (980, Android) / `IglooworksError.lockUidNotFound` (iOS) during BLE delete is non-fatal — server record is still removed.
