BLACK FRIDAY SALE: Save big on all my Swift books and bundles! >>

SOLVED: How do I access and change Data from another View

Forums > SwiftUI

I want to change "Completed" in UpcomingExams.swift View to true in values from Design.swift View. UpcomingExamView.swift

import SwiftUI
struct UpcomingExamsView: View {
    struct Data: Identifiable {
        let id = UUID()
        var name: String
        var completed: Bool
    }

    // Data that I want to Change from Design.swift View

    @State private var values: [Data] = [
        Data(name: "John", completed: false),
        Data(name: "Katherine", completed: false),
        Data(name: "Jonas", completed: false),
    ]

    func addNewData() {
        values.append(Data(name: examName, completed: false))
    }
    @State var examName: String = ""
    @FocusState var keyboardFocused: Bool
    @State var DisclosureGroupOpen = false
    @State var addNewExam = false
    @Environment(\.colorScheme) var colorScheme
    var body: some View {
        ZStack {
            ScrollView {
                VStack {
                    ForEach(values){ item in
                        DesignView(name: item.name, completed: item.completed)
                    }
                    DisclosureGroup(isExpanded: $DisclosureGroupOpen) {
                        ScrollView {
                            VStack {
                                ForEach(values){ item in
                                    DesignView(name: item.name, completed: item.completed)
                                }
                            }
                        }
                    }
                label: {
                    RoundedRectangle(cornerRadius: 8)
                        .frame(width: 120, height: 20)
                        .foregroundColor(colorScheme == .dark ? Color(.systemGray6) : Color.gray.opacity(0.6))
                        .padding(.horizontal)
                        .overlay(
                            HStack {
                                if DisclosureGroupOpen == false {
                                    Image(systemName: "chevron.right")
                                        .foregroundColor(.white)
                                } else {
                                    Image(systemName: "chevron.down")
                                        .foregroundColor(.white)
                                }
                                Text("Completed")
                                    .foregroundColor(.white)
                            })
                }.accentColor(.clear)
                }
            }
            .navigationTitle("Upcoming Exams")
            VStack {
                Spacer()
                if addNewExam {
                    Rectangle()
                        .frame(maxWidth: .infinity, maxHeight: 100)
                        .foregroundColor(Color(.systemBackground))
                        .overlay(VStack{
                            TextField ("Add an Exam", text: $examName)
                                .focused($keyboardFocused)
                                .padding()
                                .onAppear{keyboardFocused = true}
                                .onSubmit {
                                    addNewData()
                                    addNewExam = false
                                    examName = ""
                                }
                            Spacer()
                        })
                }

                if keyboardFocused == false {
                    Rectangle()
                        .onAppear {
                            addNewExam = false
                        }
                        .onTapGesture {
                            addNewExam = true
                        }
                        .frame(maxWidth: .infinity, maxHeight: 50)
                        .foregroundColor(Color(.systemBackground))
                        .overlay(
                            HStack {
                                Image(systemName: "plus")
                                    .resizable()
                                    .frame(width: 20, height: 20)
                                Text("Add an Exam")
                            }) 
                }
            }
        }
    }
}

Design.swift

import SwiftUI

struct DesignView: View {
    @State var name: String
    @State var completed: Bool
    var body: some View {
        ZStack (alignment: .leading) {
            RoundedRectangle(cornerRadius: 10.0)
                .fill(Color(.systemBackground))
                .frame(height: 50)
                .padding(.horizontal)
            HStack {
                Image(systemName: "circle")
                    .resizable()
                    .frame(width: 22, height: 22)
                    .padding(.horizontal)
                    .onTapGesture {

                        // here it should set Completed in values to true
                    } 
                Text(name+" \(completed.description)")
            }
            .padding()
        }
    }
}

   

You have the following code:

ForEach(values){ item in
    DesignView(name: item.name, completed: item.completed)
}

You need to pass the current item to DesignView so the changes you make in DesignView are applied to the item.

In DesignView, instead of having the following properties:

@State var name: String
@State var completed: Bool

Include a binding to the item.

@Binding var item: Data

Then you can set the completed property to true.

item.completed = true

The ForEach becomes the following:

