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

how to add item to items

Forums > SwiftUI

Hello I have an array of families that I add via a sheet. I would like to gradually add members to each family that would appear in the family list. I have. created struct family and people then class which contains array families. I can add a family via the navigation link, but I can't add a family member to the given family

import SwiftUI

struct NovyClovek: View {
    @ObservedObject  var people: Families
    @Environment(\.dismiss) var dismiss
    //var family: Family

    @State private var name = ""
    @State private var age = 11

    var body: some View {
        NavigationView{
            List{
                TextField("name", text: $name)
                TextField("age", value: $age, formatter: NumberFormatter())
            }
            .navigationTitle("new person")
            .toolbar {
                Button ("save") {
                    let newPerson = People(name: name, age: age)
                    if let index = people.families.firstIndex(of: Family) {
                        people.families[index].peoples.append(newPerson)
                        dismiss()
                    }

                }
            }
        }
    }
}

struct NovyClovek_Previews: PreviewProvider {
    static var previews: some View {
        NovyClovek(people: Families())
    }
}

2      

This should work. You need to uncomment your var family and replace index part with below:

if let index = people.families.firstIndex(where: { $0.id == family.id }) {
    people.families[index].peoples.append(newPerson)
    dismiss()
}

2      

How to declare it?

struct NovyClovek_Previews: PreviewProvider {
    static var previews: some View {
        NovyClovek(people: Families(), family: Family())
    }
}

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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

In you ObervableObject add this variable

 static var sample = [Family(name: "Family One", peoples: [People(name: "Person One", age: 21), People(name: "Person Two", age: 22)])]

then you can get family by:

Families.sample[0]

to get people array in family

Families.sample[0].peoples

or in preview you can create them directly. This is for preview so you need to pass some sample data.

2      

this is my struct an class file

import Foundation

struct Family: Codable, Identifiable, Equatable{
    var id = UUID()
    let name: String
    var peoples: [People]
    static func == (lhs: Family, rhs: Family) -> Bool {
        return lhs.id == rhs.id
    }
}

struct People: Codable, Identifiable {
    var id = UUID()
    let name: String
    let age: Int
}
class Families: ObservableObject {
    static var sample = [Family(name: "Family One", peoples: [People(name: "Person One", age: 21), People(name: "Person Two", age: 22)])]
    @ Published var families = [Family]() {
        didSet {
            if let encoded = try? JSONEncoder().encode(families) {
                UserDefaults.standard.set(encoded, forKey: "Items")
            }
        }
    }

    init() {
        if let savedItems = UserDefaults.standard.data(forKey: "Items") {
            if let decodedItems = try? JSONDecoder().decode([Family].self, from: savedItems) {
                families = decodedItems
                return
            }
        }

        families = []
    }

}

and adont know what to do

import SwiftUI

struct NovyClovek: View {
    @ObservedObject  var people: Families
    @Environment(\.dismiss) var dismiss
    var family: Family

    @State private var name = ""
    @State private var age = 11

    var body: some View {
        NavigationView{
            List{
                TextField("meno", text: $name)
                TextField("vek", value: $age, formatter: NumberFormatter())
            }
            .navigationTitle("nove")
            .toolbar {
                Button ("save") {
                    let newPerson = People(name: name, age: age)
                    if let index = people.families.firstIndex(where: { $0.id == family.id }) {
                        people.families[index].peoples.append(newPerson)
                        dismiss()
                    }
                }
            }
        }
    }
}

struct NovyClovek_Previews: PreviewProvider {
    static var previews: some View {
        NovyClovek(people: Families(), family: Families.sample[0].peoples)
    }
}

2      

Ok, then better remove static var and place that directly into your preview

NovyClovek(people: Families(), family: Family(name: "Family One", peoples: [People(name: "Person One", age: 11)]))

2      

now I have no errors. unfortunately, they still do not place members in individual families

2      

Share your ContentView file.

2      

import SwiftUI

struct ContentView: View {

    @StateObject var family = Families()
    @State private var showingAddExpense = false

    var body: some View {
        NavigationView{
            List{
                ForEach(family.families) { familia in
                    //Text(familia.name)
                    NavigationLink{
                        ListFamily()
                    }label: {
                        Text(familia.name)
                    }
                }
                .onDelete(perform: removeItems)

            }
            .navigationTitle("Rodina")
            .toolbar {
                Button {
                    showingAddExpense = true
                } label: {
                    Image(systemName: "plus")
                }
            }
            .sheet(isPresented: $showingAddExpense) {
                NewFamily(family: family)
            }
        }
    }
    func removeItems(at offsets: IndexSet) {
        family.families.remove(atOffsets: offsets)
    }
}

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

2      

import SwiftUI

struct ListFamily: View {

    @StateObject var family = Families()
    @State private var showingAddExpense = false

