This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Use @agent-swift-cpp-interop.
QwiftUI is an experimental Swift UI library based on Qt6, implementing cross-platform GUI development using Swift 6.2's C++ interoperability features. The library itself is standalone and does not depend on SwiftCrossUI.
Key Goals:
- Provide Qt6 access through direct C++ interop without C API bridging, leveraging Swift 6.2's enhanced C++ support
- Maintain QwiftUI as a standalone library for direct Qt6 access from Swift
- Provide optional SwiftCrossUI integration through a separate Qt6AppBackend target
swift build # Build the project
swift test # Run tests (no tests currently)
swift package resolve # Resolve dependencies
swift run QtDemo #runs the demo app
swift run SimpleTestDemo # Run the UI testing appThe project has transitioned from a C wrapper approach to direct Swift C++ interoperability:
-
QtBridge - C++ wrapper classes for Qt types
- Location:
Sources/QtBridge/ - Purpose: Thin C++ layer providing Swift-compatible Qt widget wrappers
- Key classes:
SwiftQApplication,SwiftQWidget,SwiftQLabel - Uses standard C++ types (std::string) for Swift compatibility
- Dependencies: None (pure C++)
- Location:
-
QwiftUI - Standalone Swift API layer
- Location:
Sources/QwiftUI/ - Purpose: High-level Swift wrappers around QtBridge C++ classes, think UIKit-style API around Qt6 for Swift
- Key files:
SimpleApp.swift- Manages Qt application lifecycleWidget.swift- Base widget wrapper, other controls can inherit from itLabel.swift- Label widget implementationApplication.swift- Application management
- Dependencies: QtBridge only (no SwiftCrossUI dependency)
- Location:
-
Qt6AppBackend - SwiftCrossUI integration (planned)
- Location:
Sources/Qt6AppBackend/ - Purpose: Implements SwiftCrossUI's AppBackend protocol using QwiftUI
- Dependencies: QwiftUI + SwiftCrossUI
- Key components:
QtBackend.swift- AppBackend protocol implementationQtWindow.swift- Window abstraction for SwiftCrossUIQtBackendWidget.swift- Widget wrapper for AppBackend
- Location:
-
QtDemo - Example executable
- Location:
Sources/QtDemo/ - Purpose: Demonstrates direct C++ interop usage
- Shows window creation, widget hierarchy, and event handling
- Can optionally use Qt6AppBackend for SwiftCrossUI demos
- Location:
- CQtWrapper - Original C API wrapper (deprecated in favor of C++ interop)
- Swift version: 6.2 (required for enhanced C++ interop features)
- Interoperability mode:
.interoperabilityMode(.Cxx)enabled on all targets - Qt integration: Direct framework linking without module maps
- Memory management: Raw pointers instead of std::unique_ptr (avoids incomplete type issues)
Working:
- Direct C++ class instantiation from Swift
- Basic widget creation (QWidget, QLabel)
- Window properties (title, size, position)
- Factory functions for widget creation
Known Issues:
- QApplication must be created before any QWidget
- Type casting between widget classes needs refinement
- Qt namespace constants not directly accessible in Swift
// Example from QtBridge.h
class SwiftQApplication {
private:
QApplication* app;
public:
SwiftQApplication(int& argc, char** argv);
~SwiftQApplication();
int exec();
};macOS (Homebrew):
- Qt6 installed via:
brew install qt - Expected location:
/opt/homebrew/Cellar/qt/6.9.1/ - Frameworks linked: QtCore, QtWidgets, QtGui
Linux:
- Qt6 headers expected at:
/usr/include/qt6 - System package installation required
Note: Qt paths are hardcoded in Package.swift and need updating for different Qt versions.
-
Add C++ wrapper class to
QtBridge.h:class SwiftQButton : public SwiftQWidget { public: SwiftQButton(const std::string& text); void setText(const std::string& text); };
-
Implement in
QtBridge.cpp -
Create Swift wrapper in QwiftUI target
-
Use factory functions for complex object creation to avoid initialization order issues
- Update paths in Package.swift:
cxxSettingsinclude pathslinkerSettingsframework paths
- Ensure Qt version compatibility with C++ features used
- Check QApplication initialization order when encountering "Must construct a QApplication before a QWidget" errors
- Use factory functions (
createWidget,createLabel) to ensure proper C++ object construction - Print statements help trace initialization order issues
- Qt applications require running event loop, it must always be started for executable targets.
- Swift Package Manager only - No additional build systems
- Minimal dependencies - QwiftUI itself has no Swift dependencies; Qt6AppBackend adds SwiftCrossUI
- Cross-platform focus - Primary targets are Linux/Windows where SwiftUI is unavailable
- Direct C++ interop - No C wrapper layer per user requirement
- Clean separation - QwiftUI remains standalone; SwiftCrossUI support is optional via Qt6AppBackend
- Follow Swift API Design Guidelines
- Use spaces, not tabs
- Document all public APIs
- C++ code should use modern C++ practices (C++17)
- Prefer Swift-safe C++ patterns (std::string over const char*)
The primary goal is to create a beautiful, idiomatic Swift API that feels natural to Swift developers:
- No raw pointers in Swift code - Hide all unsafe operations in the C++ bridge
- Use Swift enumerations - Replace magic numbers (like
0x0084) with proper Swift enums - Natural naming - Use names like
widgetorcontrolinstead ofpointee - Type safety - Leverage Swift's type system for compile-time safety
- Documentation - Include relevant Qt documentation as Swift comments
- SwiftUI-like feel - Make the API feel familiar to SwiftUI developers where possible
- Automatic memory management - No manual memory management required from client code
Example of what we want to avoid:
// Bad - uses magic numbers and pointee
label.pointee.setAlignment(0x0084)
// Bad - requires manual memory management
storeAllocatedCallback(callback)Example of beautiful Swift:
// Good - uses Swift enum with clear intent
label.setAlignment(.center)
// Good - automatic memory management
button.onClicked {
print("Button clicked!")
}QwiftUI uses a sophisticated automatic memory management system that provides UIKit/AppKit-like simplicity:
- CallbackManager - Singleton that automatically tracks and deallocates callbacks
- Automatic cleanup - Callbacks are automatically freed when widgets are deallocated
- No manual management - Client code never needs to call
storeAllocatedCallbackor similar - Safe event handling - All event handlers are automatically managed through
CallbackHelper
- RAII pattern - C++ objects are created in init and destroyed in deinit
- Reference semantics - Widgets use class semantics with automatic reference counting
- Parent-child relationships - Qt's parent-child hierarchy works seamlessly with Swift ARC
- No manual deletion - The Swift runtime automatically manages widget lifetime
- SafeEventWidget base class - Provides automatic callback cleanup in deinit
- CallbackHelper functions - Automatically register callbacks with CallbackManager
- Unified cleanup -
CallbackManager.shared.remove(for: self)handles all cleanup - Zero client burden - Developers never see or interact with memory management
The project uses Swift 6.2's concurrency features with MainActor isolation:
- All targets have
.defaultIsolation(MainActor.self)in Package.swift - This means all code is MainActor-isolated by default
- No locks are used - rely on actor isolation for thread safety
- Command line arguments are handled through static storage to avoid deinit concurrency issues
- Qt requires argc/argv to remain valid for QApplication's lifetime
The QtDemo target demonstrates QwiftUI capabilities through a dropdown-based gallery system:
- AppMain.swift - Main entry point with dropdown navigation
- ComponentDemos.swift - Individual demo implementations for each widget category
- main.swift - Selects which demo to run (defaults to AppMain)
- Welcome - Introduction screen
- Labels & Alignment - Text positioning demonstrations
- Buttons - Various button styles and states
- Text Input - LineEdit and TextEdit widgets
- Checkboxes & Radio Buttons - Checkable widgets
- Dropdown Lists - ComboBox demonstrations
- Advanced Widgets - Sliders, ProgressBars, ScrollViews, ImageViews
- Mixed Components - Complex form example
- Create a new class implementing
ComponentDemoprotocol in ComponentDemos.swift - Add the demo to the dropdown in AppMain.swift
- Implement
setupDemo(in:)andcleanup()methods - All widgets should be tracked for proper cleanup
For details on implementing SwiftCrossUI's AppBackend protocol with QwiftUI, see docs/004-swiftcrossui-appbackend.md. The integration follows these principles:
- QwiftUI remains a standalone library for direct Qt6 access from Swift
- Qt6AppBackend is a separate target that bridges QwiftUI with SwiftCrossUI
- New Qt widgets should be added to QwiftUI first, then exposed through Qt6AppBackend
- The separation ensures QwiftUI can be used independently by projects that don't need SwiftCrossUI
QwiftUI provides QwiftUITesting target for developers to test their apps. Use the same approach:
- Create a testing target under Tests
- Use Swift testing https://developer.apple.com/documentation/testing
- import QwiftUITesting
- creata a single test function (because Swift tests always run concurrently but our application can't) and in it, write out GUI simulations like clicks, and focus and assertions to make sure functionality is as expected.
- You can extend
QwiftUITestingwith functions when they're needed (e.g. additional simulation of events). - IMPORTANT: All demo apps (QtDemo, Qt6AppBackendDemo) must have such tests.