Skip to content

[DO NOT MERGE] feat(ios): add experimental SwiftUI client - #5178

Open
t3dotgg wants to merge 71 commits into
mainfrom
t3code/rebuild-mobile-app-swift
Open

[DO NOT MERGE] feat(ios): add experimental SwiftUI client#5178
t3dotgg wants to merge 71 commits into
mainfrom
t3code/rebuild-mobile-app-swift

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Aug 1, 2026

Copy link
Copy Markdown
Member

Caution

DO NOT MERGE. This is an experimental PR for teammate testing only.

T3 Code's shipped mobile client is React Native. This experiment adds a standalone native SwiftUI client so the team can try its feel, performance, and connection workflows without replacing any existing surface.

The app lives entirely in apps/swift-ios, speaks the existing server contracts directly, and installs side by side as T3 Code (SwiftUI) with bundle ID com.t3tools.t3code.swiftui.

Try it

  1. Open apps/swift-ios/T3Code.xcodeproj in Xcode.
  2. Select the T3Code scheme and an iOS 17+ simulator or device.
  3. Build and pair with an existing T3 Code server using its URL and code, pairing link, or QR code.

See apps/swift-ios/README.md for architecture, included functionality, and known gaps.

What to test

  • Pairing, onboarding, and multiple environments
  • Web V2 home behavior with large thread collections
  • Message-first thread creation and model selection
  • Long Markdown transcripts, composer states, approvals, and input requests
  • Image attachments, reconnect behavior, project tools, and terminal sessions

Preview

Home Thread
SwiftUI home with a large thread collection SwiftUI long Markdown thread

Verification

  • 245 native simulator tests passed, 0 failed, 1 skipped

  • Repeated A to B to C to A long-thread navigation verified against an isolated real-data snapshot

  • Latest build compiled, installed, and launched on an iPhone 17 Pro simulator

  • Signed latest build installed on Big O and DevPhone15; automatic launch deferred because both devices were locked

  • Remove DO NOT MERGE only after explicit maintainer approval

This PR was built by GPT-5.6-sol using the Codex harness in T3 Code.


Note

High Risk
New authentication, OAuth, DPoP token exchange, and Keychain-backed crypto on the critical path for cloud environment access; mistakes could leak credentials or break managed connections.

Overview
Adds the T3 Connect stack under App/Cloud/ so the SwiftUI client can sign in with T3 accounts and attach to relay-managed environments instead of only manual pairing.

Clerk OAuth (T3ConnectClerkSession) wraps ClerkKit for Google/GitHub sign-in, session refresh, and relay JWTs from a configurable template, with t3code:// redirect handling.

Relay HTTP client (T3ConnectRelayClient) lists linked environments, fetches status, connects for bootstrap credentials, links/unlinks, and registers devices and Live Activities. Relay calls use DPoP access tokens from Clerk JWT token exchange, with caching and 401 retry.

Device DPoP (T3ConnectDPoPSigner) keeps a P-256 key in the Keychain and builds ES256 DPoP proofs for relay and environment requests.

Managed environment auth (T3ConnectManagedEnvironmentAuthorizer, T3ConnectRuntimeAuthorization) exchanges bootstrap credentials for environment DPoP tokens, authorizes HTTP with per-request proofs, issues WebSocket tickets, and coalesces credential refresh via ManagedEnvironmentAuthorizing.

Orchestration (T3ConnectController) drives account state, environment list refresh with parallel status, connect/credential flows, sign-out with best-effort device unregister, and posts t3ConnectSessionChanged. Configuration is resolved from Info.plist with HTTPS relay URL validation.

Also adds Swift/iOS .gitignore entries for DerivedData and xcuserdata.

Reviewed by Cursor Bugbot for commit a5f4313. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add experimental SwiftUI iOS client with widgets, share extension, and cloud relay support

  • Introduces a full SwiftUI iOS app (apps/swift-ios) including a workspace/thread browser, chat composer with Markdown rendering, file browser, terminal, source control, and review screens
  • Adds a Share Extension that accepts text, URLs, and up to 8 images from the iOS share sheet, staging them into a shared app group container for import into a project draft
  • Adds WidgetKit extensions for a Recent Tasks home screen widget and a Live Activity on the lock screen and Dynamic Island with deep link support
  • Implements a WebSocket RPC transport layer (WebSocketRPCClient) with reconnection, request coalescing, deadlines, and permessage-deflate support
  • Adds T3 Connect cloud relay integration with DPoP proof-of-possession auth, Clerk OAuth sign-in, managed environment linking, and APNS/push registration
  • Adds a background refresh coordinator, Siri Shortcuts for new tasks and opening recent threads, haptic feedback, and deep link parsing for custom and trusted web URL schemes
  • Includes extensive unit tests covering Markdown parsing/caching, multi-environment routing, retry identity stability, composer features, outbox policy, and platform/extension contracts
  • Risk: marked DO NOT MERGE; this is an experimental branch and is not intended for production