    var body: some View {
        NavigationView{
            List{
                ForEach(family.families) { familia in
                    ForEach(familia.peoples) {person in
                        Text(person.name)
                    }

                }
                .onDelete(perform: removeItems)

            }
            .navigationTitle("dsdfs")
            .toolbar {
                Button {
                    showingAddExpense = true
                } label: {
                    Image(systemName: "plus")
                }
            }
            .sheet(isPresented: $showingAddExpense) {
                NewPeople(people: Families(), family: Family(name: "Family One", peoples: [People(name: "Person One", age: 11)]))
            }
        }
    }
    func removeItems(at offsets: IndexSet) {
        family.families.remove(atOffsets: offsets)
    }
}

struct ListFamily_Previews: PreviewProvider {
    static var previews: some View {
        ListFamily()
    }
}

2      

In your ContentView you create an object @StateObject var family = Families(). Then you loop through it and follow a navlink that leads you to ListFamily. But in there you create absolutely new object @StateObject var family = Families(). In order to pass the object you either have to inject it as @ObservedObject var family: [Family] or inject that object via Environment. Otherwise you are working with two different objects which are not conneted to each other.

2      

Can you please show me how to fix it? I'm at my wits end.

2      

Can you upload that to github and share the link. Looking at those files you posted I am already confused what are you planning to do. As i understood you want to create a family then add people in there? I posted sample before, can you just copy paste that code in your Xcode, run it and say if this is the behavior you would like to have. This is what I posted in your previous thread. https://www.hackingwithswift.com/forums/swiftui/how-to-create-items-in-items/20488/20534. Just add your data model as well to make it compile.

2      

Now i copy your code from yesterday but ij dont have how prite PreviewProvider. https://github.com/rabrez/test my project

2      

To make the code compile from github you just need to update few lines for in previews:

  1. FamilyMembersView.swift

    struct FamilyMembersView_Previews: PreviewProvider {
    static var previews: some View {
        FamilyMembersView(people: .constant([People]()), familyName: "Test")
    }
    }
  2. AddPersonView.swift

struct AddPersonView_Previews: PreviewProvider {

    static var previews: some View {

        AddPersonView(people: .constant([People]()))
    }
}

and that's it.

2      

Thanks a lot.You are my hero

2      

As I already completed another approach post it as well. So you can choose. This one maybe more easier to follow that the one with lots of binding I used before.

  1. So let's start with ContentView.swift

    struct ContentView: View {
    @StateObject var family = Families()
    @State private var showAddFamily = false
    
    var body: some View {
        NavigationStack {
            List{
                ForEach(family.families) { familia in
                    NavigationLink{
                        ListFamily(family: family, familia: familia)
                    } label: {
                        Text(familia.name)
                    }
                }
                .onDelete(perform: removeItems)
            }
            .navigationTitle("Rodina")
            .toolbar {
                Button {
                    showAddFamily = true
                } label: {
                    Image(systemName: "plus")
                }
            }
            .sheet(isPresented: $showAddFamily) {
                NewFamily(family: family)
            }
        }
    }
    func removeItems(at offsets: IndexSet) {
        family.families.remove(atOffsets: offsets)
    }
    }

2      

  1. Then let's add FamilyView.
struct ListFamily: View {
    @ObservedObject var family: Families
    var familia: Family
    @State private var showingAddExpense = false
     /* We need id to "cheat" a bit our view
     We have an issue with @Published variable as
     we first add family in [Family] array
     So @Published publish changes and our view refreshes
     But when we add a person to family.peoples we do not
     change @Published property as nothing added to [Family]
     as we change peoples inside it, so the variable is not changed
     per se and it does't notify us.
     So we use that id to change every time when we press + button
     and make the view refresh as UUID will be
     different every time. Better to find the way to make @Published
     property change when underlying data in peoples array added.
      */

    @State private var id = UUID()

    var body: some View {
        List {
            ForEach(familia.peoples) { person in
                Text(person.name)
            }
            .onDelete(perform: removeItems)
        }
        .toolbar {
            Button {
                showingAddExpense = true
            } label: {
                Image(systemName: "plus")
            }
        }
        .sheet(isPresented: $showingAddExpense) {
            NewPeople(family: family, familia: familia)
        }
        .onChange(of: showingAddExpense) { _ in
            id = UUID()
        }
    }

    func removeItems(at offsets: IndexSet) {
        if let index = family.families.firstIndex(where: { familia.id == $0.id }) {
            family.families[index].peoples.remove(atOffsets: offsets)
        }
    }
}

struct ListFamily_Previews: PreviewProvider {
    static var previews: some View {
        NavigationStack {
            ListFamily(family: Families(), familia: Families.sample[0])
        }
    }
}

2      

  1. And with NewFamily we will add new families to our object
