Swift and SwiftUI tutorials for Swift Developers

SwiftUI Tutorial for Beginners

Welcome. If you are reading this, you have decided to take the leap. You want to build applications for iPhone, Mac, iPad, or even visionOS. You have arrived at the perfect time. SwiftUI is no longer “the future”; it is the absolute present.

This article is not just a tutorial; it is a roadmap. We are going to ignore unnecessary jargon and focus on how to think like an Apple developer. Grab a coffee, open your mind, and let’s begin.


Module 1: The Philosophy and Environment

What is SwiftUI and why should I care?

Before writing code, you must understand the paradigm shift. In the past (about 6 years ago), we programmed in an Imperative way. We told the computer how to do things step by step:

  1. Create a button.
  2. Place it at position X: 50, Y: 100.
  3. Change its color to red.
  4. If someone taps it, change the text.

SwiftUI is Declarative. You tell the computer what you want, and it handles the how.

  • “I want a list of tasks showing this array of data.”
  • “I want the text to be red when there is an error.”

You define the rules, SwiftUI draws the interface. If the data changes, SwiftUI automatically redraws the interface for you.

Your Tool: Xcode

To code, you need a workshop. That workshop is Xcode.

  1. Download it for free from the Mac App Store.
  2. Upon opening, you will see “Create New Project”.
  3. Select App (under the iOS tab).
  4. Project Name: HelloSwiftUI.
  5. Interface: SwiftUI (obviously).
  6. Language: Swift.

When it opens, you will see three main areas:

  • Left: The Navigator (your files).
  • Center: The Editor (your code) and the Canvas (real-time visual preview).
  • Right: The Inspector (properties).

Module 2: Swift Fundamentals (Crash Course)

SwiftUI is written in Swift. You don’t need to be a Swift expert to start, but you need to know the basic building blocks.

Variables and Constants

  • var: Something that can change (Variable).
  • let: Something that never changes (Constant). Use this whenever possible for safety.
var name = "John"
name = "Peter" // ✅ Valid

let birthYear = "1990"
// birthYear = "2000" // ❌ Error: You cannot change a constant

Structs

SwiftUI relies almost entirely on structs. Imagine a structure as a blueprint for creating something.

struct Dog {
    var name: String
    var breed: String
}

let myDog = Dog(name: "Buddy", breed: "Labrador")

In SwiftUI, all your screens (Views) are structs. They are lightweight, fast, and secure.


Module 3: Your First View in SwiftUI

Look at the ContentView.swift file. You will see something like this:

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
        }
        .padding()
    }
}

Let’s dissect this line by line. This is crucial.

  1. struct ContentView: View: You are creating a structure named ContentView that behaves like a View. The View protocol is the contract: it promises that this structure knows how to draw itself on the screen.
  2. var body: some View: This is the only mandatory property. SwiftUI asks you: “What is the body of this view?”. You must return something that is a view.
  3. Text("Hello, world!"): This is a basic component. It displays text.

Modifiers: The SwiftUI “Brush”

Do you see .padding() or .foregroundStyle(.tint)? These are called Modifiers. They take a view (like the Text), apply a change to it, and return a new modified view.

Text("Hello, SwiftUI")
    .font(.largeTitle) // Makes the font big
    .fontWeight(.bold) // Makes it bold
    .foregroundColor(.blue) // Paints it blue
    .padding() // Adds air around it
    .background(Color.yellow) // Puts a yellow background

⚠️ Order Matters: Imagine you are a painter.

  • If you paint the background yellow and then add a margin (padding), the margin will be transparent.
  • If you add a margin and then paint the background, the margin will also be yellow.

Module 4: The Layout System (Stacks)

In classic design, we used coordinates (X, Y). In SwiftUI, we use Boxes. There are three main types of boxes for organizing elements:

  1. VStack (Vertical Stack): Stacks elements one below the other.
  2. HStack (Horizontal Stack): Places elements side by side.
  3. ZStack (Depth Stack): Places elements on top of each other (stacking towards the viewer, like Photoshop layers).

Practical Example: Profile Card

Let’s create a simple user card.

