Using SwiftUI to Build a Workout Tracking App

SwiftUI, Apple’s modern UI framework, simplifies the process of building interactive user interfaces across all Apple platforms. With its declarative syntax and live preview feature, SwiftUI makes it easier than ever to create stunning and functional apps. This blog post will guide you through the process of building a workout tracking app using SwiftUI, showcasing some of its key features and best practices.

What is SwiftUI?

SwiftUI is a declarative UI framework developed by Apple. It allows developers to define user interfaces using Swift code in a more readable and concise manner. Key features of SwiftUI include:

  • Declarative Syntax: Describe what the UI should look like based on the current state.
  • Live Preview: See real-time changes as you code.
  • Cross-Platform Compatibility: Build UIs that work across iOS, macOS, watchOS, and tvOS.
  • Integration with Combine: Utilize reactive programming for managing data flow.

Why Use SwiftUI for a Workout Tracking App?

SwiftUI is particularly well-suited for building a workout tracking app due to its ability to handle dynamic data and user interactions efficiently. Some benefits include:

  • Rapid Development: Create UI components quickly with live previews.
  • Adaptive Layouts: Easily create layouts that adapt to different screen sizes.
  • Data Binding: Simplify data management and UI updates with @State, @Binding, and @ObservedObject.

How to Build a Workout Tracking App in SwiftUI

Let’s dive into building a workout tracking app using SwiftUI.

Step 1: Set Up a New Xcode Project

Open Xcode and create a new project. Select the ‘App’ template under the iOS tab, choose a name for your project (e.g., “WorkoutTracker”), select SwiftUI as the interface, and click ‘Create’.

Step 2: Create the Data Model

Define the data structure for workouts and exercises. Create a new Swift file named Workout.swift and add the following code:


import Foundation

struct Exercise: Identifiable {
    let id = UUID()
    var name: String
    var sets: Int
    var reps: Int
    var weight: Double
}

struct Workout: Identifiable {
    let id = UUID()
    var name: String
    var date: Date
    var exercises: [Exercise]
}

Step 3: Implement the Workout List View

Create a view to display the list of workouts. Open ContentView.swift and replace its content with the following code:


import SwiftUI

struct ContentView: View {
    @State private var workouts: [Workout] = [
        Workout(name: "Chest Day", date: Date(), exercises: [
            Exercise(name: "Bench Press", sets: 3, reps: 8, weight: 100.0),
            Exercise(name: "Incline Dumbbell Press", sets: 3, reps: 10, weight: 30.0)
        ]),
        Workout(name: "Leg Day", date: Date(), exercises: [
            Exercise(name: "Squats", sets: 3, reps: 8, weight: 120.0),
            Exercise(name: "Leg Press", sets: 3, reps: 10, weight: 200.0)
        ])
    ]

    var body: some View {
        NavigationView {
            List {
                ForEach(workouts) { workout in
                    NavigationLink(destination: WorkoutDetailView(workout: workout)) {
                        VStack(alignment: .leading) {
                            Text(workout.name)
                                .font(.headline)
                            Text(workout.date, style: .date)
                                .font(.subheadline)
                                .foregroundColor(.gray)
                        }
                    }
                }
            }
            .navigationTitle("Workouts")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

In this code:

  • A ContentView struct is defined to display a list of workouts.
  • @State is used to manage the array of workouts.
  • A NavigationView provides navigation functionality.
  • List and ForEach are used to display the workout list.
  • NavigationLink is used to navigate to a detail view for each workout.

Step 4: Create the Workout Detail View

Create a new SwiftUI view to display the details of a selected workout. Create a new Swift file named WorkoutDetailView.swift and add the following code:


import SwiftUI

struct WorkoutDetailView: View {
    let workout: Workout

    var body: some View {
        VStack {
            Text(workout.name)
                .font(.largeTitle)
                .padding()

            Text(workout.date, style: .date)
                .font(.headline)
                .foregroundColor(.gray)
                .padding(.bottom)

            List {
                ForEach(workout.exercises) { exercise in
                    VStack(alignment: .leading) {
                        Text(exercise.name)
                            .font(.headline)
                        Text("Sets: \(exercise.sets), Reps: \(exercise.reps), Weight: \(exercise.weight, specifier: "%.1f") kg")
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                    }
                }
            }
        }
        .padding()
        .navigationTitle("Workout Details")
    }
}

struct WorkoutDetailView_Previews: PreviewProvider {
    static var previews: some View {
        WorkoutDetailView(workout: Workout(name: "Test Workout", date: Date(), exercises: [
            Exercise(name: "Test Exercise", sets: 3, reps: 10, weight: 50.0)
        ]))
    }
}

This view:

  • Displays the details of a selected workout, including its name, date, and list of exercises.
  • Uses a List and ForEach to display the exercises.
  • Formats exercise details such as sets, reps, and weight.

Step 5: Add Workout Creation Feature

Implement a feature to add new workouts to the app. First, create a new SwiftUI view for adding workouts named AddWorkoutView.swift:


import SwiftUI

struct AddWorkoutView: View {
    @Environment(\\.presentationMode) var presentationMode
    @State private var workoutName: String = ""
    @State private var workoutDate: Date = Date()

    @Binding var workouts: [Workout]

    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Workout Details")) {
                    TextField("Workout Name", text: $workoutName)
                    DatePicker("Date", selection: $workoutDate, displayedComponents: .date)
                }

                Section {
                    Button("Add Workout") {
                        let newWorkout = Workout(name: workoutName, date: workoutDate, exercises: [])
                        workouts.append(newWorkout)
                        presentationMode.wrappedValue.dismiss()
                    }
                }
            }
            .navigationTitle("Add Workout")
            .navigationBarItems(trailing: Button("Cancel") {
                presentationMode.wrappedValue.dismiss()
            })
        }
    }
}