struct NewFamily: View {
    @Environment(\.dismiss) var dismiss
    @ObservedObject var family: Families
    @State private var name = ""

    var body: some View {
        NavigationStack {
            VStack {
                TextField("New family", text: $name)
                    .textFieldStyle(.roundedBorder)

                Button("Save") {
                    let newFamily = Family(name: name, peoples: [])
                    // We got name for new family so let's add it to our family object
                    family.families.append(newFamily)
                    dismiss()
                }
                .buttonStyle(.borderedProminent)

                Spacer()
            }
            .padding()
            .toolbar {
                Button("Close") {
                    dismiss()
                }
            }
        }
    }
}

struct NewFamily_Previews: PreviewProvider {
    static var previews: some View {
        NavigationStack {
            NewFamily(family: Families())
        }
    }
}
  1. With NewPeople we will add new people to family
struct NewPeople: View {
    @Environment(\.dismiss) var dismiss
    @ObservedObject var family: Families
    var familia: Family
    @State private var name = ""
    @State private var age = 10

    var body: some View {
        NavigationStack {
            VStack {
                TextField("Name", text: $name)
                    .textFieldStyle(.roundedBorder)
                TextField("Age", value: $age, format: .number)
                    .textFieldStyle(.roundedBorder)

                Button("Save") {
                    let newPerson = People(name: name, age: age)
                    // We got new person, so lets find where it is in our family object in families array
                    // And add that person accordingly
                    if let index = family.families.firstIndex(where: { familia.id == $0.id }) {
                        family.families[index].peoples.append(newPerson)
                    }
                    dismiss()
                }
                .buttonStyle(.borderedProminent)

                Spacer()
            }
            .padding()
            .toolbar {
                Button("Close") {
                    dismiss()
                }
            }
        }
    }
}

struct NewPeople_Previews: PreviewProvider {
    static var previews: some View {
        NavigationStack {
            NewPeople(family: Families(), familia: Families.sample[0])
        }
    }
}
  1. and DataModel is the same basically
struct Family: Codable, Identifiable, Equatable{
    var id = UUID()
    let name: String
    var peoples: [People]
    static func == (lhs: Family, rhs: Family) -> Bool {
        return lhs.id == rhs.id
    }
}

struct People: Codable, Identifiable {
    var id = UUID()
    let name: String
    let age: Int
}
class Families: ObservableObject {
    static var sample = [Family(name: "Family One", peoples: [People(name: "Person One", age: 21), People(name: "Person Two", age: 22)])]

    @Published var families = [Family]() {
        didSet {
            if let encoded = try? JSONEncoder().encode(families) {
                UserDefaults.standard.set(encoded, forKey: "Items")
            }
        }
    }

    init() {
        if let savedItems = UserDefaults.standard.data(forKey: "Items") {
            if let decodedItems = try? JSONDecoder().decode([Family].self, from: savedItems) {
                families = decodedItems
                return
            }
        }

        families = []
    }

}

2      

Best way.Thanks

2      

I have some isue. How to add other items under people

import SwiftUI

struct NewNotes: View {
    @Environment(\.dismiss) var dismiss
    @ObservedObject var family: Families
    @State private var newNote = ""
    @State private var zaznamCislo = 1
    var familia: Vcelnica
    var ule: Ule

    var body: some View {
        NavigationStack{
            Form {
                VStack{

                    TextField("Note", text: $newNote)
                        .textFieldStyle(.roundedBorder)
                    Button("save") {
//                        let zaznam = Zanam(zaznmaCislo: zaznamCislo, zaznamObsah: newNote)
//                        if let index = family.families.firstIndex(where: { familia.id == $0.id }) {
//                            if let index2 = family.families[index].ul.firstIndex(where: {ule.id == $0.id}) {
//                                family.families[index].ul[index2].zaznamy.append(zaznam)
//                            }
//                        }
//                        dismiss()
                        let index1 = family.families.firstIndex(where: { familia.id == $0.id })
                        let zaznam = Zanam(zaznmaCislo: zaznamCislo, zaznamObsah: newNote)
                        if let index = ule.zaznamy.firstIndex(where: {ule.id == $0.id}) {
                            family.families[index1 ?? 0].ul[index].zaznamy.append(zaznam)
                        }
                    }
                    .buttonStyle(.bordered)
                }
            }
            //.padding()
            .toolbar {
                Button("Close") {
                    dismiss()
                }
            }
        }
    }
}

struct NewNotes_Previews: PreviewProvider {
    static var previews: some View {
        NewNotes(family: Families(), familia: Families.sample[0], ule: Ule(name: "", cisloUla: 21, cisloMatky: 1, farbaMatky: "", age: 1, zaznamy: []))
    }
}

2      