Macroscope summarized f1df601.

t3dotgg added 30 commits August 1, 2026 02:10
Comment thread apps/swift-ios/Resources/Info.plist Outdated
Comment thread apps/swift-ios/App/Cloud/T3ConnectManagedAuthorization.swift
Comment thread apps/swift-ios/Features/Settings/SettingsView.swift
Comment thread apps/swift-ios/App/Cloud/T3ConnectRelayClient.swift Outdated
} label: {
Label("Cloud environments", systemImage: "cloud.slash")
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

T3 Connect UI never reachable

High Severity

Settings only opens T3ConnectView when model.client conforms to T3ConnectCapable, but NativeFeatureClient never adopts that protocol or implements connectT3Environment. The Cloud environments entry always stays on the unavailable path, so the new T3 Connect flow cannot be reached or tested.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3dfa445. Configure here.

Comment thread apps/swift-ios/App/Cloud/T3ConnectConfiguration.swift
Comment thread apps/swift-ios/Scripts/ci-test.sh
Comment thread apps/swift-ios/Core/T3Client.swift Outdated
Comment thread apps/swift-ios/App/NativeFeatureClient.swift
Comment thread apps/swift-ios/App/Cloud/T3ConnectManagedAuthorization.swift
Comment thread apps/swift-ios/App/Platform/PlatformDeepLinks.swift Outdated
Comment thread apps/swift-ios/Extensions/Share/SharePayloadLoader.swift
Comment thread apps/swift-ios/Extensions/Share/SharePayloadLoader.swift Outdated
Comment thread apps/swift-ios/App/Platform/PlatformCloudDelivery.swift
Comment thread apps/swift-ios/App/Platform/PlatformAgentAwareness.swift
Comment thread apps/swift-ios/Features/Root/FeatureRootModel.swift
Comment thread apps/swift-ios/App/Platform/PlatformAgentAwareness.swift
Comment thread apps/swift-ios/App/NativeFeatureClient.swift Outdated
Comment thread apps/swift-ios/Scripts/resolve-device-udid.swift Outdated
Comment thread apps/swift-ios/Core/WebSocketRPC.swift
Comment thread apps/swift-ios/Core/WebSocketRPC.swift Outdated
Comment thread apps/swift-ios/App/NativeFeatureClient.swift
case let .pair(endpoint, code):
didConnect = await model.pair(endpoint: endpoint, token: code)
case let .activate(id, _):
didConnect = await model.activateEnvironment(id)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Activate success skips connection check

Medium Severity

Onboarding now treats a non-throwing activateEnvironment as connected. That return value only means activation and snapshot install succeeded, while initialSnapshot can still leave connection.state as .disconnected when the server is unreachable. The previous check against the connected snapshot state is gone, so success and onConnected() can run without an active connection.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d4bc68a. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

There are 5 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a5f4313. Configure here.

return
}
presenter.present(picker, animated: true)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Photo library presentation race

Medium Severity

presentPhotoLibrary waits a fixed 0.35s then imperatively presents PHPickerViewController from the key window. If the confirmation dialog is still dismissing, UIKit can ignore present with no error handling or delegate cleanup, so Photo Library appears to do nothing and the retained delegate can leak. The delay is also long enough for navigation away before presentation, which can show the picker over the wrong screen.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a5f4313. Configure here.

}
onSelect(images)
FeaturePhotoLibraryPresenter.finish(picker)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Silent photo load failures

Medium Severity

Selected photo results load with try? and failed items are skipped. If every item fails, onSelect still receives an empty array and loadPhotoSelection returns without setting errorMessage, so the picker dismisses and the user gets no attachments and no error—unlike the Files and Camera paths.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a5f4313. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DO NOT MERGE Experimental pull request. Do not merge. enhancement Requested improvement or new capability. size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant