> ## Documentation Index
> Fetch the complete documentation index at: https://jdev-e8db0569.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Platform Notes

> iOS and Android specific implementation details, requirements, and behaviors.

## iOS

### Requirements

* **Minimum iOS version**: iOS 13.0 or higher
* **CocoaPods**: Required for dependency management
* **Swift**: The native implementation is written in Swift

### Dependencies

The library depends on the native SEON Orchestration SDK, declared in the podspec:

```ruby theme={null}
s.dependency 'SEONOrchSDK'
```

### UINavigationController Handling

The SEON SDK requires a `UINavigationController` to present its verification UI. The library handles this automatically:

1. Retrieves the root view controller from the key window
2. If it's already a `UINavigationController`, uses it directly
3. If not, wraps the verification flow in a new `UINavigationController`
4. Presents the navigation controller modally

```swift theme={null}
guard let rootViewController = self.findRootViewController() else {
    flutterResult(FlutterError(code: "E_VERIFICATION_FAILED", message: "Unable to find root view controller.", details: nil))
    return
}

self.pendingResult = flutterResult

let navigationController: UINavigationController
if let navController = rootViewController as? UINavigationController {
    navigationController = navController
} else if let navController = rootViewController.navigationController {
    navigationController = navController
} else {
    let placeholderVC = UIViewController()
    placeholderVC.view.backgroundColor = .clear
    let navController = UINavigationController(rootViewController: placeholderVC)
    navController.setNavigationBarHidden(true, animated: false)
    navController.modalPresentationStyle = .fullScreen
    self.presentedNavigationController = navController
    rootViewController.present(navController, animated: false) {
        SEONOrchSDKService.shared.delegate = self
        SEONOrchSDKService.shared.startWebIdVerificationFlow(navigationController: navController)
    }
    return
}

SEONOrchSDKService.shared.delegate = self
SEONOrchSDKService.shared.startWebIdVerificationFlow(navigationController: navigationController)
```

### State Management

Both iOS and Android maintain the initialized state across multiple verification flows. The SDK remains initialized after `startVerification()` completes. To explicitly reset the state and release resources, call `dispose()`.

### Thread Safety

All SDK operations are dispatched to the main thread:

```swift theme={null}
DispatchQueue.main.async {
    // All SDK calls happen here
}
```

This is critical because:

* Flutter MethodChannel calls may arrive on any thread
* UIKit operations must run on the main thread
* SDK delegate callbacks need main thread context

### Permissions

Required `Info.plist` keys:

| Permission Key                        | Purpose                              |
| ------------------------------------- | ------------------------------------ |
| `NSCameraUsageDescription`            | ID verification and selfie capture   |
| `NSMicrophoneUsageDescription`        | Video liveness checks                |
| `NSLocationWhenInUseUsageDescription` | GeoLocation for fraud prevention API |
| `NSPhotoLibraryUsageDescription`      | Proof of address document upload     |

```xml theme={null}
<key>NSCameraUsageDescription</key>
<string>Required for ID verification and selfie capture</string>

<key>NSMicrophoneUsageDescription</key>
<string>Required for video liveness checks</string>

<key>NSLocationWhenInUseUsageDescription</key>
<string>Required for fraud prevention</string>

<key>NSPhotoLibraryUsageDescription</key>
<string>Required for proof of address document upload</string>
```

<Info>The SEON SDK handles runtime permission requests automatically during the verification flow.</Info>

***

## Android

### Requirements