struct ProfileCard: View {
    var body: some View {
        HStack { // Horizontal Box
            Image(systemName: "person.circle.fill") // Icon
                .font(.system(size: 60))
                .foregroundColor(.blue)
            
            VStack(alignment: .leading) { // Vertical Box (left aligned)
                Text("Gemini AI")
                    .font(.headline)
                Text("iOS Developer")
                    .font(.subheadline)
                    .foregroundColor(.gray)
            }
        }
        .padding() // Internal margin
        .background(Color.white) // White background
        .cornerRadius(10) // Rounded corners
        .shadow(radius: 5) // Drop shadow
    }
}

Copy and paste this into your Xcode. You will see how Stacks create structure without the need for complex math.

Spacers

The Spacer() is an invisible spring. It occupies all possible space, pushing other elements aside.

  • HStack { Text("A") Spacer() Text("B") }: “A” on the left, “B” on the right, empty space in the middle.

Module 5: State Management (The Heart of the App)

This is where beginners often get stuck. Read carefully. An app is not static; it changes. The user taps buttons, types text, downloads data. In SwiftUI, the View is a function of its State.

UI = f(State)

If the state changes, the UI redraws automatically. To tell SwiftUI that a variable is “special” and should be watched, we use Property Wrappers.

@State (Local State)

Use @State for simple variables that belong only to that view (like if a button is pressed or what text is in a field).

struct CounterView: View {
    // 1. Declare the state.
    // SwiftUI now "observes" this variable.
    @State private var count = 0
    
