Home > Software design >  Get data from model for multiple List views ObservableObject class and App Storage to save for both
Get data from model for multiple List views ObservableObject class and App Storage to save for both

Time:10-05

How to pull in the data from MenuListItemModel.swift ObservableObject classes to load into FriendshipListView.swift and WealthListView.swift. The WealthListView.swift is loading the same data as FriendshipListView.swift. Also, do I have to do something with @AppStorage so each list saves the states properly?

MenuApp.swift

import SwiftUI

@main
struct ManifestationMenuApp: App {
    
    @AppStorage("isOnboarding") var isOnboarding: Bool = true
    
    var body: some Scene {
        WindowGroup {
            if isOnboarding {
                OnboardingView()
            } else {
                MainView()
                    .environmentObject(MenuListFriendshipModel())
                    .environmentObject(MenuListWealthModel())
            }
        }
    }
}

MenuListItemModel.swift

import SwiftUI

struct MenuListItemModel: Identifiable, Codable {
    var id: String = UUID().uuidString
    var title: String
    var description: String
    var isCompleted: Bool
}

MenuListFriendshipModel.swift

import SwiftUI

//use array in @Appstorage
extension Array: RawRepresentable where Element: Codable{
    public init?(rawValue: String) {
            guard let data = rawValue.data(using: .utf8),
                  let result = try? JSONDecoder().decode([Element].self, from: data)
            else {
                return nil
            }
            self = result
        }

        public var rawValue: String {
            guard let data = try? JSONEncoder().encode(self),
                  let result = String(data: data, encoding: .utf8)
            else {
                return ""
            }
            return result
        }
    
}

class MenuListFriendshipModel: ObservableObject {
    // this loads and saves the items automatic to UserDefaults
    @AppStorage("task_items_list") var items: [MenuListItemModel] = []
    
    
    init() {
        // fill the collection only if it is empty
        if items.isEmpty{
            myTaskItems()
        }
    }
    
    func myTaskItems() {
        items = [
            MenuListItemModel(title: "I am grateful to have close and meaningful relationships.", description: "My relationships are healthy and enduring, whether with friends, colleagues, family, or romantic contacts.", isCompleted: false),
            MenuListItemModel(title: "My relationships are fun and rewarding", description: "(e.g., exciting, generous, etc.) ",isCompleted: false),
            MenuListItemModel(title: "No matter how long my relationships last, they are experiences I cherish.", description: "Thirty years or thirty minutes, my relationships are not judged on how long they last but on their meaningful moments.",isCompleted: false),
            MenuListItemModel(title: "I treat my friends as I wish them to treat me.", description: "I am grateful for the wisdom to know when my friends and loved ones need my understanding, advice, or ears.",isCompleted: false),
            MenuListItemModel(title: "I have a few friends whom I love.", description: "I realize how fortunate I am to have a few close friends who are always by my side through good and bad times.",isCompleted: false),
            MenuListItemModel(title: "My friends are great at finding new ways for us to be together. ", description: "We are never out of ideas on how to create excellent and meaningful moments together.",isCompleted: false),
            MenuListItemModel(title: "I am fine without relationships.", description: "I do not shun relationships, but I do not find them necessary for a meaningful life.",isCompleted: false),
            MenuListItemModel(title: "I am open to making new friends.", description: "Each new friend is a new world to discover; the more, the merrier.",isCompleted: false)
        ]
        
    }
}

class MenuListWealthModel: ObservableObject {
    // this loads and saves the items automatic to UserDefaults
    @AppStorage("task_items_list") var items: [MenuListItemModel] = []
    
    
    init() {
        //fill the collection only if it is empty
        if items.isEmpty{
            myTaskItems()
        }
    }
    
    func myTaskItems() {
        items = [
            MenuListItemModel(title: "Test", description: "testing",isCompleted: false),
            MenuListItemModel(title: "Test", description: "testing",isCompleted: false),
            MenuListItemModel(title: "Test", description: "testing",isCompleted: false),
            MenuListItemModel(title: "Test", description: "testing",isCompleted: false),
            MenuListItemModel(title: "Test", description: "testing",isCompleted: false),
            MenuListItemModel(title: "Test", description: "testing",isCompleted: false),
            MenuListItemModel(title: "Test", description: "testing",isCompleted: false),
            MenuListItemModel(title: "Test", description: "testing",isCompleted: false)
        ]
        
    }
}

MenuListRowView.swift

import SwiftUI

struct MenuListRowView: View {
    // This binding will assure changes will bubble up into the ViewModel represents the state of model data that will be saved in UserDefaults
    
    @Binding var item: MenuListItemModel
    
    var body: some View {
        HStack(alignment: .top) {
            Image(systemName: item.isCompleted ? "checkmark.circle" : "circle") //.imageScale(.large)
                .foregroundColor(item.isCompleted ? .green : .gray)
                .font(.system(size: 30, weight: .bold))
                .padding(.top, 6)
            VStack(alignment: .leading, spacing: 8) {
                Text(item.title)
                    .font(.title3.bold())
                Text(item.description)
                    .font(.body)
            }
            Spacer()
        }
        .padding(.vertical, 8)
        .onTapGesture {
            item.isCompleted.toggle()
        }
    }
}

FriendshipListView.swift

import SwiftUI

struct FriendshipListView: View {
    
    @EnvironmentObject var listViewModel: MenuListFriendshipModel
    
    var body: some View {
        NavigationView {
            List {
                //Use the $ syntax to pass a binding on to the subviews
                ForEach($listViewModel.items) { $item in
                    MenuListRowView(item: $item)
                }
            }
            .navigationBarTitle(Text("Friendships / Relationships"))
            .listStyle(PlainListStyle())
        }
    }
}

struct ListView_Previews: PreviewProvider {
    static var previews: some View {
        FriendshipListView()
        .environmentObject(MenuListFriendshipModel())
    }
}

WealthListView.swift

import SwiftUI

struct WealthListView: View {
    
    @EnvironmentObject var listViewModel: MenuListWealthModel
    
    var body: some View {
        NavigationView {
            List {
                //Use the $ syntax to pass a binding on to the subviews
                ForEach($listViewModel.items) { $item in
                    MenuListRowView(item: $item)
                }
            }
            .navigationBarTitle(Text("Money / Wealth / Security"))
            .listStyle(PlainListStyle())
        }
    }
}

struct WealthListView_Previews: PreviewProvider {
    static var previews: some View {
        WealthListView()
        .environmentObject(MenuListWealthModel())
    }
}

FriendListView

WealthListView

CodePudding user response:

Well your items are the same because you use the same key for @AppStorage. The key defines where this is saved. So when you are checking for your items in MenuListWealthModel there are allready items there and nothing is added.

            //here is the error
@AppStorage("task_items_list") var items: [MenuListItemModel] = []

Change the key to be something different in MenuListWealthModel e.g.:

@AppStorage("task_items_list_for_wealth") var items: [MenuListItemModel] = []

Remarks:

Don´t call these MenuListWealthModel and MenuListFriendshipModel. These are Viewmodel classes and not Model´s. It could only lead to confusion.

  • Related