* **Minimum SDK version**: API 26 (Android 8.0) or higher
* **Compile SDK version**: API 36 (as configured in the plugin's `build.gradle`)
* **Kotlin**: The library is written in Kotlin

### Dependencies

The library depends on the native SEON Orchestration SDK from Maven Central:

```gradle theme={null}
dependencies {
    implementation 'io.seon:orchestration-android-sdk:0.1.0'
}
```

Ensure your project includes `mavenCentral()` in repositories:

```gradle theme={null}
allprojects {
    repositories {
        mavenCentral()
    }
}
```

### Proxy Activity Pattern

The Android implementation uses a transparent proxy Activity to manage the verification flow.

**Why a proxy Activity:**

1. **Lifecycle requirement** — `ActivityResultLauncher` must be registered before the STARTED lifecycle state
2. **Clean separation** — Keeps verification logic separate from the host app's Activities
3. **Result management** — Provides a clean way to bridge async results back to Dart
4. **No UI interference** — Transparent theme means users don't see the proxy Activity

**Manifest configuration:**

```xml theme={null}
<activity
    android:name=".SeonVerificationActivity"
    android:theme="@android:style/Theme.Translucent.NoTitleBar"
    android:exported="false" />
```

| Attribute                      | Purpose                               |
| ------------------------------ | ------------------------------------- |
| `Theme.Translucent.NoTitleBar` | Makes the Activity invisible to users |
| `exported="false"`             | Activity is only for internal use     |

### MissingLocationPermission Status

Android has a specific verification status for missing location permissions. The proxy Activity maps `SEONOrchFlowResult` result codes to status strings:

```kotlin theme={null}
val resultMap = HashMap<String, Any?>()

when (resultCode) {
    SEONOrchFlowResult.Completed.code -> {
        resultMap["status"] = "completed"
    }
    SEONOrchFlowResult.CompletedSuccess.code -> {
        resultMap["status"] = "completedSuccess"
    }
    SEONOrchFlowResult.Error.code -> {
        resultMap["status"] = "error"
        val errorMessage = data?.getStringExtra(VERIFICATION_ERROR_KEY)
        if (errorMessage != null) {
            resultMap["errorMessage"] = errorMessage
        }
    }
    // ... other result codes mapped similarly
}
```

<Info>The `missingLocationPermission` value exists in the Dart enum for forward compatibility but is not currently emitted by the native Android SDK. Location permission denial is reported as an `error` status with a descriptive `errorMessage`.</Info>

### Permissions

Required permissions in `AndroidManifest.xml`:

```xml theme={null}
<!-- Always required -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

<!-- Optional: only if your workflow uses geolocation-based fraud detection -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- Only for older Android versions -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
                 android:maxSdkVersion="32" />
```

### Hardware Feature Declarations

You should also declare required hardware features. These affect Google Play Store filtering — devices without a camera will not see your app in the store:

```xml theme={null}
<uses-feature
    android:name="android.hardware.camera"
    android:required="true" />
<uses-feature android:name="android.hardware.camera.autofocus" />
```

| Permission                                         | Purpose                                             |
| -------------------------------------------------- | --------------------------------------------------- |
| `CAMERA`                                           | ID verification and selfie capture                  |
| `RECORD_AUDIO` / `MODIFY_AUDIO_SETTINGS`           | Video liveness checks                               |
| `ACCESS_FINE_LOCATION`                             | GeoLocation for fraud prevention API                |
| `WRITE_EXTERNAL_STORAGE` / `READ_EXTERNAL_STORAGE` | Proof of address document handling (legacy Android) |

<Info>The SEON SDK handles runtime permission requests during the verification flow. If permissions are denied, the verification will fail with an appropriate status.</Info>

### ActivityResultLauncher Lifecycle

`ActivityResultLauncher` must be registered **before** the Activity reaches the STARTED state. In the proxy Activity, it's registered at the property level — before `onCreate()` is even called:

```kotlin theme={null}
class SeonVerificationActivity : ComponentActivity() {
    // Registered at property level (before STARTED lifecycle state)
    private val verificationLauncher = registerForActivityResult(StartActivityForResult()) { result ->
        resolveResult(result.resultCode, result.data)
        finish()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Launcher is already registered, safe to use
        OrchestrationService.instance.startVerificationFlow(verificationLauncher, applicationContext)
    }
}
```

This is why the proxy Activity pattern is essential — it provides a fresh lifecycle boundary for each verification.

***

## Common Platform Differences

| Aspect              | iOS                             | Android                                          |
| ------------------- | ------------------------------- | ------------------------------------------------ |
| **Initialization**  | State held in Swift singleton   | Config passed directly to `OrchestrationService` |
| **Error handling**  | FlutterResult with FlutterError | FlutterResult with FlutterError                  |
| **Thread context**  | Explicit main thread dispatch   | Activity lifecycle ensures UI thread             |
| **Verification UI** | `UINavigationController`        | Transparent proxy Activity                       |

### Verification Result Mapping

Both platforms map native SDK result enums to the common `SeonVerificationStatus` enum:

```dart theme={null}
enum SeonVerificationStatus {
  completed,
  completedSuccess,
  completedPending,
  completedFailed,
  interruptedByUser,
  error,
  missingLocationPermission,  // Android-specific
}
```

## Verification UI

The verification flow on both platforms occurs within a WebView context. This is important to understand when:

* Debugging UI issues in the verification flow
* Understanding why permissions like camera and microphone are required
* Troubleshooting Content Security Policy (CSP) or network-related issues

<Warning>
  For security reasons, the SEON SDK actively detects simulators and emulators and may refuse to operate. Always test on real physical devices.
</Warning>

## Testing Considerations

* Debug builds may have different SDK behavior
* Always test release builds before shipping
* Permissions may behave differently in debug mode

## Native SDK References

For platform-specific details beyond the Flutter wrapper, see the native SDK documentation:

* [SEON Orchestration SDK for iOS](https://github.com/seontechnologies/seon-orchestration-sdk-ios-public)
* [SEON Orchestration SDK for Android](https://github.com/seontechnologies/seon-orchestration-sdk-android-public)
