'How to use MVVM correctly in swiftUI?

I'm learning SwiftUI and tried to implement the MVVM architecture. The idea is simple, I tried to add photo to a list which it changes based on the weekday selected. The photos are locally saved.

However When I used the MVVM architecture, it complicated the whole situation. Specially if you want to save each schedule of the weekday separately. Because each weekday is a separate array. I'm sure there is a much easier way to do the code below. But I didn't figure it out.

The model :

import Foundation

struct Activity: Identifiable, Codable {
  var id = UUID()
  var image: String
  var name: String
    
}

Viewmodel :

import Foundation
import UIKit

class Activities: ObservableObject {
    
    //MARK:- PROPERTIES:
    
    var indoorActivities = [Activity]()
    var outdoorActivities = [Activity]()
    let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.path
    
    //Use it as example in RowView preview
    var exampleAct: [Activity] {
        return [indoorActivities[0], indoorActivities[1]]
    }
    
    @Published  var sundayActivities = [Activity]() {
        didSet {
            print("Change hapeend to sundayAcitivty")
            let encoder = JSONEncoder()
            if let data = try? encoder.encode(sundayActivities) {
                UserDefaults.standard.set(data, forKey: "sunday")
                
            }
        }
    }
    @Published var mondayActivities = [Activity]() {
        didSet {
            let encoder = JSONEncoder()
            if let data = try? encoder.encode(mondayActivities) {
                UserDefaults.standard.set(data, forKey: "monday")
                
            }
        }
    }
    @Published var tuesdayActivities = [Activity]() {
        didSet {
            let encoder = JSONEncoder()
            if let data = try? encoder.encode(tuesdayActivities) {
                UserDefaults.standard.set(data, forKey: "tuesday")
                
            }
        }
    }
    @Published var wednesdayActivities = [Activity]() {
        didSet {
            let encoder = JSONEncoder()
            if let data = try? encoder.encode(wednesdayActivities) {
                UserDefaults.standard.set(data, forKey: "wednesday")
                
            }
        }
    }
    @Published var thursdayActivities = [Activity]() {
        didSet {
            let encoder = JSONEncoder()
            if let data = try? encoder.encode(thursdayActivities) {
                UserDefaults.standard.set(data, forKey: "thursday")
                
            }
        }
    }
    @Published var fridayActivities = [Activity]() {
        didSet {
            let encoder = JSONEncoder()
            if let data = try? encoder.encode(fridayActivities) {
                UserDefaults.standard.set(data, forKey: "friday")
                
            }
        }
    }
    @Published var saturdayActivities = [Activity]() {
        didSet {
            let encoder = JSONEncoder()
            if let data = try? encoder.encode(saturdayActivities) {
                UserDefaults.standard.set(data, forKey: "saturday")
                
            }
        }
    }
    
   //MARK:- INIT:
    
    init() {
        if let data = UserDefaults.standard.data(forKey: "sunday") {
            let decoder = JSONDecoder()
            if let sunday = try? decoder.decode([Activity].self, from: data) {
                self.sundayActivities = sunday

            }
        } else {
            self.sundayActivities = []
        }
        
        if let data = UserDefaults.standard.data(forKey: "monday") {
            let decoder = JSONDecoder()
            if let monday = try? decoder.decode([Activity].self, from: data) {
                self.mondayActivities = monday
            }
        } else {
            self.mondayActivities = []
        }
        
        if let data = UserDefaults.standard.data(forKey: "tuesday") {
            let decoder = JSONDecoder()
            if let monday = try? decoder.decode([Activity].self, from: data) {
                self.tuesdayActivities = monday
            }
        } else {
            self.tuesdayActivities = []
        }
        
        if let data = UserDefaults.standard.data(forKey: "wednesday") {
            let decoder = JSONDecoder()
            if let monday = try? decoder.decode([Activity].self, from: data) {
                self.wednesdayActivities = monday
            }
        } else {
            self.wednesdayActivities = []
        }
        
        if let data = UserDefaults.standard.data(forKey: "thursday") {
            let decoder = JSONDecoder()
            if let monday = try? decoder.decode([Activity].self, from: data) {
                self.thursdayActivities = monday
            }
        } else {
            self.thursdayActivities = []
        }
        
        if let data = UserDefaults.standard.data(forKey: "friday") {
            let decoder = JSONDecoder()
            if let monday = try? decoder.decode([Activity].self, from: data) {
                self.fridayActivities = monday
            }
        } else {
            self.fridayActivities = []
        }
        
        if let data = UserDefaults.standard.data(forKey: "saturday") {
            let decoder = JSONDecoder()
            if let monday = try? decoder.decode([Activity].self, from: data) {
                self.saturdayActivities = monday
            }
        } else {
            self.saturdayActivities = []
        }
        
        
        
        if let urls = Bundle.main.urls(forResourcesWithExtension: "jpg", subdirectory: "/activities/indoorActivities") {
            
            for url in urls {
                
                let path = "//activities/indoorActivities" + "/\(url.lastPathComponent)"
                let name = url.deletingPathExtension().lastPathComponent
                let activity = Activity(image: path, name: name)
                indoorActivities.append(activity)
               
            }
        }
        
        //Get the documnet directory
        // attach the image path to the document direcotroy
        // assign it to image
        
        
        if let urls = Bundle.main.urls(forResourcesWithExtension: "jpg", subdirectory: "/activities/outdoorActivities") {
            
            for url in urls {
                
                let path = "//activities/outdoorActivities" + "/\(url.lastPathComponent)"
                let name = url.deletingPathExtension().lastPathComponent
                let activity = Activity(image: path, name: name)
                outdoorActivities.append(activity)
               
            }
        }
        
    }
    