ForEach(values){ item in
    DesignView(item: $item)
}

You need to add the $ character when using a binding.

By the way you should come up with a more descriptive name for your Data struct, such as ExamData. Apple has a Data class in the Foundation framework so naming your struct Data can confuse people reading your code.

   

Thanks for your help but when I change the ForEach it says that $item cannot be found in scope.

   

Hacking with Swift is sponsored by RevenueCat

SPONSORED In-app subscriptions are a pain to implement, hard to test, and full of edge cases. RevenueCat makes it straightforward and reliable so you can get back to building your app. Oh, and it's free if your app makes less than $10k/mo.

Learn more

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

Does adding a $ to item at the start of the block fix the error?

ForEach(values){ $item in
    DesignView(item: $item)
}

The following article describes SwiftUI bindings:

What is the @Binding property wrapper?

   

No it doesn't. The problem is that its trying to convert Binding< UpcomingExamsView.ExamData > to Binding< Data >

   

@Bnerd  

try this, add the binding to your DesignView for the bool value i.e.:

@Binding var completed: Bool

Then in your UpcomingExamsView replace item.completed with

$item.completed

as necessary. My understanding for the above is that, whenever the @Binding var completed will change the $item.completed will update too.

   

I couldn't get it to work with seperate views so I combined it in one view Here's my updated code.

import SwiftUI
struct UpcomingExamsView: View {
    struct Data: Identifiable {
        let id = UUID()
        var name: String
        var completed: Bool
    }
    @State private var values: [Data] = [
        Data(name: "John", completed: false),
        Data(name: "Katherine", completed: false),
        Data(name: "Jonas", completed: false),
    ]

    func addNewData() {
        values.append(Data(name: examName, completed: false))
    }
    @State var examName: String = ""
    @FocusState var keyboardFocused: Bool
    @State var DisclosureGroupOpen = false
    @State var addNewExam = false
    @Binding var Wallpaper: Image
    @Environment(\.colorScheme) var colorScheme
    var body: some View {
        ZStack {
            ScrollView {
                VStack {
                    ForEach($values){ $item in
                        if item.completed == false {
                        ZStack (alignment: .leading) {
                            RoundedRectangle(cornerRadius: 10.0)
                                .fill(Color(.systemBackground))
                                .frame(height: 50)
                            HStack {
                                Image(systemName: "circle")
                                    .resizable()
                                    .frame(width: 22, height: 22)
                                    .padding(.horizontal)
                                    .onTapGesture {
                                        item.completed.toggle()
                                    } 

                                Text(item.name+" \(item.completed.description)")

                            }

                        }
                        .padding(.horizontal)
                    }
                }
                    DisclosureGroup(isExpanded: $DisclosureGroupOpen) {
                        ScrollView {
                            VStack {
                                ForEach($values){ $item in
                                    if item.completed {
                                    ZStack (alignment: .leading) {
                                        RoundedRectangle(cornerRadius: 10.0)
                                            .fill(Color(.systemBackground))
                                            .frame(height: 50)
                                        HStack {
                                            Image(systemName: "circle")
                                                .resizable()
                                                .frame(width: 22, height: 22)
                                                .padding(.horizontal)
                                                .onTapGesture {
                                                    item.completed.toggle()
                                                } 

                                            Text(item.name+" \(item.completed.description)")

                                        }

                                    }
                                    .padding(.horizontal)
                                }
                            }
                            }
                        }
                    }
                label: {
                        RoundedRectangle(cornerRadius: 8)
                            .frame(width: 120, height: 20)
                            .foregroundColor(colorScheme == .dark ? Color(.systemGray6) : Color.gray.opacity(0.6))
                            .padding(.horizontal)
                            .overlay(
                                HStack {
                                    if DisclosureGroupOpen == false {
                                        Image(systemName: "chevron.right")
                                            .foregroundColor(.white)
                                    } else {
                                        Image(systemName: "chevron.down")
                                            .foregroundColor(.white)
                                    }
                                    Text("Completed")
                                        .foregroundColor(.white)
                                })
                    }.accentColor(.clear)
                }
            }
            .navigationTitle("Upcoming Exams")
            VStack {
                Spacer()
                if addNewExam {
                    Rectangle()
                        .frame(maxWidth: .infinity, maxHeight: 100)
                        .foregroundColor(Color(.systemBackground))
                        .overlay(VStack{
                            TextField ("Add an Exam", text: $examName)
                                .focused($keyboardFocused)
                                .padding()
                                .onAppear{keyboardFocused = true}
                                .onSubmit {
                                    addNewData()
                                    addNewExam = false
                                    examName = ""
                                }
                            Spacer()
                        })
                }

                if keyboardFocused == false {
                    Rectangle()
                        .onAppear {
                            addNewExam = false
                        }
                        .onTapGesture {
                            addNewExam = true
                        }
                        .frame(maxWidth: .infinity, maxHeight: 50)
                        .foregroundColor(Color(.systemBackground))
                        .overlay(
                            HStack {
                            Image(systemName: "plus")
                                .resizable()
                                .frame(width: 20, height: 20)
                            Text("Add an Exam")
                        }) 
                }
            }
        }
    }
}

