# How iloader Handles Device Selection and Pairing with iOS Devices: A Technical Deep Dive

> Discover how iloader handles iOS device selection and pairing using Tauri, React, and Rust. Learn about automatic detection and manual pairing processes.

- Repository: [Nicholas Sharp/iloader](https://github.com/nab138/iloader)
- Tags: deep-dive
- Published: 2026-09-12

---

**iloader implements device selection and pairing through a Tauri-based architecture where React frontend components orchestrate Rust backend commands that interface directly with Apple's MobileDevice APIs, automatically detecting stored pairings or guiding users through manual selection and pairing file placement.**

nab138/iloader is a desktop application that simplifies iOS device management by wrapping low-level Apple framework interactions in a modern TypeScript/React interface. The application handles the complete lifecycle of device discovery, selection, and secure pairing through a coordinated command flow between the frontend UI and the Rust backend, supporting both USB and network connections.

## Device Discovery and Automatic Pairing Detection

The device selection process begins immediately when the application mounts the main device interface component.

### Scanning for Available iOS Devices

When the `<Device>` component initializes, it invokes the `list_devices` command to query the backend for connected iOS hardware. According to the source code in [`src/Device.tsx`](https://github.com/nab138/iloader/blob/main/src/Device.tsx) at line 98, this command returns an array of `DeviceInfo` objects containing essential metadata:

- Device name and unique identifier (UUID)
- Connection type (USB or network)
- iOS version information

```typescript
// From src/Device.tsx
const devices = await invoke<DeviceInfo[]>("list_devices");

```

### Automatic Selection of Pre-Paired Devices

Before presenting the device list to the user, iloader checks for existing trust relationships. At lines 23-27 in [`src/Device.tsx`](https://github.com/nab138/iloader/blob/main/src/Device.tsx), the application queries each discovered device using the `has_stored_rppairing` command. If any device already maintains a stored pairing file from a previous session, iloader automatically selects the first qualified device via `selectDevice(devicesWithPairing[0])`, bypassing manual selection for returning users.

## Manual Device Selection and the Pairing Workflow

When automatic selection does not occur—or when users explicitly choose a different device—iloader initiates a structured pairing handshake.

### Initiating Selection via the UI

Each rendered device card functions as a clickable button (lines 8-12 in [`src/Device.tsx`](https://github.com/nab138/iloader/blob/main/src/Device.tsx)). Clicking a device triggers the `selectDevice` callback, which performs three critical operations:

1. **State Transmission**: Sends the selected device to the backend via the `set_selected_device` command (lines 66-75)
2. **Visual Feedback**: Displays a modal indicating "pairing in progress" while the handshake occurs (lines 70-84)
3. **Result Handling**: Processes success, cancellation, or error states with appropriate UI updates and toast notifications

```typescript
// Inside src/Device.tsx
const selectDevice = useCallback((device: DeviceInfo | null) => {
  invoke("set_selected_device", { device })
    .then(() => setSelectedDevice(device))
    .catch(e => toast.error(err(t("device.failed_select"), e)));
}, [setSelectedDevice, err, t]);

```

### Backend Pairing Handshake

While the modal displays, the Rust backend (implemented in [`src-tauri/src/lib.rs`](https://github.com/nab138/iloader/blob/main/src-tauri/src/lib.rs)) executes the low-level pairing protocol using Apple's MobileDevice framework. The backend handles certificate exchanges, trust dialog prompts on the iOS device, and secure session establishment. The frontend remains responsive during this process, updating the modal status based on command resolution.

## Managing Pairing Files and Advanced Operations

Beyond initial device connection, iloader provides granular control over pairing file distribution and management through dedicated interface pages.

### Placing Pairing Files for Specific Applications

The **Pairing** page ([`src/pages/Pairing.tsx`](https://github.com/nab138/iloader/blob/main/src/pages/Pairing.tsx)) lists applications installed on the iOS device that support external pairing file injection. At line 31, the component calls `installed_pairing_apps` to retrieve eligible targets, including each app's bundle ID and file system path.

When a user clicks **Place**, the application invokes `place_pairing_cmd` (lines 51-55) to transfer the pairing file onto the device:

```typescript
// Inside src/pages/Pairing.tsx
const pair = useCallback(async (app: PairingAppInfo) => {
  const promise = invoke<void>("place_pairing_cmd", {
    bundleId: app.bundleId,
    path: app.path,
  });
  toast.promise(promise, {
    loading: t("pairing.placing_pairing_file"),
    success: t("pairing.pairing_file_placed_success"),
    error: e => err(t("pairing.failed_place_pairing"), e),
  });
}, [t, err]);

```

### Bulk and Advanced Pairing Management

The interface supports bulk operations through a "Place All" button that iterates through all discovered pairing-capable apps, sequentially executing `place_pairing_cmd` for each. Additionally, the **Settings** page ([`src/pages/Settings.tsx`](https://github.com/nab138/iloader/blob/main/src/pages/Settings.tsx)) provides administrative controls:

- **Delete Stored Pairing**: Clears the persistent pairing record via `delete_stored_rppairing` (lines 78-89) and resets the selected device state
- **Export Pairing File**: Advanced users can extract pairing data using `export_pairing_cmd` (lines 43-48) for backup or transfer purposes

```typescript
// From src/pages/Settings.tsx
await invoke("delete_stored_rppairing");
await invoke("set_selected_device");
setSelectedDevice(null);

```

All commands utilize `toast.promise` wrappers to provide immediate visual feedback on operation status, ensuring users understand when background processes succeed or fail.

## Backend Architecture and MobileDevice Integration

The Rust backend serves as the abstraction layer between the TypeScript frontend and Apple's proprietary MobileDevice framework. Each frontend command maps to native Rust implementations in [`src-tauri/src/lib.rs`](https://github.com/nab138/iloader/blob/main/src-tauri/src/lib.rs) that handle:

- USB and network socket management with iOS devices
- Certificate parsing and validation for pairing handshakes
- File system operations for placing and extracting pairing records
- Secure communication channel establishment

This architecture allows iloader to present a web-based user experience while performing operations that require native platform privileges and direct hardware access.

## Summary

- **iloader** uses a Tauri stack (React frontend, Rust backend) to bridge web technologies with native iOS device management.
- **Device discovery** occurs via the `list_devices` command in [`src/Device.tsx`](https://github.com/nab138/iloader/blob/main/src/Device.tsx), returning comprehensive device metadata.
- **Automatic pairing detection** checks for stored pairing files using `has_stored_rppairing`, enabling zero-click reconnection for trusted devices.
- **Manual selection** triggers the `set_selected_device` command, which initiates a backend handshake while displaying progress modals.
- **Pairing file management** happens through [`src/pages/Pairing.tsx`](https://github.com/nab138/iloader/blob/main/src/pages/Pairing.tsx), supporting individual and bulk placement via `place_pairing_cmd`.
- **Advanced controls** in Settings allow deletion (`delete_stored_rppairing`) and export (`export_pairing_cmd`) of pairing records.

## Frequently Asked Questions

### How does iloader automatically detect previously paired iOS devices?

iloader queries each discovered device using the `has_stored_rppairing` command (found at lines 23-27 in [`src/Device.tsx`](https://github.com/nab138/iloader/blob/main/src/Device.tsx)). This checks the backend's storage for existing pairing records (rppairing files) associated with each device's UUID. If the command returns true for any device, iloader immediately calls `selectDevice()` with the first matching device, establishing the connection without requiring user interaction.

### What happens when I click a device card in the iloader interface?

Clicking a device card invokes the `selectDevice` callback, which sends the device object to the Rust backend via `set_selected_device` (lines 66-75 in [`src/Device.tsx`](https://github.com/nab138/iloader/blob/main/src/Device.tsx)). The UI simultaneously displays a pairing progress modal while the backend performs the MobileDevice handshake. Upon resolution, the modal closes and the UI updates to show the selected device state, or displays an error toast if the pairing fails or is cancelled.

### Can I place pairing files for multiple apps simultaneously?

Yes. While the `place_pairing_cmd` command handles single app placement (lines 51-55 in [`src/pages/Pairing.tsx`](https://github.com/nab138/iloader/blob/main/src/pages/Pairing.tsx)), iloader provides a bulk placement button that iterates through all apps returned by `installed_pairing_apps`. The interface executes the command sequentially for each discovered pairing-capable application, providing individual toast notifications for each operation's success or failure.

### Where does iloader store pairing information, and how can I remove it?

Pairing information is stored on the host machine via the Rust backend's data directory, accessible through the `delete_stored_rppairing` command. In [`src/pages/Settings.tsx`](https://github.com/nab138/iloader/blob/main/src/pages/Settings.tsx) (lines 78-89), the Settings page provides a "Delete stored rppairing" button that invokes this command followed by `set_selected_device` with a null parameter, effectively clearing the persistent record and resetting the UI state to unpaired.