Build With Me · Learn

Learn to build, level by level.

Nine levels, from basic vocabulary to running a product. Each level points to what really exists to learn it: Build School for the web, official documentation and this site’s guides for Apple and AI.

The levels

From zero to a running product.

Measured AI features and automations.

  1. 0

    Computer and development fundamentals

    Files and folders, the terminal, what a server, a database and an API are — the vocabulary that keeps you from getting lost.

  2. 1

    Programming foundations

    Variables, functions, conditions, collections, types — in your platform’s language.

  3. 2

    Git, GitHub and development environments

    Version every step, go back, work with a coding agent without losing control.

  4. 3

    Platform-specific development

    Screens, state, navigation and your platform’s conventions.

  5. 4

    Databases and backend engineering

    Modelling data, authentication, authorization and integrations.

  6. 5

    Deployment and distribution

    Going live, distributing a beta, submitting to a store.

  7. 6

    Product architecture

    Where rules, data and responsibilities live — and what makes a change safe.

  8. 7

    Production software engineering

    Tests, security, error monitoring, proven launch readiness.

  9. 8

    Operating and growing a digital product

    Analytics, search visibility, user feedback, release rhythm.

    • PlannedA Bynilson guide to operating an AI product

Formats

Written first, never audio only.

  • Step-by-step written guides
  • Checklists to tick
  • Verified code samples
  • Captioned screen recordings (in Build School)
  • Practice projects
  • No narration-only lessons

Starters

Starting points that really exist.

A starter is marked available only when its files and instructions exist. The others are planned, and labelled as such.

SwiftUI basic app (iOS)A task list: an @Observable model shared through the environment, add, check, delete, empty state.Available · iOS 17+
Requirements
A Mac with Xcode 26 or later.
Supported versions
iOS 17+
Environment variables
None.
Security
No data leaves the device and nothing is saved: tasks disappear when the app closes. Add SwiftData to keep them.
Licence
MIT
Maintenance
Reviewed with every major Xcode release.

Setup

  1. In Xcode: File → New → Project → iOS → App. Interface: SwiftUI. Any name.
  2. Set the deployment target (Minimum Deployments) to iOS 17 or later.
  3. Replace the whole content of <Name>App.swift with the code below, and delete ContentView.swift (the starter contains it).
  4. Run with ⌘R on a simulator, then on your iPhone (a free Apple Account is enough to test on your own device).
StarterApp.swift
import SwiftUI
import Observation

@main
struct StarterApp: App {
    @State private var store = TaskStore()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(store)
        }
    }
}

struct TaskItem: Identifiable, Hashable {
    let id = UUID()
    var title: String
    var isDone = false
}

@Observable
final class TaskStore {
    var items: [TaskItem] = []

    func add(_ title: String) {
        let trimmed = title.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmed.isEmpty else { return }
        items.append(TaskItem(title: trimmed))
    }

    func toggle(_ item: TaskItem) {
        guard let index = items.firstIndex(where: { $0.id == item.id }) else { return }
        items[index].isDone.toggle()
    }

    func delete(at offsets: IndexSet) {
        items.remove(atOffsets: offsets)
    }
}

struct ContentView: View {
    @Environment(TaskStore.self) private var store
    @State private var draft = ""

    var body: some View {
        NavigationStack {
            List {
                Section {
                    HStack {
                        TextField("New task", text: $draft)
                            .onSubmit(addDraft)
                        Button("Add", action: addDraft)
                            .disabled(draft.trimmingCharacters(in: .whitespaces).isEmpty)
                    }
                }
                Section {
                    if store.items.isEmpty {
                        ContentUnavailableView("No tasks yet", systemImage: "checklist", description: Text("Add your first task above."))
                    }
                    ForEach(store.items) { item in
                        Button {
                            store.toggle(item)
                        } label: {
                            Label(item.title, systemImage: item.isDone ? "checkmark.circle.fill" : "circle")
                        }
                        .accessibilityValue(item.isDone ? "Done" : "Not done")
                    }
                    .onDelete(perform: store.delete)
                }
            }
            .navigationTitle("Tasks")
        }
    }

    private func addDraft() {
        store.add(draft)
        draft = ""
    }
}

#Preview {
    ContentView()
        .environment(TaskStore())
}

Typechecked: Xcode 27.1 (iOS SDK 27.1, macOS SDK 27.0), Swift 5 and Swift 6 modes — 2026-10-23

SwiftUI app with navigation (iOS)A list and a detail view with NavigationStack: value-based links, typed routes, search, toolbar.Available · iOS 17+
Requirements
A Mac with Xcode 26 or later.
Supported versions
iOS 17+
Environment variables
None.
Security
Sample data written in the code, no network. Replace it with yours.
Licence
MIT
Maintenance
Reviewed with every major Xcode release.

Setup

  1. In Xcode: File → New → Project → iOS → App. Interface: SwiftUI. Any name.
  2. Set the deployment target (Minimum Deployments) to iOS 17 or later.
  3. Replace the whole content of <Name>App.swift with the code below, and delete ContentView.swift (the starter contains it).
  4. Run with ⌘R on a simulator, then on your iPhone (a free Apple Account is enough to test on your own device).
NavigationStarterApp.swift
import SwiftUI

@main
struct NavigationStarterApp: App {
    var body: some Scene {
        WindowGroup {
            PlaceList()
        }
    }
}

struct Place: Identifiable, Hashable {
    let id = UUID()
    let name: String
    let canton: String
    let note: String
}

