I wanted to add the counter on how many times the activity was completed. But the button I created in ActivityView does not work because of the error "cannot use mutating member on immutable value: self is immutable"
Why is my Activity immutable? How should I increment the counter?
Activity
import Foundation
struct Activity: Codable, Identifiable {
var id = UUID() // swift will create ids automatically
let title: String
let description: String
var completedCount = 0
mutating func completed() {
self.completedCount += 1
}
}
Activities
import Foundation
class Activities: ObservableObject {
@Published var items = [Activity]() {
// Whenever you set this property it will save the data to disk in UserDefaults
didSet {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(items) {
UserDefaults.standard.set(encoded, forKey: "activities")
}
}
}
init() {
// Try to read saved data at init-time, if there is no data then create an empty array
if let savedActivities = UserDefaults.standard.data(forKey: "activities") {
if let decoded = try? JSONDecoder().decode([Activity].self, from: savedActivities) {
items = decoded
return
}
}
items = [Activity]()
}
static var previewData: Activities {
let sample = Activities()
sample.items.append(Activity(title: "Outdoor Cycling", description: "Cycling outside in the nature."))
return sample
}
}
ActivityView
import SwiftUI
struct ActivityView: View {
var activity: Activity
var body: some View {
GeometryReader { geometry in
ScrollView {
VStack(alignment: .leading) {
Text("This activity has been completed: \(activity.completedCount) times")
.padding(.top, 15)
Button {
activity.completed() // HERE is the error
} label: {
Image(systemName: "plus")
}
Text(activity.description)
.padding(.top, 10)
}
.padding([.horizontal])
}
}
.navigationTitle(activity.title)
}
}
struct ActivityView_Previews: PreviewProvider {
static var previews: some View {
ActivityView(activity: Activities.previewData.items.popLast()!)
}
}
ContentView
import SwiftUI
struct ContentView: View {
@StateObject var activities = Activities()
@State private var showingAddActivity = false
@State private var dummies = ["Outdoor Cycle", "Walk", "Run"]
var body: some View {
NavigationView {
List {
Section {
ForEach(activities.items, id: \.id) { activity in
NavigationLink {
ActivityView(activity: activity)
} label: {
Text(activity.title)
}
}
.onDelete(perform: removeActivityItem(at:))
} header: {
Text("My Activities")
}
}
.navigationTitle("Time Tracker")
.toolbar {
Button {
showingAddActivity = true
} label: {
Image(systemName: "plus")
}
}
.sheet(isPresented: $showingAddActivity) {
AddActivityView(activities: activities)
}
}
}
func removeActivityItem(at offsets: IndexSet) {
activities.items.remove(atOffsets: offsets)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}