But how can I save the Data to be available after the App's restart?

   

@Bnerd  

Saving in UserDeFaults , use it for learning purposes and small data:

Saving

@State private var values: [Data] = [
        Data(name: "John", completed: false),
        Data(name: "Katherine", completed: false),
        Data(name: "Jonas", completed: false),
    ] {
    //Saving into UserDefaults
     didSet {
            let encoder =  JSONEncoder()
            if let encoded = try? encoder.encode(values) {
                UserDefaults.standard.set(encoded, forKey: "values")
            }
        }
    }

Loading

init() {
        if let savedItems = UserDefaults.standard.data(forKey: "values") {
            if let decodedItems = try? JSONDecoder().decode([Data].self, from: savedItems) {
                values = decodedItems
                return
            }
        }
        values = [] //This is in case the loading fails, you can even add your previous Array

    }

and...add Codable to Data struct

struct Data: Identifiable, Codable {
        let id = UUID()
        var name: String
        var completed: Bool
    }

   

It doesn't work.

import SwiftUI
struct UpcomingExamsView: View {
    struct Data: Identifiable, Codable {
        let id = UUID()
        var name: String
        var completed: Bool
    }
    @State private var values: [Data] = [
        Data(name: "John", completed: false),
        Data(name: "Katherine", completed: false),
        Data(name: "Jonas", completed: false),
    ] {
        //Saving into UserDefaults
        didSet {
            let encoder =  JSONEncoder()
            if let encoded = try? encoder.encode(values) {
                UserDefaults.standard.set(encoded, forKey: "values")
            }
        }
    }

    init() {
        if let savedItems = UserDefaults.standard.data(forKey: "values") {
            if let decodedItems = try? JSONDecoder().decode([Data].self, from: savedItems) {
                values = decodedItems
                return
            }
        }
        values = [] //This is in case the loading fails, you can even add your previous Array

    }