struct Vcelnica: Codable, Identifiable, Equatable{
    var id = UUID()
    let name: String
    var ul: [Ule]
    static func == (lhs: Vcelnica, rhs: Vcelnica) -> Bool {
        return lhs.id == rhs.id
    }
}

struct Ule: Codable, Identifiable{

    var id = UUID()
    let name: String
    let cisloUla: Int
    let cisloMatky: Int
    let farbaMatky: String
    let age: Int
    var zaznamy: [Zanam]
}

struct Zanam: Codable, Identifiable {
    var id = UUID()
    let zaznmaCislo: Int
    let zaznamObsah: String
}

class Families: ObservableObject {
    static var sample = [Vcelnica(name: "Family One", ul: [Ule(name: "Person One", cisloUla: 1, cisloMatky: 1, farbaMatky: "modra", age: 1, zaznamy: [(Zanam(zaznmaCislo: 1, zaznamObsah: "test1"))] ), Ule(name: "Person Two", cisloUla: 2, cisloMatky: 2, farbaMatky: "modra", age: 2, zaznamy: [(Zanam(zaznmaCislo: 2, zaznamObsah: "test2"))])])]

    @Published var families = [Vcelnica]() {
        didSet {
            if let encoded = try? JSONEncoder().encode(families) {
                UserDefaults.standard.set(encoded, forKey: "Items")
            }
        }
    }

    init() {
        if let savedItems = UserDefaults.standard.data(forKey: "Items") {
            if let decodedItems = try? JSONDecoder().decode([Vcelnica].self, from: savedItems) {
                families = decodedItems
                return
            }
        }

        families = []
    }

}

2      

As you already have Family conform to Equatable you can use firstIndex(of:) method. The logic should follow as:

// First find the index of Family to make modifications
if let familyIndex = family.families.firstIndex(of: familia) {
  // Second find the index of Ul to make modifications
  if let ulIndex = family.families[familyIndex].ul.firstIndex(where: {ule.id == $0.id}) {
     let zaznam = Zanam(zaznmaCislo: zaznamCislo, zaznamObsah: newNote)
     // Then add your zaznam
     family.families[familyIndex].ul[ulIndex].zaznamy.append(zaznam)
  }
}

well if no typos from my side, this should work

2      

something went wrong Dont work :)

2      

Have you tried to run on simulator? I suspect, the item might be added but it does not appear after addition. The issue is the same, you change the underlying value of @Published property, and as there is no change in property itself it does not publish the change. Or the item is not added at all?

2      

it seems that the item will not be added. I think I've tried all the combinations, but it's not enough with my knowledge

2      

II uploaded it to git https://github.com/rabrez/check

2      

@ygeras pls help meeeee

2      

I'd like to, but with current state of the project I have no idea what are you trying to do there. I tried to clone your project but on Preview it says Active Scheme does not build this file. No idea what is wrong with preview even.

2      

So exactly... the project will be for an apiary In the first preview (family) I will create apiaries (I have more) In the second view (people) I will create hives in the given apiary in the third preview, I want to make a report (notes) for each beehive.

2      

As there are too many changes you want to implement, I have revised the logic. You will want to adjust to your data which you'd like to collect. But this one is more or less workable. Though it is still not good idea to save everything to UserDefaults. It's recommended to keep it less than 100kb. Otherwise it will affect app startup.

https://github.com/ygeras/Beehives.git

2      

Which storage method is better?

2      

There is no right answer to your question. All depends on the scope of your project and what you are plannig to do later. But as a starting point you can save it in a file in your documents directory. Look at the second part of this lesson, where Paul explains how to do that. Minimal changes to your current project will be required. https://www.hackingwithswift.com/books/ios-swiftui/introducing-mvvm-into-your-swiftui-project

2      

@Tools  

Hey ,Please help me anyone here

2      

Here's an updated version of your code with the necessary modifications:

swift Copy code import SwiftUI

struct NovyClovek: View { @ObservedObject var people: Families @Environment(.dismiss) var dismiss var family: Family

@State private var name = ""
@State private var age = 11

var body: some View {
    NavigationView {
        List {
            TextField("Name", text: $name)
            TextField("Age", value: $age, formatter: NumberFormatter())
        }
        .navigationTitle("New Person")
        .toolbar {
            Button("Save") {
                let newPerson = People(name: name, age: age)
                if let index = people.families.firstIndex(of: family) {
                    people.families[index].peoples.append(newPerson)
                    dismiss()
                }
            }
        }
    }
}

}

struct NovyClovek_Previews: PreviewProvider { static var previews: some View { NovyClovek(people: Families(), family: Family()) } } Make sure that the Family and People structs and the Families class are defined correctly. Additionally, ensure that you pass the appropriate Family object when navigating to the NovyClovek view.

With these modifications, you should be able to add a new person to the selected family.

2      

Got it alot. Perfactly. Thanks

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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.