# Coming from SwiftUI

> View/@State→Component mapping and a rewritten form, with the target difference.

Source: https://crossfly.dev/docs/migrate/from-swiftui — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Coming from SwiftUI

In SwiftUI the `body` is computed and the view **invalidates itself** when `@State` changes. In CrossFly you
**build once** and **mutate**. Read [the philosophy](/docs/migrate/overview) first.

## Translation table
| SwiftUI | CrossFly |
|---|---|
| computed `View` / `body` | UI built **once** in `onShow()` |
| `@State` / `@Binding` / `@ObservedObject` | fields + `repaint()` (no automatic invalidation) |
| `VStack` / `HStack` / `ZStack` | `VerticalLayout` / `HorizontalLayout` / `RelativeLayout` |
| `Spacer()` | `Gap` / fractions (the framework's `Spacer` was **removed**) |
| `.padding()` | `ingap` in the style |
| `List` | `VirtualListView` |
| `Text` | `Label` |
| `Button` | `Button` |
| `NavigationStack` / `.sheet` | stack of `Window` / `ActionSheet` |
| `.alert` | `Alert` |
| `async` / `await` / `Task` | `Thread` + `Component.onUI(...)` |
| `Color` | `int 0xAARRGGBB` |

## Target difference
SwiftUI runs **on Apple only**. CrossFly is **one Java codebase** that runs on iOS **and** Android, Desktop and
Web — the same screen code on all of them (see [platforms](/docs/platforms)).

## Before / after — form
```swift
// SwiftUI
struct LoginView: View {
   @State private var email = ""
   var body: some View {
      VStack {
         TextField("E-mail", text: $email)
         Button("Entrar") { entrar(email) }
      }
   }
}
```
```java
// CrossFly
public class LoginScreen extends Window {
   @Override public void onShow() throws Exception {
      super.onShow();
      setLayout(new VerticalLayout());
      TextField email = new TextField().caption("E-mail");
      add(LAY_AUTO, email);
      Button entrar = new Button("Entrar");
      entrar.onAction(c -> entrar(email.getText()));   // reads the value on submit (no $binding)
      add(LAY_AUTO, entrar);
   }
}
```
> No `$binding`: you **read `getText()`** when you need it (e.g., in `onAction`); there is no automatic
> field↔variable binding (see [Text fields](/docs/ui/forms-and-input/text-fields)).

## See also
- [Forms and input](/docs/ui/forms-and-input) · [Navigation](/docs/ui/navigation)