    func addNewData() {
        values.append(Data(name: examName, completed: false))
    }
    @State var examName: String = ""
    @FocusState var keyboardFocused: Bool
    @State var DisclosureGroupOpen = false
    @State var addNewExam = false
    @Environment(\.colorScheme) var colorScheme
    var body: some View {
        ZStack {
            ScrollView {
                VStack {
                    ForEach($values){ $item in
                        if item.completed == false {
                            ZStack (alignment: .leading) {
                                RoundedRectangle(cornerRadius: 10.0)
                                    .fill(Color(.systemBackground))
                                    .frame(height: 50)
                                HStack {
                                    Image(systemName: "circle")
                                        .resizable()
                                        .frame(width: 22, height: 22)
                                        .padding(.horizontal)
                                        .onTapGesture {
                                            item.completed.toggle()
                                        } 

                                    Text(item.name+" \(item.completed.description)")

                                }
                            }
                            .padding(.horizontal)
                        }
                    }
                    DisclosureGroup(isExpanded: $DisclosureGroupOpen) {
                        ScrollView {
                            VStack {
                                ForEach($values){ $item in
                                    if item.completed {
                                        ZStack (alignment: .leading) {
                                            RoundedRectangle(cornerRadius: 10.0)
                                                .fill(Color(.systemBackground))
                                                .frame(height: 50)
                                            HStack {
                                                Image(systemName: "circle")
                                                    .resizable()
                                                    .frame(width: 22, height: 22)
                                                    .padding(.horizontal)
                                                    .onTapGesture {
                                                        item.completed.toggle()
                                                    } 

                                                Text(item.name+" \(item.completed.description)")

                                            }

                                        }
                                        .padding(.horizontal)
                                    }
                                }
                            }
                        }
                    }
                label: {
                    RoundedRectangle(cornerRadius: 8)
                        .frame(width: 120, height: 20)
                        .foregroundColor(colorScheme == .dark ? Color(.systemGray6) : Color.gray.opacity(0.6))
                        .padding(.horizontal)
                        .overlay(
                            HStack {
                                if DisclosureGroupOpen == false {
                                    Image(systemName: "chevron.right")
                                        .foregroundColor(.white)
                                } else {
                                    Image(systemName: "chevron.down")
                                        .foregroundColor(.white)
                                }
                                Text("Completed")
                                    .foregroundColor(.white)
                            })
                }.accentColor(.clear)
                }
            }
            .navigationTitle("Upcoming Exams")
            VStack {
                Spacer()
                if addNewExam {
                    Rectangle()
                        .frame(maxWidth: .infinity, maxHeight: 100)
                        .foregroundColor(Color(.systemBackground))
                        .overlay(VStack{
                            TextField ("Add an Exam", text: $examName)
                                .focused($keyboardFocused)
                                .padding()
                                .onAppear{keyboardFocused = true}
                                .onSubmit {
                                    addNewData()
                                    addNewExam = false
                                    examName = ""
                                }
                            Spacer()
                        })
                }

                if keyboardFocused == false {
                    Rectangle()
                        .onAppear {
                            addNewExam = false
                        }
                        .onTapGesture {
                            addNewExam = true
                        }
                        .frame(maxWidth: .infinity, maxHeight: 50)
                        .foregroundColor(Color(.systemBackground))
                        .overlay(
                            HStack {
                                Image(systemName: "plus")
                                    .resizable()
                                    .frame(width: 20, height: 20)
                                Text("Add an Exam")
                            }) 
                }
            }
        }
    }
}

   

@Bnerd  

compiles but does not save?

   

yes

   

its trying to convert Binding< UpcomingExamsView.ExamData > to Binding< Data >

Did you change the type of the values array from Data to ExamData? If so, you have to change the type of the item binding in the detail view from my original reply from Data to ExamData as well.

You have another problem. You nested the Data or ExamData struct inside UpcomingExamsView so the detail view can't find the struct. Move the code for the struct outside the view.

struct Data: Identifiable {
    let id = UUID()
    var name: String
    var completed: Bool
}

struct UpcomingExamsView: View {
...
}

   

@Bnerd in the console it says "set defaults did not run because the user hasn't changed anything"

   

@Bnerd  

Ok you got me a challenge.. :P Previously I though we could avoid the MVVM, I was wrong..anyhow..again using UserDefaults as storage follow the steps below: Make a new file and make the following Struct (Your Data)

import Foundation
struct Data: Identifiable, Codable {
    var id = UUID()
    var name: String
    var completed: Bool
}

Make one more file and make the following Class (your model to handle the Data, save/load)

import Foundation
class Values: ObservableObject {
    @Published var values = [Data]() {
         //@Published so thew view can refresh on updates.
        //Saving into UserDefaults
            didSet {
                let encoder =  JSONEncoder()
                if let encoded = try? encoder.encode(values) {
                    UserDefaults.standard.set(encoded, forKey: "values")
                }
            }
        }

        //Loading from UserDefaults
        init() {
            if let savedItems = UserDefaults.standard.data(forKey: "values") {
                if let decodedItems = try? JSONDecoder().decode([Data].self, from: savedItems) {
                    values = decodedItems
                    return
                }
            }
        }

}

Finally change your ContenView (or how you call it..) should be like this:

import SwiftUI
struct ContentView: View {

    @StateObject var values = Values()

