WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Day 37: Data structure

Forums > 100 Days of SwiftUI

As I continued with the project, I made a diagram below to better understand the data structure.

ObservedObject (tempExpenses) of Addview is synced to the Stateobject (myExpenses) of MainView - hence, any changes made to tempExpenses is reflected on the ObservableObject class and myExpenses of MainView.

The part I don't fully understand is below.

In AddView, NO instance of the Expenses class is created. However, you can still call tempExpenses.expensesArray.append(). If there is no instance of tempExpenses created, how can you add something to its properties?

My theory is, 1) Upon creating an ObservedObject without creating an instance of the class, it simply creates a reference to the class. 2) Whenever change is made to an ObservedObject's properties, it simply signals that change is trying to be made, and sends it to the original class, changes the class's published properties and hence changing the StateObject's properties as well?

   

Lulu wonders:

In AddView, NO instance of the Expenses class is created.
However, you can still call tempExpenses.expensesArray.append().
If there is no instance of tempExpenses created, how can you add something to its properties?

Lulu assumes:

ObservedObject (tempExpenses) of Addview is synced to the StateObject (myExpenses) of MainView
hence, any changes made to tempExpenses is reflected on the ObservableObject class and myExpenses of MainView.

+10 for the diagram. Excellent!
+1 for renaming ContentView to something more concrete! Nice!

You're so close!

You assume that the ObservedObject in AddView is synced to the StateObject in MainView. Not quite!

In fact, they are the same object! When you created AddView, you passed a reference to the myExpenses object. While you call it something else inside AddView, it is referencing the same object you created in MainView.

So you can amend your excellent graphic by creating an Expenses object that exists outside of MainView and AddView. Both views have references to the same object. The ObservedObject property wrapper is a mechanism to inform any and all objects that hold a reference that some published data inside the object has changed.

Instead of your solution with a MainView and an AddView, imagine an application with 10 views each with a reference to the Expenses()object. All of them will be notified when the one referenced object changes.

PS: It's = It is, or It has.

   

Lulu postulates a theory:

My theory is,
1) Upon creating an ObservedObject without creating an instance of the class, it simply creates a reference to the class.
2) Whenever change is made to an ObservedObject's properties, it simply signals that change is trying to be made, and sends it to the original class, changes the class's published properties and hence changing the StateObject's properties as well?

1) True. It creates a reference to the object. It does NOT create an instance of the object.
2) False. It does NOT signal a change, or send changes to the original class object. It directly modifies a single object.

Keep coding!

   

@Obelix, thank you so much! This could not have been explained more clearly - and it also explains what Paul said about structs being more efficient than classes.

One more question - If I understood it correctly, there can only be one StateObject for every class, because if there are multiple StateObject instances, then we won't be able to identify which instance to modifiy with our ObservedObject. So what happens if we want to make multiple distinguishibale instances of a class? (For example, Lulu's expenses, Paul's expenses, and Obelix's expenses.) How do we identify which one of the three objects we want to refer to with just ObeservedObject wrapper?

What I think we can do is use inheritance and create different child classes however many times we want, but that's as far as my guess goes. (If this is something that will be covered in future lessons, please let me know and I'll wait until they come up!)

EDIT: Oh, it seems like I have to tackle this for Day 38's Challenge in any case! I shall try using inheritance and create another class and see how it goes!

   

Lulu asks for more clarification:

So what happens if we want to make multiple distinguishable instances of a class?
For example, Lulu's expenses, Paul's expenses, and Obelix's expenses.
How do we identify which one of the three objects we want to refer to with just ObservedObject wrapper?

Glad you asked! Please copy this into a new project, read the comments, and see if this helps clarify.

Optional Homework Assignment:

Draw a diagram for this code and post here for future students to review.

// View and edit multiple observable objects
// Coded by Obelix on 09 May 2022.

import SwiftUI

class Programmer: ObservableObject, Identifiable {
    var id = UUID()
    @Published var name: String // Yell out when this changes!
    init(named newName: String) { name = newName }
}

struct ProgrammerView: View {
    @ObservedObject var myProgrammer: Programmer // Pass in a reference!
    var myColor      = Color.teal
    var myBackground = Color.yellow

    var body: some View {
        VStack{
            Text("Observe and edit").font(.headline).foregroundColor(.black)
            TextField("name", text: $myProgrammer.name) // Yes! you can edit the name.
                .multilineTextAlignment(.center)
        }
        .frame(width: 330, height: 100, alignment: .center)
        .foregroundColor( myColor ).background(myBackground.opacity(0.6))
    }
}

// Observe three objects, but not edit them.
struct AllProgrammersView: View {
    @ObservedObject var lead:         Programmer // Pass this in!
    @ObservedObject var novice:       Programmer
    @ObservedObject var intermediate: Programmer

    var body: some View {
        VStack {
            Text("Observe, but not edit.").font(.headline)
            Text(lead.name)           // Just view. No editing.
            Text(intermediate.name)   // Just view. No editing.
            Text(novice.name)         // Just view. No editing.
        }.foregroundColor(.yellow)
            .frame(width: 330, height: 120).background(Color.black)
    }
}

struct PublishedObjectTest: View {
    // You want to observe THREE objects that might change.
    @StateObject var obelix    = Programmer(named: "Obelix")
    @StateObject var lulu      = Programmer(named: "Lulu")
    @StateObject var twoStraws = Programmer(named: "twoStraws")

    var body: some View {
        VStack {
            Form {
                Text("Edit a name").font(.headline)
                TextField("name", text: $obelix.name    )
                TextField("name", text: $lulu.name      )
                TextField("name", text: $twoStraws.name )
            }.frame(height: 270)
            // pass in specific objects to ProgrammerView
            ProgrammerView(myProgrammer: obelix,    myColor: .red,   myBackground: .yellow )
            ProgrammerView(myProgrammer: lulu,      myColor: .white, myBackground: .indigo )
            ProgrammerView(myProgrammer: twoStraws, myColor: .black, myBackground: .teal   )
            // pass in ALL objects to AllProgrammersView
            AllProgrammersView(lead: twoStraws, novice: lulu, intermediate: obelix)
        }
    }
}

   

Thank you! I completely understand how it works now. Here's a diagram I made for the code above. (I divided PublishedObjectTest view into three views because it looked a little too clutterd.)

   

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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.