    // Get UIImage from a url path
    class func getImage(image: String) -> UIImage {
        
        
        let bundle = Bundle.main.bundlePath
        if let uiImage = UIImage(contentsOfFile: bundle + image) {
            return uiImage
        }
        return UIImage(systemName: "circle.fill")!
    }
    
}

Views :

DailyLifeView :

import SwiftUI

struct DailyLifeView: View {
    
    @EnvironmentObject var activities: Activities
    @State private var weekDay = "Sun"
    @State private var showActivites = false
    @State private var showDeleteAllButton = false
     var weekDays = ["Sun", "Mon", "Tue", "Wed", "Thur", "Fri", "Sat"]
  
   
    var body: some View {
        
        NavigationView {
            ZStack {
                VStack (spacing: 20){
                    Text("Choose the daily activities for your child")
                        .font(.headline)
                        .fontWeight(.bold)
                        .foregroundColor(.secondary)
                    Picker("Weeks", selection: $weekDay) {
                        ForEach(weekDays, id: \.self) {
                            Text($0)
                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
                    
                    if showDeleteAllButton {
                        Button(action: {
                            withAnimation {
                                deleteAll()
                            }
                            
                            
                        }, label: {
                            
                            HStack {
                                Text("Delete All")
                                    
                                Image(systemName: "trash")
                            }
                            .font(.headline)
                            .foregroundColor(.white)
                        })
                        .padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20))
                        .background(Color.red)
                        .cornerRadius(10)
                        .transition(.scale)
                        
                    }
                  
                    
                    
                    
                    List {
                        switch weekDay {
                        case "Sun":
                            RowView(activities: $activities.sundayActivities)
                                
                        case "Mon":
                            RowView(activities: $activities.mondayActivities)
                        case "Tue":
                            RowView(activities: $activities.tuesdayActivities)
                        case "Wed":
                            RowView(activities: $activities.wednesdayActivities)
                        case "Thur":
                            RowView(activities: $activities.thursdayActivities)
                        case "Fri":
                            RowView(activities: $activities.fridayActivities)
                        case "Sat":
                            RowView(activities: $activities.saturdayActivities)
                        default:
                            RowView(activities: $activities.sundayActivities)
                        }
                    }
                    .listStyle(InsetListStyle())
                    
                    
                    Spacer()
                    
                }
                .padding()
                VStack{
                    Spacer()
                    HStack{
                        Spacer()
                        Button(action: {showActivites.toggle()}, label: {
                            Text("+")
                                .font(.system(.largeTitle))
                                .frame(width: 77, height: 70)
                                .foregroundColor(Color.white)
                                .padding(.bottom, 7)
                            
                        })
                        .background(Color(hex: "64B5F6"))
                        .cornerRadius(38.5)
                        .padding()
                        .shadow(color: Color.black.opacity(0.3),
                                radius: 3,
                                x: 3,
                                y: 3)
                        .sheet(isPresented: $showActivites, content: {
                            ActivitiesList(weekDay: self.weekDay)
                        })
                    }
                }
            }
            .navigationViewStyle(DefaultNavigationViewStyle())
            .navigationBarTitle("Daily Life")
            .navigationBarTitleDisplayMode(.inline)
            .navigationBarItems(
                trailing: Button(action: {
                    withAnimation {
                        showDeleteAllButton.toggle()

                    }
                    
                }, label: {
                    Text(showDeleteAllButton ? "Done" : "Edit")
                })
            
            )
        }
    }
    func deleteAll() {
        switch weekDay {
        case "Sun":
            activities.sundayActivities.removeAll()
        case "Mon":
            activities.mondayActivities.removeAll()
        case "Tue":
            activities.tuesdayActivities.removeAll()
        case "Wed":
            activities.wednesdayActivities.removeAll()
        case "Thur":
            activities.thursdayActivities.removeAll()
        case "Fri":
            activities.fridayActivities.removeAll()
        case "Sat":
            activities.saturdayActivities.removeAll()
        default:
            activities.sundayActivities.removeAll()
        }
        
        
    }
}

ActivitiesList :

import SwiftUI



struct ActivitiesList: View {
    
    @Environment(\.presentationMode) var presentationMode
    @EnvironmentObject var activities: Activities
    var weekDays = ["Sun", "Mon", "Tue", "Wed", "Thur", "Fri", "Sat"]
    var weekDay: String = "Sun"
    let columns = [
        GridItem(.adaptive(minimum: 100), spacing: 20)
    ]
    
    
    var body: some View {
        ScrollView {
            LazyVGrid(
                columns: columns,
                spacing: 30
//                pinnedViews: [.sectionHeaders]
            ) {
                Section(
                    header: Text("INDOOR ACTIVITIES")
                        .font(.title)
                        .fontWeight(.bold)
                        .foregroundColor(.white)
                
                ) {
                    ForEach(activities.indoorActivities) { activity in
                        Button(action: {
                            self.selectWeekDay(activity: activity)
                            print(activity.name)
                            self.presentationMode.wrappedValue.dismiss()
                            
                        }, label: {
                            Image(uiImage: Activities.getImage(image: activity.image))
                                .resizable()
                                .scaledToFit()
                                .cornerRadius(10)
//
                        })
                        
                            
                    }
                }
                Section(
                    header: Text("OUTDOOR ACTIVITIES")
                        .font(.title)
                        .fontWeight(.bold)
                        .foregroundColor(.white)
                
                ) {
                    ForEach(activities.outdoorActivities) { activity in
                        Button(action: {
                            self.activities.sundayActivities.append(activity)
                            print(activity.name)
                            self.presentationMode.wrappedValue.dismiss()
                        }, label: {
                            Image(uiImage: Activities.getImage(image: activity.image))
                                .resizable()
                                .scaledToFit()
                                .cornerRadius(10)
                              
                        })
                    }
                }
            }
            .padding()
        }
        .background(Color(hex: "90CAF9"))
        
       
    }
    
    //MARK: - FUNCTIONS:
    
 
    
    func selectWeekDay(activity: Activity) {
        switch weekDay {
        case "Sun":
            self.activities.sundayActivities.append(activity)
        case "Mon":
            self.activities.mondayActivities.append(activity)
        case "Tue":
            self.activities.tuesdayActivities.append(activity)
        case "Wed":
            self.activities.wednesdayActivities.append(activity)
        case "Thur":
            self.activities.thursdayActivities.append(activity)
        case "Fri":
            self.activities.fridayActivities.append(activity)
        case "Sat":
            self.activities.saturdayActivities.append(activity)


        default:
            self.activities.sundayActivities.append(activity)

        }
    }
}

RowView:

import SwiftUI
import AVFoundation

struct RowView: View {
    
    @Binding var activities: [Activity]
    
    var body: some View {
        ForEach(activities) { activity in
            Button(action: {
                let Synth = AVSpeechSynthesizer()
                let utterance = AVSpeechUtterance(string: activity.name)
                Synth.speak(utterance)
                
                
                
            }, label: {
                HStack {
                    Image(uiImage: Activities.getImage(image: activity.image))
                        .resizable()
                        .scaledToFit()
                        .cornerRadius(10)
                        

                    Spacer()
                     
                    Text(activity.name)
                        .font(.title3)
                        .fontWeight(.bold)
                        .foregroundColor(.white)
                }
                .padding()
                .background(Color(hex: "90CAF9"))
                .frame(height: 100)
                .cornerRadius(20)
                .shadow(
                    color: Color.black.opacity(0.3), radius: 3, x: 3, y: 3)
                
               
            })
           
        }
        .onDelete(perform: removeItems)
        
        
       
        
    }
    
    func getImage(image: String) -> UIImage {
        if let uiImage = UIImage(contentsOfFile: image) {
            return uiImage
        }
        return UIImage(systemName: "circle.fill")!
    }
    
     func removeItems(at offsets: IndexSet) {

        activities.remove(atOffsets: offsets)

    }
    
     
}

Sorry for the long code attached, but I couldn't explain the issue unless you see the full code of the view.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source