-1

After having searched the documentation and reading this essentially unanswered question I still don't know how to pass a @Published var from Class A to Class B in SwiftUI.

I'm not sure if I have fundamentally misunderstood something, since in my opinion the following use-case is quite simple: Suppose we have two "ViewModels" or "Controllers"

  1. LocationManager
  2. PostManager

The LocationManager has a the current location, while the PostManager -completely independently- is responsible for fetching some posts which it then stores in a property "posts". Let's say this fetching is done based on location, so only posts that have a location in a specific relation to the current location are fetched. By having the two managers separate, all the logic (and source of truth) is neatly organised and not directly in any view. Suppose both these classes live as @StateObject in a view. How can the PostManager always have an up to date version of the location to fetch posts (for example periodically, without any input from any view)?

Of course, as a commenter in the linked question suggests, one could use UserDefaults to accomplish this, but I feel like that is rather a workaround than a state of the art implementation..

Example in code

import Foundation
import SwiftUI

class LocationManager: ObservableObject {
    
    @Published var location: [Double] = []
    
    init() {
        // running stuff to keep location up to date
    }
}

class PostManager: ObservableObject {
    
    @Published var posts: [LocationPost] = []
    
    init() {
        // fetching posts based on location in LocationManager instance <- but how do we always have this information
    }
}

class LocationPost {
    var text: String = ""
    var coordinates: [Double] = []
    
    convenience init(coordinates: [Double] = [], test: String? = nil) {
        self.init()
        self.coordinates = coordinates
        self.text = text
    }
}
7
  • If the view does not need the LocationManager, and just the PostManager requires it, consider moving it inside the PostManager. Commented Dec 17, 2022 at 13:17
  • State and its reference counterpart StateObject are sources of truth you would never make one depend on another because then it is no longer a source of truth. Commented Dec 18, 2022 at 4:12
  • @malhal I tried to show with this example that even with this concept in mind it could still happen. The source of truth of the location is in one and the source of truth off posts in another. But where else would the logic run that needs access to both this data? To create posts that need a location in a view seems also the wrong place... Commented Dec 18, 2022 at 11:29
  • @MrAlirezaa could you elaborate, maybe as an answer? Do you mean just moving all the logic into one ObservableObject class? Either way I feel like then the sources of truth are not well separated, which also feels wrong. Commented Dec 18, 2022 at 11:35
  • @D.Kee you can't pass a @Published var from Class A to Class B you can subscribe to it with all of the examples provided by the two people that have answered and more. You might want to look into Dependency injection though, I think that is more of what you are looking for all those solutions are spaghetti like. Commented Dec 18, 2022 at 13:00

2 Answers 2

1

@Published Property Wrapper is actually wrapping a Combine Publisher on your property, and you could subscribe to it with to get updates when it changes.

To do this you have a few options and you could use the one that fits better your use case.

  • Pass LocationManager to PostManager so that it subscribes itself to changes, you can do this directly on the init, or in another method.
  • Pass PostManager to LocationManager.
  • Subscribe one to the other from a class or view holding or having a reference to them.
  • Etc.

Example 1

Subscribing from an external class or view

var cancellable: AnyCancellable? // hold a reference to it

cancellable = locationManager.$location.sink() { location in
    postManager.updateLocation(location)
}

// inside PostManager
func updateLocation(_ location: [Double]) {
    // location was updated
}

Example 2

Updating PostManager to receive LocationManager on the init

class PostManager: ObservableObject {
    
    @Published var posts: [LocationPost] = []
    private var cancellable: AnyCancellable?    

    init(locationManager: LocationManager) {
        cancellable = locationManager.$location.sink() { location in
            // location is updated
        }
    }
}

Example 3

If you have a View with both @StateObjects you can do it directly with SwiftUI.

struct MyView: View {
    @StateObject var locationManager = LocationManager()
    @StateObject var postManager = PostManager()
    
    var body: some View {
        VStack {
            
        }
        .onReceive(locationManager.$location) { location in
            postManager.updateLocation(location)
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

If the view has access to both ObservedObjects you can just pass the location from the view when creating new posts:

in PostManager:

func createPost(location: [Double], text: String) {
        posts.append(LocationPost(coordinates: location, text: text)) 
    }

in the view

@StateObject var locationmanager = LocationManager()
@StateObject var postmanager = PostManager()

...

   postmanager.create(locationmanager.location, text: "my Message")

1 Comment

Sorry for not being clear enough with the example. The answer you gave would solve the issue of creating the posts with the location at the time of posting. But my question is rather "how to pass a @Published var from Class A to Class B" in the sense that the data has to be "live". I edited the question to give a better example to the problem, where your solution would unfortunately not work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.