SwiftCrossUI
repository·main·Indexed 23 days ago
https://github.com/moreswift/swift-cross-uiA SwiftUI-like framework for creating cross-platform applications using Swift (5.10+). It enables developers to write code once and render it natively across macOS, Windows, Linux, iOS, and tvOS using various backends including AppKit, WinUI, Gtk, and UIKit.
What's inside SwiftCrossUI
- SwiftCrossUI is a framework for creating cross-platform desktop applications for macOS, Linux, Windows, iOS, and tvOS. It uses a declarative syntax inspired by SwiftUI, allowing developers to build native user experiences across multiple platforms using a unified set of concepts and a suite of platform-specific backends.
Overview of SwiftCrossUI example apps
mainSwiftCrossUI includes several example applications designed to demonstrate specific features and APIs:
MusicPlayerExample: Offline music player with playlist persistence.CounterExample: Basic state management with increment/decrement buttons.RandomNumberGeneratorExample: Simple range-based random number generation.WindowingExample: DemonstratesWindowGroup, multi-window apps, and modals (alerts, sheets, file pickers).GreetingGeneratorExample: Demonstrates dynamic state and theForEachview.NavigationExample: ShowcasesNavigationStackand related navigation concepts.SplitExample: Showcases hierarchical navigation usingNavigationSplitView.StressTestExample: Tests view update performance.SpreadsheetExample: Demonstrates table usage.ControlsExample: Showcases various available UI controls.NotesExample: Demonstrates multi-line text editing and realistic app usage.PathsExample: Demonstrates drawing shapes usingPath.WebViewExample: ShowcasesWebView(Note: Currently only works on Apple platforms).AdvancedCustomizationExample: Demonstrates advanced APIs for customizing underlying native views.
Overview of WinUIBackend
mainWinUIBackend is SwiftCrossUI's native Windows backend built on top of WinUI 3. It is the recommended backend for compiling SwiftCrossUI apps for Windows as it aims to provide the most native experience. It supports both arm64 and x64 Windows 10/11 computers.Use controls to receive user input
mainSwiftCrossUI provides a variety of controls to capture and receive user input within your application. These controls allow you to build interactive interfaces by handling different types of data, from simple button clicks to complex date selections.Use Tables in SwiftCrossUI
mainSwiftCrossUI provides aTablecomponent for displaying structured data in rows and columns. To build a table, you use aTableview containing one or moreTableColumndefinitions. Each row's content is defined usingTableRowContentor its tuple-based variants, which allow you to specify what is rendered in each column for a given data item.Understand the new AppBackend protocol hierarchy
mainThe
AppBackendprotocol has been refactored into a modular system organized within theBackendFeaturesnamespace. Instead of one massive protocol, features are now split into smaller, composable protocols. This allows backends to implement only what they support, and features are now optional.There are three core top-level protocol compositions that backend developers should use:
BackendFeatures.Core: The bare minimum required for a SwiftCrossUI app to launch without crashing. This is a trueprotocol.BaseAppBackend: ExtendsCorewith most UI controls, containers, and non-interactive views. This is the protocol required by theApp.Backendassociated type.FullAppBackend: The most comprehensive composition. It includes everything inBaseAppBackendplus advanced features like URL handling, file dialogs, alerts, sheets, web views, tables, gestures, and windowing functionality.
When building or extending a backend, you should aim for
BaseAppBackendas the baseline, adding specific feature protocols fromBackendFeaturesas needed.Understand layout behavior for Stacks (VStack and HStack)
mainThe SwiftCrossUI layout system adopts certain approximations to match SwiftUI's performance characteristics. When working with
VStackorHStack, be aware of the following behaviors:- Minimum Height/Width: The minimum size of a stack is the sum of the minimum sizes of its children. This can cause a stack to overflow its frame if proposed its minimum size, especially when not inside a decoupling container like a
ScrollView. - Concrete Dimensions: If a
VStackis given a concrete height but an unspecified width, it may overflow its reported bounds because the final layout pass is delayed until thecommitstep. - Frame Clamping: When a
minHeightframe is proposed an unspecified height, the child is first laid out with an unspecified height, then the frame clamps it. During thecommitstep, the child is laid out again with the clamped height. This can cause the unconstrained axis of a frame to not perfectly hug its content, even if it appears it should.
- Minimum Height/Width: The minimum size of a stack is the sum of the minimum sizes of its children. This can cause a stack to overflow its frame if proposed its minimum size, especially when not inside a decoupling container like a
How Dynamic Properties (State/Environment) are updated
mainProperties conforming to
DynamicProperty(like@Stateor@Environment) are updated when view or app bodies are recomputed using two methods managed byDynamicPropertyUpdater:- Primary Method (
DynamicKeyPath): UsesDynamicKeyPathto construct a runtime key path by calculating the property's offset within the type usingwithUnsafeBytes. This is highly performant. - Fallback Method (
Mirror): If the primary method fails (e.g., due to bit-level collisions between properties), the system falls back to usingMirrorto query and update properties. This method is significantly slower (up to 1500x) and is used only as a safety measure.
- Primary Method (
Create scenes using Scene and SceneBuilder
mainYou can define the structure of your application by using theSceneprotocol and theSceneBuilderDSL. These allow you to declare top-level containers that host your application's root views.Draw custom shapes using Path and AffineTransform
mainFor complex or custom geometry, use thePathAPI.Pathallows you to define arbitrary shapes by specifying a sequence of lines, curves, and arcs. To manipulate these paths (such as scaling, rotating, or translating them), useAffineTransformto apply geometric transformations to the path coordinates.Understand SwiftCrossUI navigation paradigms
mainSwiftCrossUI provides two primary navigation models for structuring your application's user interface:
- Hierarchical Navigation: Uses
NavigationSplitViewto create multi-column layouts (e.g., sidebar, content, and detail views), common in iPadOS or macOS-style applications. - Stack-based Navigation: Uses
NavigationStackto manage a stack of views that are pushed and popped, typical for mobile-style drill-down interfaces.
- Hierarchical Navigation: Uses
How SwiftCrossUI apps start and initialize
mainThe entry point for a SwiftCrossUI application is the implementation of the
Appprotocol, specifically themain()function. Whenmain()is called, it defers to_App.run(), which manages the backend lifecycle and application setup.The initialization sequence includes:
- Starting the backend's main loop.
- Computing the root
Environment. - Instantiating
Environmentproperties on the app struct viaupdateDynamicProperties(of:previousValue:environment:). - Observing
Stateproperties usingMirrorandobserveAsUIUpdater(backend:action:)(which includes update debouncing). - Creating the root scene graph node.
- Listening for system-level environment changes (like theme changes).
- Updating the root scene graph node.