@Published as a function argument with Swift Combine

Viewed 2479

I want to be able to pass a reference to a @Published property in as a parameter so that other objects can use, edit, and view the data on the property, and I want to do it while keeping the functionality of Property wrappers in Swift

Here is my example. I create a MainStore object. It has an array of names which is @Published. I can use this object all across my app, I can change the data in names and anywhere that subscribes will get the update.

class MainStore {
    @Published var names: [String]
    
    init(names: [String]) {
        self.names = names
    }
}

Now I want to create a second Store. This has a bit of additional functionality, and it needs to be more focused but I want it to reference the names property from the MainStore. When MainStore.names is updated, I want SecondStore.names to be updated as well. When SecondStore.names is updated, I want MainStore.names to be updated as well.

class SecondStore {
    
    @Published var names: [String]
    
    init(@Published names: [String]) {
        self.names = names
    }
}

I want the syntax of a @Published property wrapper as well

eg SecondStore.names.append("Billy") and SecondStore.$names.sink() { }

Is there a way to do this, if not what is the recommended practice to accomplish my general direction?

1 Answers

You can pass the MainStore object to the SecondStore and then listen to the changes made to the mainStore.names property.

class MainStore {
    @Published var names: [String]

    init(names: [String]) {
        self.names = names
    }
}
class SecondStore {
    @Published var names: [String] = []

    private var cancellables = Set<AnyCancellable>()

    init(mainStore: MainStore) {
        // listen to the `$names` publisher
        mainStore.$names
            // all `@Published` properties must be updated on the `main` thread
            .receive(on: RunLoop.main)
            // assign received value to self.names
            .sink { [weak self] in
                self?.names = $0
            }
            // keep it in memory so it won't be cancelled
            .store(in: &cancellables)
    }
}

Note that in the .sink part there is a possibility of a strong reference cycle.

This is why [weak self] is used. You can read more here: Weak Self in Swift Made Easy: What it is and why it’s needed

Related