Next, modify ContentView.swift to include a button to present AddWorkoutView as a sheet:


import SwiftUI

struct ContentView: View {
    @State private var workouts: [Workout] = [
        // Existing workout data
    ]

    @State private var isPresentingAddWorkoutView: Bool = false

    var body: some View {
        NavigationView {
            List {
                ForEach(workouts) { workout in
                    NavigationLink(destination: WorkoutDetailView(workout: workout)) {
                        VStack(alignment: .leading) {
                            Text(workout.name)
                                .font(.headline)
                            Text(workout.date, style: .date)
                                .font(.subheadline)
                                .foregroundColor(.gray)
                        }
                    }
                }
            }
            .navigationTitle("Workouts")
            .navigationBarItems(trailing: Button(action: {
                isPresentingAddWorkoutView = true
            }) {
                Image(systemName: "plus")
            })
            .sheet(isPresented: $isPresentingAddWorkoutView) {
                AddWorkoutView(workouts: $workouts)
            }
        }
    }
}

Step 6: Add Exercise Details to Workout

Let’s improve our app to include an ability to add specific exercises to each workout. Begin by modifying WorkoutDetailView to include a way to add exercises:


import SwiftUI

struct WorkoutDetailView: View {
    @State var workout: Workout
    @State private var isPresentingAddExerciseView: Bool = false

    var body: some View {
        VStack {
            Text(workout.name)
                .font(.largeTitle)
                .padding()

            Text(workout.date, style: .date)
                .font(.headline)
                .foregroundColor(.gray)
                .padding(.bottom)

            List {
                ForEach(workout.exercises) { exercise in
                    VStack(alignment: .leading) {
                        Text(exercise.name)
                            .font(.headline)
                        Text("Sets: \(exercise.sets), Reps: \(exercise.reps), Weight: \(exercise.weight, specifier: "%.1f") kg")
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                    }
                }
            }
        }
        .padding()
        .navigationTitle("Workout Details")
        .navigationBarItems(trailing: Button(action: {
            isPresentingAddExerciseView = true
        }) {
            Image(systemName: "plus")
        })
        .sheet(isPresented: $isPresentingAddExerciseView) {
            AddExerciseView(workout: $workout)
        }
    }
}

Then, create AddExerciseView.swift with a form to input new exercise details:


import SwiftUI

struct AddExerciseView: View {
    @Environment(\\.presentationMode) var presentationMode
    @Binding var workout: Workout
    @State private var exerciseName: String = ""
    @State private var sets: Int = 3
    @State private var reps: Int = 10
    @State private var weight: Double = 0.0

    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Exercise Details")) {
                    TextField("Exercise Name", text: $exerciseName)
                    Stepper("Sets: \(sets)", value: $sets, in: 1...10)
                    Stepper("Reps: \(reps)", value: $reps, in: 1...20)
                    TextField("Weight (kg)", value: Binding<String>(
                        get: { String(format: "%.1f", weight) },
                        set: { newValue in
                            if let value = Double(newValue) {
                                weight = value
                            }
                        }
                    ))
                }

                Section {
                    Button("Add Exercise") {
                        let newExercise = Exercise(name: exerciseName, sets: sets, reps: reps, weight: weight)
                        workout.exercises.append(newExercise)
                        presentationMode.wrappedValue.dismiss()
                    }
                }
            }
            .navigationTitle("Add Exercise")
            .navigationBarItems(trailing: Button("Cancel") {
                presentationMode.wrappedValue.dismiss()
            })
        }
    }
}

Advanced Features to Consider

  • Data Persistence: Use Core Data, Realm, or UserDefaults to persist workout data across app sessions.
  • Cloud Sync: Implement iCloud or other cloud services for syncing workouts across devices.
  • Charts and Statistics: Integrate charts and graphs to visualize workout progress.
  • User Authentication: Add user authentication to allow multiple users.
  • Apple Watch Integration: Extend the app to Apple Watch for real-time tracking.

Conclusion

SwiftUI offers a powerful and intuitive way to build a workout tracking app. By leveraging its declarative syntax, data binding features, and live preview, you can rapidly develop an engaging and functional app for workout enthusiasts. This guide provides a solid foundation for building your workout tracking app, and you can continue to enhance it with advanced features to meet your users’ needs. With SwiftUI, the possibilities are endless!