enum Route: Hashable {
    case place(Place)
    case about
}

struct PlaceList: View {
    // Sample data, written here for the starter — replace with your own.
    private let places = [
        Place(name: "Lausanne", canton: "Vaud", note: "Sample entry."),
        Place(name: "Zürich", canton: "Zürich", note: "Sample entry."),
        Place(name: "Lugano", canton: "Ticino", note: "Sample entry."),
    ]
    @State private var path: [Route] = []
    @State private var query = ""

    private var filtered: [Place] {
        query.isEmpty ? places : places.filter { $0.name.localizedCaseInsensitiveContains(query) }
    }

    var body: some View {
        NavigationStack(path: $path) {
            List(filtered) { place in
                NavigationLink(value: Route.place(place)) {
                    VStack(alignment: .leading) {
                        Text(place.name).font(.headline)
                        Text(place.canton).font(.subheadline).foregroundStyle(.secondary)
                    }
                }
            }
            .searchable(text: $query)
            .navigationTitle("Places")
            .toolbar {
                Button("About") { path.append(.about) }
            }
            .navigationDestination(for: Route.self) { route in
                switch route {
                case .place(let place):
                    PlaceDetail(place: place)
                case .about:
                    Text("A NavigationStack starter: value-based links and a typed route.")
                        .padding()
                        .navigationTitle("About")
                }
            }
        }
    }
}

struct PlaceDetail: View {
    let place: Place

    var body: some View {
        Form {
            LabeledContent("Canton", value: place.canton)
            Section("Note") { Text(place.note) }
        }
        .navigationTitle(place.name)
    }
}

Typechecked: Xcode 27.1 (iOS SDK 27.1, macOS SDK 27.0), Swift 5 and Swift 6 modes — 2026-10-23

macOS menu bar utilityA timer in the menu bar: MenuBarExtra, a Settings window, keyboard shortcuts.Available · macOS 14+
Requirements
A Mac with Xcode 26 or later.
Supported versions
macOS 14+
Environment variables
None.
Security
No network or file access. The Xcode template keeps the App Sandbox on: leave it on.
Licence
MIT
Maintenance
Reviewed with every major Xcode release.

Setup

  1. In Xcode: File → New → Project → macOS → App. Interface: SwiftUI.
  2. Set the deployment target to macOS 14 or later.
  3. Replace the content of <Name>App.swift with the code below and delete ContentView.swift.
  4. To hide the Dock icon, add the LSUIElement key (Application is agent) to the target’s Info.plist.
  5. Run with ⌘R: the icon appears in the menu bar.
MenuBarStarterApp.swift
import SwiftUI
import Observation

@main
struct MenuBarStarterApp: App {
    @State private var timer = FocusTimer()

    var body: some Scene {
        MenuBarExtra {
            TimerMenu()
                .environment(timer)
        } label: {
            Label(timer.label, systemImage: timer.isRunning ? "timer" : "timer.circle")
        }
        .menuBarExtraStyle(.window)

        Settings {
            SettingsView()
                .environment(timer)
        }
    }
}

@Observable
final class FocusTimer {
    var minutes = 25
    private(set) var endDate: Date?

    var isRunning: Bool { endDate != nil }

    var label: String {
        guard let endDate else { return "Focus" }
        let remaining = max(0, Int(endDate.timeIntervalSinceNow / 60))
        return "\(remaining) min"
    }

    func start() { endDate = Date().addingTimeInterval(TimeInterval(minutes * 60)) }
    func stop() { endDate = nil }
}

struct TimerMenu: View {
    @Environment(FocusTimer.self) private var timer
    @Environment(\.openSettings) private var openSettings

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text(timer.isRunning ? "Session running" : "Ready for \(timer.minutes) minutes")
                .font(.headline)
            HStack {
                Button(timer.isRunning ? "Stop" : "Start") {
                    timer.isRunning ? timer.stop() : timer.start()
                }
                .keyboardShortcut(.defaultAction)
                Button("Settings…") { openSettings() }
            }
            Divider()
            Button("Quit") { NSApplication.shared.terminate(nil) }
                .keyboardShortcut("q")
        }
        .padding()
        .frame(width: 240)
    }
}

struct SettingsView: View {
    @Environment(FocusTimer.self) private var timer

    var body: some View {
        @Bindable var timer = timer
        Form {
            Stepper("Session length: \(timer.minutes) min", value: $timer.minutes, in: 5...90, step: 5)
        }
        .padding()
        .frame(width: 320)
    }
}

Typechecked: Xcode 27.1 (iOS SDK 27.1, macOS SDK 27.0), Swift 5 and Swift 6 modes — 2026-10-23

Web reference project “Coût Réel” (Build School)Build School’s reference project: a published Next.js calculator, with a Stripe payment in test mode and a single Supabase table.In Build School · Web
iOS app with API integrationURLSession, Codable, loading and error states.Planned
macOS windowed applicationWindows, Commands menus, documents.Planned
Web landing pageA fast, indexable server-rendered page.Planned
Authenticated SaaS skeletonAccounts, per-user data, tested authorization.Planned
Full-stack dashboardLists, filters, export.Planned
Stripe payment integration exampleCheckout, verified webhook, idempotency.Planned
AI assistant starterServer call, structured output, evaluations.Planned
API starterOpenAPI contract, authentication, rate limits.Planned

Ready for the next step?

Describe your project in a few answers: you get an explained plan and a roadmap, with no account and no commitment.

danilson.daveigaramos.pro@gmail.com