Skip to content

iOS: port onboarding + QR pairing flow stability#18162

Merged
mbelinky merged 15 commits intomainfrom
port/ios-onboarding-qr-flow
Feb 16, 2026
Merged

iOS: port onboarding + QR pairing flow stability#18162
mbelinky merged 15 commits intomainfrom
port/ios-onboarding-qr-flow

Conversation

@mbelinky
Copy link
Contributor

@mbelinky mbelinky commented Feb 16, 2026

Summary

Ports iOS onboarding/QR pairing flow improvements from ios-new-alpha-core into main as an isolated pack.

Included

  • QR scanner onboarding flow
  • Onboarding wizard/state flow restoration and stability fixes
  • Pairing reconnect loop safeguards and launch-route handling
  • iOS crash fixes around NodeAppModel access in onboarding surfaces

Scope

  • iOS onboarding/gateway UX files and related tests only

Testing

  • Targeted iOS test run was attempted with single-worker settings, but local disk space blocked full Xcode test execution (code 28).
  • This PR is intentionally isolated for CI validation on clean runners.

Greptile Summary

Ports the iOS onboarding wizard, QR pairing flow, and reconnect stability improvements from the alpha branch into main. Adds a full-screen onboarding wizard (OnboardingWizardView) with QR scanning, manual connection, and pairing approval flows; introduces GatewayConnectionIssue for classifying connection errors; adds a keepalive mechanism to GatewayChannelActor to prevent NAT/proxy idle drops; and extends the device-pair extension with a /pair qr action for QR code generation.

  • Build blocker: GatewayQuickSetupSheet is referenced in RootCanvas.swift but never defined — iOS will not compile
  • Build blocker: renderQrPngBase64 is imported from openclaw/plugin-sdk in the device-pair extension but is not exported from the plugin-sdk barrel (src/plugin-sdk/index.ts)
  • Layout bug: Duplicate .padding(.horizontal, 24) in OnboardingWizardView.swift welcome step causes double horizontal padding on the status text
  • Trust prompt alert race condition fix in GatewayTrustPromptAlert is correct — binding setter is now a no-op to prevent SwiftUI dismissal from racing with button handlers
  • Talk mode sync via SSE events is properly guarded against infinite loops
  • New tests cover deep link parsing, connection issue detection, onboarding state, and gateway controller commands

Confidence Score: 2/5

  • This PR has two compilation/runtime blockers that must be resolved before merging.
  • Two critical issues prevent this from building: (1) GatewayQuickSetupSheet is used in RootCanvas.swift but the type does not exist anywhere in the codebase, and (2) renderQrPngBase64 is imported in the device-pair extension from openclaw/plugin-sdk but not exported from that module. The underlying logic for onboarding, pairing, keepalive, and talk mode sync is well-structured and correctly implemented.
  • apps/ios/Sources/RootCanvas.swift (missing GatewayQuickSetupSheet type), extensions/device-pair/index.ts (missing renderQrPngBase64 export from plugin-sdk), apps/ios/Sources/Onboarding/OnboardingWizardView.swift (duplicate padding layout bug)

Last reviewed commit: 421b6f9

@github-actions
Copy link
Contributor

⚠️ Formal models conformance drift detected

