# Introduction to SwiftUI

> Learn how SwiftUI uses declarative views, state, modifiers, scenes, and previews to build interfaces across Apple platforms.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2021-09-13 | Updated: 2026-07-18 | Topics: [Swift](https://flaviocopes.com/tags/swift/) | Canonical: https://flaviocopes.com/swiftui-introduction/

**SwiftUI** is Apple's framework for building user interfaces with Swift.

You can use it across iOS, iPadOS, macOS, watchOS, tvOS, and visionOS. Many views adapt their appearance and behavior to the platform where they run.

SwiftUI uses a **declarative** approach. You describe what the interface should display for the current data, and SwiftUI updates the affected views when that data changes.

This is different from manually creating every view and telling it how to update after each event.

## Does SwiftUI replace UIKit and AppKit?

No.

SwiftUI is the recommended starting point for many new interfaces, but UIKit, AppKit, and WatchKit are still supported frameworks.

You can put a SwiftUI view inside an existing UIKit or AppKit app. You can also wrap a UIKit view or view controller and use it inside SwiftUI.

This lets you adopt SwiftUI one screen at a time. It also helps when SwiftUI does not provide a feature your app needs.

## The structure of a SwiftUI app

A SwiftUI app starts with a type that conforms to the `App` protocol:

```swift
import SwiftUI

@main
struct HelloApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}
```

The `@main` attribute marks the app's entry point.

The `body` property returns a scene. A scene represents part of the interface with a lifecycle managed by the system.

`WindowGroup` is the usual main scene for an app. It contains the root view, which is `ContentView` in this example.

## Creating a view

You create a custom view by defining a structure that conforms to the `View` protocol:

```swift
import SwiftUI

struct ContentView: View {
  var body: some View {
    Text("Hello, world!")
  }
}
```

The `View` protocol requires a `body` computed property. The value returned by `body` describes that part of the interface.

Views can contain other views. Use a stack to arrange several views:

```swift
struct ContentView: View {
  var body: some View {
    VStack {
      Text("Hello, world!")
      Text("Welcome to SwiftUI")
    }
  }
}
```

`VStack` arranges its child views vertically. SwiftUI also provides `HStack`, `ZStack`, lists, grids, forms, and many other containers.

## What does some View mean?

You will see `some View` in almost every SwiftUI file.

`some` creates an **opaque result type**. It tells callers that `body` returns a value conforming to `View` without exposing the long concrete type produced by the view hierarchy.

The compiler still knows the exact underlying type and checks it at compile time. SwiftUI's result builder also lets you write multiple child views and supported conditionals inside `body`.

You do not normally need to write the concrete type yourself.

## Configuring views with modifiers

You customize a view by applying modifiers:

```swift
Text("Hello, world!")
  .font(.title)
  .bold()
  .padding()
```

Each modifier returns a new view that wraps or configures the previous one.

The order can matter. For example, adding a background before padding gives a different result than adding it after padding.

## Connecting a view to state

The interface becomes interesting when it changes in response to data.

Use `@State` for a small value owned by one view:

```swift
struct CounterView: View {
  @State private var count = 0

  var body: some View {
    VStack {
      Text("Count: \(count)")

      Button("Add one") {
        count += 1
      }
    }
  }
}
```

When the button changes `count`, SwiftUI updates the parts of the interface that read it.

Declare state as private and keep it in the highest view that owns the value. Pass a binding to a child view when that child needs to change the value.

Use an observable model for application data shared by several views. `@State` is best for temporary interface state, not permanent storage.

## Views are descriptions

A SwiftUI view structure is a lightweight value describing part of the interface. SwiftUI can create that value and evaluate its `body` many times.

Do not treat a view structure like a long-lived view controller. Keep mutable data in state or a model, and avoid starting network requests or other side effects directly while computing `body`.

Use lifecycle modifiers such as `task`, `onAppear`, and `onChange` when the view needs to perform work.

## Previewing a view in Xcode

Xcode can show a live preview next to your Swift code:

```swift
#Preview {
  ContentView()
}
```

Previews are useful for checking different data, screen sizes, accessibility settings, and appearance modes without navigating through the whole app.

You still need to run the app on simulators and real devices. A preview does not replace normal testing.

## Starting a SwiftUI project

Open Xcode and create a new app project. Select SwiftUI for the interface when Xcode offers the choice.

Xcode creates an `App` type, a `WindowGroup`, and an initial view. Start by changing that view and running the project.

Check the deployment target before using a new SwiftUI API. SwiftUI evolves with each Apple platform release, and an API available in the current SDK might require a newer operating system than your app supports.

The official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui) covers the framework, and Apple's [SwiftUI pathway](https://developer.apple.com/swiftui/) is a good place to continue learning.
