UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Milestone: Projects 7-9, issue updating value

Forums > 100 Days of SwiftUI

Hello!

I have an issue in the HabitsTracker project app when it comes to update the counter for the habit in its detail view. The structure is as follows:

  • I have an Activities class which conforms to ObservableObject which, in turn, has a @Published var items = [Activity] that is read/written from/to UserDefaults
  • My list view has an @ObservedObject var activities: Activities which is passed from the App struct
  • When it navigates to the detail view, I pass both activities and the selected activity
  • My details view has:
    • @ObservedObject var activities: Activities
    • @State var activity: Activity
    • A button that calls the following method
private func increment() {
    guard let index = activities.items.firstIndex(of: activity) else { return }

    activity.numberOfTimes += 1
    activities.items[index].numberOfTimes = activity.numberOfTimes
}

The problem is:

  • If I press the button once, the numberOfTimes increments by one, its corresponding label shows the updated value and, if I go back to the list view, I can see that the value is also updated correctly. But, if I press it a second time, numberOfTimes is no longer incremented, and the value doesn't change no matter how many times I call the increment method.

Maybe I'm missing something, but I can't understand what's going on. I followed the example in the iExpense app, to no avail.

Can you help me out?

2      

hi,

i have removed my initial response(s) and i'll defer to Patrick's explanation below; i was confused, thinking that activity was a (class) object; apparently it's a struct. i should have recognized that. my apologies for the confusion (which was mine alone).

regards,

DMG

2      

Instead of passing both the Activities class and the individual Activity, you should be passing in a Binding to the Activity so that you can update it in your detail view.

Here's a very simplified example to demonstrate:

import SwiftUI

struct Activity: Identifiable {
    let id = UUID()
    let name: String
    var numberOfTimes: Int = 0
}

class Activities: ObservableObject {
    @Published var activities: [Activity]

    init() {
        activities = [
            Activity(name: "Jogging"),
            Activity(name: "Reading"),
            Activity(name: "Petting the cat"),
        ]
    }
}

struct ContentView: View {
    @StateObject var activities = Activities()

    var body: some View {
        ActivitiesListView(activities: activities)
    }
}

struct ActivitiesListView: View {
    @ObservedObject var activities: Activities

    var body: some View {
        NavigationView {
            List {
                ForEach($activities.activities) { $activity in
                    NavigationLink(destination: ActivityDetailView(activity: $activity)) {
                        HStack {
                            Text(activity.name)
                            Spacer()
                            Text("\(activity.numberOfTimes)")
                        }
                    }
                }
            }
        }
    }
}

struct ActivityDetailView: View {
    @Binding var activity: Activity

    var body: some View {
        VStack(spacing: 40) {
            Text(activity.name)
                .font(.title)
            Text("\(activity.numberOfTimes)")
                .font(.subheadline)
            Button("Increment") {
                activity.numberOfTimes += 1
            }
        }
    }
}

This syntax is available starting in Swift 5.5 and iOS15 but it's backwards compatible to iOS13 as long as you are compiling with Xcode 13.

activities incrementing

3      

@delawaremathguy tried your method (to convert Activity to Class, didn't make it clear it was a Struct), but @roosterboy's approach is exactly what I was looking for.

Thank you both for the help!

2      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.