    var body: some View {
        VStack {
            Text("You tapped: \(count) times")
                .font(.title)
            
            Button("Tap me") {
                // 2. Change the state.
                // Automatically, SwiftUI destroys the view and recreates it with the new value.
                count += 1
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

@Binding (The Connection)

What happens if you have a “Parent” view and a “Child” view, and you want the child to change data in the parent? You can’t pass the normal variable, because in Swift structures are passed by copy (value). You need a “link” or reference.

  • Parent: Owns the @State.
  • Child: Has the @Binding.
struct SwitchView: View {
    @State private var isOn = false // The absolute truth lives here
    
    var body: some View {
        VStack {
            // We pass the variable with '$' to create the binding
            SwitchButton(isOn: $isOn)
            
            if isOn {
                Text("Lights on!")
            }
        }
    }
}

struct SwitchButton: View {
    // Binding has no value of its own, it points to the parent's @State
    @Binding var isOn: Bool 
    
    var body: some View {
        Button(isOn ? "Turn Off" : "Turn On") {
            isOn.toggle() // This changes the variable in the PARENT
        }
    }
}

Module 6: Lists and Dynamic Data

Real apps show lists of things. In UIKit we used UITableView (which was painful). In SwiftUI we use List.

To show data in a list, the data must be Identifiable (it must have a unique ID).

// 1. Define the data model
struct TaskItem: Identifiable {
    let id = UUID() // Automatically generates a unique ID
    let name: String
}

struct TaskListView: View {
    // 2. Our data
    let tasks = [
        TaskItem(name: "Learn Swift"),
        TaskItem(name: "Exercise"),
        TaskItem(name: "Read 5000 words")
    ]
    
    var body: some View {
        // 3. The List
        List(tasks) { task in
            // How EACH row looks
            HStack {
                Image(systemName: "circle")
                Text(task.name)
            }
        }
    }
}

t’s that simple. SwiftUI iterates through the array and creates a row for each element.


Module 7: Navigation

How do we move from one screen to another? We use NavigationStack and NavigationLink.

Think of it like a deck of cards.

  1. NavigationStack: The table where you place the cards. It wraps your main view.
  2. NavigationLink: The action of placing a new card on top.
struct MenuView: View {
    var body: some View {
        NavigationStack {
            List {
                NavigationLink("View Profile") {
                    // Destination
                    ProfileCard()
                }
                
                NavigationLink("View Settings") {
                    Text("Settings Screen")
                }
            }
            .navigationTitle("Main Menu")
        }
    }
}

Module 8: Practical Tutorial – “ColorMinder”

Let’s combine everything learned to create a mini-app. Idea: An app that lets you save a list of favorite colors and view their details.

Step 1: The Model

Create a file named ColorItem.swift.

import SwiftUI

struct ColorItem: Identifiable {
    let id = UUID()
    let name: String
    let color: Color
    let description: String
}

Step 2: The Detail View

Create ColorDetailView.swift. This view will receive a color and display it largely.

import SwiftUI

struct ColorDetailView: View {
    let item: ColorItem // Receives data, doesn't need state
    
    var body: some View {
        ZStack {
            item.color.ignoresSafeArea() // Full screen background
            
            VStack {
                Text(item.name)
                    .font(.system(size: 50, weight: .bold))
                    .foregroundStyle(.white)
                
                Text(item.description)
                    .font(.title2)
                    .foregroundStyle(.white.opacity(0.8))
                    .padding()
            }
        }
    }
}

Step 3: The Main List (ViewModel + View)

In modern applications, we use the Observation framework to separate logic. Create ColorViewModel.swift.

import Foundation
import SwiftUI
import Observation

@Observable
class ColorViewModel {
    var items: [ColorItem] = [
        ColorItem(name: "Ocean Blue", color: .blue, description: "Calming and deep."),
        ColorItem(name: "Fire Red", color: .red, description: "Passion and energy."),
        ColorItem(name: "Forest Green", color: .green, description: "Nature and life."),
        ColorItem(name: "Sun Yellow", color: .yellow, description: "Joy and light.")
    ]
    
    func addItem() {
        // Simulation of adding a random one
        items.append(ColorItem(name: "New Purple", color: .purple, description: "Mysterious."))
    }
}

And finally, your ContentView.swift:

struct ContentView: View {
    // Instantiate the ViewModel
    @State private var viewModel = ColorViewModel()
    
    var body: some View {
        NavigationStack {
            List(viewModel.items) { item in
                NavigationLink(destination: ColorDetailView(item: item)) {
                    HStack {
                        Circle()
                            .fill(item.color)
                            .frame(width: 30, height: 30)
                        Text(item.name)
                            .font(.headline)
                    }
                }
            }
            .navigationTitle("My Colors")
            .toolbar {
                Button("Add", systemImage: "plus") {
                    viewModel.addItem()
                }
            }
        }
    }
}

Module 9: Architecture and Best Practices (Intermediate Level)

Now that you know how to create the app, let’s talk about how not to create a code disaster (“Spaghetti Code”).

MVVM (Model – View – ViewModel)

This is the pattern we just used in “ColorMinder”.

  • Model: Your pure data (struct ColorItem). It knows nothing about the screen.
  • View: Your screen (ContentView). It only knows how to paint. It should not have complex logic (like internet calls).
  • ViewModel: The brain (class ColorViewModel). It holds the data, makes database or API calls, and tells the view “Hey, I changed, redraw yourself!”.

Separating Views

A common beginner mistake is putting all the code in a single file. If your body has more than 50 lines, it’s time to extract sub-views.

Bad:

var body: some View {
   VStack {
      // 50 lines of code for a card
      // 50 lines of code for a button
   }
}

Good:

var body: some View {
   VStack {
      CardView()
      CustomButtonView()
   }
}

Select the code in Xcode, right-click -> “Refactor” -> “Extract Subview”. Xcode does it for you.


Module 10: Resources and Next Steps

You have reached the end of this massive tutorial. You now have the fundamentals: Variables, Views, Stacks, Modifiers, State, Lists, and Navigation.

What’s next?

  1. SwiftData: To save data permanently on the device (database).
  2. API Networking: Using URLSession to download data from the internet (JSON).
  3. Animations: SwiftUI has the easiest animation system in the world. Try adding .animation(.default, value: variable) to a view.

Common Mistakes to Avoid

  • Fighting the system: If something seems incredibly difficult in SwiftUI (like manually changing the color of a specific pixel), you are probably doing it wrong. Search for the “SwiftUI way”.
  • Mixing logic and UI: Keep your calculations outside the body.
  • Ignoring Previews: Use the Canvas. It saves you hours of compiling and running the simulator.

Farewell

Learning to code is a marathon, not a sprint. There will be days when the code doesn’t compile and you want to throw your Mac out the window. This is normal. Breathe, read the error (Xcode usually tells you what’s wrong).

Welcome to the Apple developer community. You are building the future.

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Article

Exploring the Foundation Models framework with SwiftUI

Next Article

How to use Liquid Glass in SwiftUI apps

Related Posts