    func addNewData() {
        values.values.append(Data(name: examName, completed: false))

    }

    @State var examName: String = ""
    @FocusState var keyboardFocused: Bool
    @State var DisclosureGroupOpen = false
    @State var addNewExam = false
    @Environment(\.colorScheme) var colorScheme

    var body: some View {
        ZStack {
            ScrollView {
                VStack {
                    ForEach(values.values, id:\.id) { item in
                        if item.completed == false {
                            ZStack (alignment: .leading) {
                                RoundedRectangle(cornerRadius: 10.0)
                                    .fill(Color(.systemBackground))
                                    .frame(height: 50)
                                HStack {
                                    Image(systemName: "circle")
                                        .resizable()
                                        .frame(width: 22, height: 22)
                                        .padding(.horizontal)
                                        .onTapGesture {
                                            if let i = values.values.index(where: { $0.id == item.id }) {
                                                values.values[i].completed.toggle()
                                            }
                                        }
                                    Text(item.name+" \(item.completed.description)")
                                }
                            }
                        }
                    }
                    DisclosureGroup(isExpanded: $DisclosureGroupOpen) {
                        ScrollView {
                            VStack {
                                ForEach(values.values, id:\.id) {  item in
                                    if item.completed {
                                        ZStack (alignment: .leading) {
                                            RoundedRectangle(cornerRadius: 10.0)
                                                .fill(Color(.systemBackground))
                                                .frame(height: 50)
                                            HStack {
                                                Image(systemName: "circle")
                                                    .resizable()
                                                    .frame(width: 22, height: 22)
                                                    .padding(.horizontal)
                                                    .onTapGesture {
                                                        if let i = values.values.index(where: { $0.id == item.id }) {
                                                            values.values[i].completed.toggle()
                                                        }
                                                    }
                                                Text(item.name+" \(item.completed.description)")

                                            }
                                        }
                                        .padding(.horizontal)
                                    }
                                }
                            }
                        }
                    }
                label: {
                    RoundedRectangle(cornerRadius: 8)
                        .frame(width: 120, height: 20)
                        .foregroundColor(colorScheme == .dark ? Color(.systemGray6) : Color.gray.opacity(0.6))
                        .padding(.horizontal)
                        .overlay(
                            HStack {
                                if DisclosureGroupOpen == false {
                                    Image(systemName: "chevron.right")
                                        .foregroundColor(.white)
                                } else {
                                    Image(systemName: "chevron.down")
                                        .foregroundColor(.white)
                                }
                                Text("Completed")
                                    .foregroundColor(.white)
                            })
                }.accentColor(.clear)
                }
            }
            .navigationTitle("Upcoming Exams")
            VStack {
                Spacer()
                if addNewExam {
                    Rectangle()
                        .frame(maxWidth: .infinity, maxHeight: 100)
                        .foregroundColor(Color(.systemBackground))
                        .overlay(VStack{
                            TextField ("Add an Exam", text: $examName)
                                .focused($keyboardFocused)
                                .padding()
                                .onAppear{keyboardFocused = true}
                                .onSubmit {
                                    addNewData()
                                    addNewExam = false
                                    examName = ""
                                }
                            Spacer()
                        })
                }

                if keyboardFocused == false {
                    Rectangle()
                        .onAppear {
                            addNewExam = false
                        }
                        .onTapGesture {
                            addNewExam = true
                        }
                        .frame(maxWidth: .infinity, maxHeight: 50)
                        .foregroundColor(Color(.systemBackground))
                        .overlay(
                            HStack {
                                Image(systemName: "plus")
                                    .resizable()
                                    .frame(width: 20, height: 20)
                                Text("Add an Exam")
                            })
                }
            }

            //end
        }
    }
}

Finally a word of advise, your view is MASSIVE..try to break it down to smaller pieces for better control..it's not mandatory but in the future I am sure you will appreciate it if you have to come back.

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED In-app subscriptions are a pain to implement, hard to test, and full of edge cases. RevenueCat makes it straightforward and reliable so you can get back to building your app. Oh, and it's free if your app makes less than $10k/mo.

Learn more

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

Reply to this topic…

You need to create an account or log in to reply.

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.