The formal models extracted constants (generated/*) do not match this openclaw PR.

This check is informational (not blocking merges yet).
See the formal-models-conformance-drift artifact for the diff.

If this change is intentional, follow up by updating the formal models repo or regenerating the extracted artifacts there.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

14 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import os from "node:os";
import { approveDevicePairing, listDevicePairing } from "openclaw/plugin-sdk";
import { approveDevicePairing, listDevicePairing, renderQrPngBase64 } from "openclaw/plugin-sdk";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renderQrPngBase64 is not exported from plugin-sdk

renderQrPngBase64 is defined in src/web/qr-image.ts but is not re-exported from src/plugin-sdk/index.ts. The existing exports (approveDevicePairing, listDevicePairing) are present at lines 164-165 of the plugin-sdk barrel, but renderQrPngBase64 is missing. This import will fail at runtime when the /pair qr action is invoked.

Either add renderQrPngBase64 to src/plugin-sdk/index.ts exports, or use a dynamic import from the internal path.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/device-pair/index.ts
Line: 3:3

Comment:
**`renderQrPngBase64` is not exported from plugin-sdk**

`renderQrPngBase64` is defined in `src/web/qr-image.ts` but is not re-exported from `src/plugin-sdk/index.ts`. The existing exports (`approveDevicePairing`, `listDevicePairing`) are present at lines 164-165 of the plugin-sdk barrel, but `renderQrPngBase64` is missing. This import will fail at runtime when the `/pair qr` action is invoked.

Either add `renderQrPngBase64` to `src/plugin-sdk/index.ts` exports, or use a dynamic import from the internal path.

How can I resolve this? If you propose a fix, please make it concise.

agentName: self.appModel.activeAgentName,
userAccent: self.appModel.seamColor)
case .quickSetup:
GatewayQuickSetupSheet()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GatewayQuickSetupSheet is not defined anywhere in the codebase

GatewayQuickSetupSheet() is instantiated here but no struct/class with this name exists in the repo. This will cause a compilation error. Was this type meant to be included in this PR, or should the .quickSetup sheet case be removed until the view is ready?

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/ios/Sources/RootCanvas.swift
Line: 107:107

Comment:
**`GatewayQuickSetupSheet` is not defined anywhere in the codebase**

`GatewayQuickSetupSheet()` is instantiated here but no struct/class with this name exists in the repo. This will cause a compilation error. Was this type meant to be included in this PR, or should the `.quickSetup` sheet case be removed until the view is ready?

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +325 to +331
Text(self.statusLine)
.font(.footnote)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal, 24)
.padding(.horizontal, 24)
.padding(.bottom, 48)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate .padding(.horizontal, 24) — likely copy-paste error

The Text view gets .padding(.horizontal, 24) on line 329, then the indentation shifts and a second .padding(.horizontal, 24) is applied on line 330 (which attaches to the parent VStack due to the indentation break). This results in 48pt total horizontal padding on the status line, whereas the surrounding buttons only get 24pt (via the VStack). The .padding(.bottom, 48) on line 331 also lands on the VStack rather than the Text.

Suggested change
Text(self.statusLine)
.font(.footnote)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal, 24)
.padding(.horizontal, 24)
.padding(.bottom, 48)
Text(self.statusLine)
.font(.footnote)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal, 24)
.padding(.bottom, 48)
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/ios/Sources/Onboarding/OnboardingWizardView.swift
Line: 325:331

Comment:
**Duplicate `.padding(.horizontal, 24)` — likely copy-paste error**

The `Text` view gets `.padding(.horizontal, 24)` on line 329, then the indentation shifts and a second `.padding(.horizontal, 24)` is applied on line 330 (which attaches to the parent `VStack` due to the indentation break). This results in 48pt total horizontal padding on the status line, whereas the surrounding buttons only get 24pt (via the `VStack`). The `.padding(.bottom, 48)` on line 331 also lands on the `VStack` rather than the `Text`.

```suggestion
            Text(self.statusLine)
                .font(.footnote)
                .foregroundStyle(.secondary)
                .multilineTextAlignment(.center)
                .padding(.horizontal, 24)
                .padding(.bottom, 48)
        }
```

How can I resolve this? If you propose a fix, please make it concise.

@mbelinky mbelinky force-pushed the port/ios-onboarding-qr-flow branch from 421b6f9 to b0a3d22 Compare February 16, 2026 16:18
Marvae and others added 15 commits February 16, 2026 17:19
iOS:
- QR scanner view using DataScannerViewController
- Photo library QR detection via CIDetector for saved QR images
- Deep link parser for openclaw://gateway URLs and base64url setup codes
- Onboarding wizard: full-screen welcome with "Scan QR Code" button,
  auto-connect on scan, back navigation, step indicators for manual flow

Backend:
- Add /pair qr action to device-pair extension for QR code generation
- TUI/WebUI differentiation: ASCII QR for TUI, markdown image for WebUI
- Telegram: send QR as media attachment via sendMessageTelegram
- Add data URI support to loadWebMedia for generic base64 media handling
- Export renderQrPngBase64 from plugin SDK for extension use

Co-Authored-By: Claude Opus 4.6 <[email protected]>
(cherry picked from commit d79ed65)
@mbelinky mbelinky force-pushed the port/ios-onboarding-qr-flow branch from b0a3d22 to a87eade Compare February 16, 2026 16:20
@github-actions
Copy link
Contributor

⚠️ Formal models conformance drift detected

The formal models extracted constants (generated/*) do not match this openclaw PR.

This check is informational (not blocking merges yet).
See the formal-models-conformance-drift artifact for the diff.

If this change is intentional, follow up by updating the formal models repo or regenerating the extracted artifacts there.

@mbelinky mbelinky merged commit 130e59a into main Feb 16, 2026
24 of 25 checks passed
@mbelinky mbelinky deleted the port/ios-onboarding-qr-flow branch February 16, 2026 16:22
@mbelinky
Copy link
Contributor Author

Merged via squash.

Thanks @mbelinky!

@mbelinky
Copy link
Contributor Author

@Marvae Hi, just noticing the QR scanning feature was merged to main. Would appreciate a mention in the description and Changelog. Thanks!

archerhpagent pushed a commit to howardpark/openclaw that referenced this pull request Feb 18, 2026
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: a87eade
Co-authored-by: mbelinky <[email protected]>
Co-authored-by: mbelinky <[email protected]>
Reviewed-by: @mbelinky
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 1, 2026
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: a87eade
Co-authored-by: mbelinky <[email protected]>
Co-authored-by: mbelinky <[email protected]>
Reviewed-by: @mbelinky

(cherry picked from commit 130e59a)

# Conflicts:
#	CHANGELOG.md
#	apps/ios/Sources/Gateway/GatewayTrustPromptAlert.swift
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 3, 2026
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: a87eade
Co-authored-by: mbelinky <[email protected]>
Co-authored-by: mbelinky <[email protected]>
Reviewed-by: @mbelinky

(cherry picked from commit 130e59a)

# Conflicts:
#	CHANGELOG.md
#	apps/ios/Sources/Gateway/GatewayTrustPromptAlert.swift
#	extensions/device-pair/index.ts
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: a87eade
Co-authored-by: mbelinky <[email protected]>
Co-authored-by: mbelinky <[email protected]>
Reviewed-by: @mbelinky
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants