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

SOLVED: Day 47: Habit Tracker. Passing data from class into DetailView

Forums > 100 Days of SwiftUI

Hey everyone!

So I am working on the 3rd challenge for Day 47 and I can't get any data to show from the ActivityClass into the DetailView. When I try to reach back to the class, all I can pull is properties about the array. I can't actually get into the Activity struct and pull specific properties. How can I rectify this conundrum? I had left the code in a buildable state, hence why I have ""(activity.activities.count)"" in my DetailView. And it shows that I can only access the array, not the struct.

//
//  ActivityClass.swift
//  HWSChallenge4
//
//  Created by Raszion23 on 11/24/20.
//

import Foundation

// Holds an array of activities in an array
class ActivityClass: ObservableObject {
    // Array that holds Activity and stores them, allowing for edits and deletion
    @Published var activities = [Activity]() {
        // When activity is added or deleted, changes will be saved
        didSet {
            // Create instance of JSONEncoder
            let encoder = JSONEncoder()
            // Encode the activities that has been added
            if let encoded = try? encoder.encode(activities) {
                // Store encoded activities on disk
                UserDefaults.standard.set(encoded, forKey: "Activities")
            }
        }
    }

    // Load activities when app is reopened
    init() {
        // Read the "Activities" key from UserDefault
        if let activities = UserDefaults.standard.data(forKey: "Activities") {
            // Create instance of JSON Decoder
            let decoder = JSONDecoder()
            // Decode the data from UserDefaults into an array of Activity
            if let decoded = try? decoder.decode([Activity].self, from: activities) {
                // Assign array to activities
                self.activities = decoded
                // Exit
                return
            }
        }
        // If decoding data didnt work, activity array will be empty
        self.activities = []
    }
}
//
//  Activity.swift
//  HWSChallenge4
//
//  Created by Raszion23 on 11/24/20.
//

import Foundation

// Define the properties of an activity and allow it to have an ID
// Allow for Acitvity object to be codable and decodable
struct Activity: Codable, Identifiable {
    var id = UUID()
    let title: String
    let description: String
}
//
//  HabitTrackerView.swift
//  HWSChallenge4
//  HabitTrackerView
//  Created by Raszion23 on 11/23/20.
//

import SwiftUI

struct HabitTrackerView: View {
    // Create instance of ActivityClass
    @ObservedObject var activityClass = ActivityClass()
    // Track if AddView is being shown
    @State private var showingAdd = false

    var body: some View {
        NavigationView {
            List {
                // Create List using items activites from ActivityClass
                ForEach(activityClass.activities) { activity in
                    // Link activity to DetailView
                    NavigationLink(destination: DetailView(activity: ActivityClass())) {
                        HStack {
                            VStack(alignment: .leading) {
                                Text(activity.title)
                                    .font(.headline)

                                Text(activity.description)
                            }
                            Spacer()
                        }
                    }
                }
                .onDelete(perform: removeActivities)
            }

            .navigationBarTitle("iHabit")
            // Add bar button item
            .navigationBarItems(leading: EditButton(), trailing:
                // Add button to add activities
                Button(action: {
                    showingAdd = true
                }) {
                    Image(systemName: "plus")
                })
            // Present AddView
            .sheet(isPresented: $showingAdd, content: {
                AddView(activities: activityClass)
            })
        }
    }

    // Delete activity
    func removeActivities(at offsets: IndexSet) {
        activityClass.activities.remove(atOffsets: offsets)
    }
}

struct HabitTrackerView_Previews: PreviewProvider {
    static var previews: some View {
        HabitTrackerView()
    }
}
//
//  DetailView.swift
//  HWSChallenge4
//
//  Created by Raszion23 on 11/24/20.
//

import SwiftUI

struct DetailView: View {
    // Reach back to ActivityClass
    var activity: ActivityClass

    var body: some View {
        VStack {
            Text("\(activity.activities.count)")
        }
    }
}

struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
        DetailView(activity: ActivityClass())
    }
}

3      

Your list is showing individual activities. Each activity (the struct) has 3 properties, 2 of which I presume you want to show in the detail view.

Your DetailView should take Activity and not ActivityClass.

import SwiftUI

struct DetailView: View {
    // This is the object you are passing in
    var activity: Activity

    var body: some View {
        VStack {
            // now we have access to that item
            Text(activity.title)
            Text(activity.description)
        }
    }
}

struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
    //You will need to update the code here to build
    //I'm just using the initializer here.. but you might want 
    //to create a sample Activity for your preview
        DetailView(activity: ActivityClass().activities[0])
    }
}

And you change your NavigationLink to this:

//ForEach(...) { activity in
NavigationLink(destination: DetailView(activity: activity)) { ... }

This should work.

4      

Thank you so much. I was thinking that since the Class already reaches back to the Activity Struct, I thought that was the way to pass it onto the DetailView. But now I understand that I was calling for the array of activities instead of the individual object itself.

3      

An additional clarification:

Your class is publishing any changes to the array. It is set as an observed object in your main view. This object (var activityClass) is what you are using for your data. Not the original class (the model). Which is why you use it in your ForEach.

Passing data between screens or views can sometimes be tricky. So always make sure you are using the right type. Don't worry about how to get it at first. Just what it needs to be. So in your DetailView, you already knew you needed to display the details of an Activity. Start there, then think about how to pass that to your DetailView.

I like to approach it like this: What do I want to show? then What data do I need for that? then How do I get it to where I want it to be?

Glad I could help. Keep up the good work 👍

5      

When I pass an activity into DetailView() How do I do, so that I actually can change the data of habits inside the DetailView()? Now the DetailView() recieves only an instance of a Struct, but there's no access to change data (that's in the class).

UPDATE: found solution here https://www.hackingwithswift.com/forums/100-days-of-swiftui/problem-with-passing-data-with-navigationlink-challenge-day-47/136/153

3      

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!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.