0

I'm trying to dynamically update a reference to an @Published var, but I'm not sure how to do it. Returning the value only is just a bool value and loses it's reference to the publisher and doesn't work. I tried returning the publisher itself ($self.isBig) but I can't seem to figure out how to update the value once I have the publisher instead of the @Published property.

I'm basically just trying to treat an @Published as a reference and update the reference on the class instead of copying the publisher's value.

This is a contrived example just trying to get the point across, what I want to do is:

import UIKit
import Combine

class MyClass {
    
    struct MyData {
        enum MyDataType {
            case big
            case yellow
            case bird
        }
        var type: MyDataType
        var isEnabled: Bool
    }
    
    private var cancellables = Set<AnyCancellable>()
    
    @Published var isBig: Bool = false
    @Published var isYellow: Bool = false
    @Published var isBird: Bool = false
    
    var mySwitch = UISwitch()
    
    init() {
        // Add mySwitch and setupSubscribers...
    }

    func updatePublishers(from data: MyData) {
        let publisherForData = specificPublisherForData(data)
        // I want to access the underlying value for the @Published and set it here
//        publisherForData = data.isEnabled
    }

    func specificPublisherForData(_ data: MyData) -> Published<Bool>.Publisher {
        switch data.type {
        case .big: return self.$isBig
        case .yellow: return self.$isYellow
        case .bird: return self.$isBird
        }
     }
    
    func setupSubscribers() {
        $isBig.sink { [weak self] isBig in
            guard let self = self else { return }
            
            self.mySwitch.isOn = isBig
        }.store(in: &cancellables)
        
        // ... add subscribers for the other ones
    }
}
jnpdx
  • 45,847
  • 6
  • 64
  • 94
Alex
  • 3,861
  • 5
  • 28
  • 44
  • I'm a bit confused about what's going on here. I think part of it is that the included code doesn't compile -- can you include a [mre] and then describe how the result is different from what you're expecting? – jnpdx Jan 14 '22 at 20:05
  • `@Published` is about subscribing. You have no subscribers so this is pointless. You are not using Combine at all. You might as well throw away the `@Published` designation altogether. – matt Jan 14 '22 at 20:05
  • @jnpdx Thanks for letting me know my example was missing information. I have updated it with a more comprehensive example. It is still a bit contrived, I'm hoping to convey what I'm trying to do (basically just access an `@Published` without referencing it directly), but I'm having trouble explaining it it seems. – Alex Jan 14 '22 at 20:57

1 Answers1

1

It looks like you're trying to assign a new value to the Publisher in updatePublishers, but that's not really how @Publishers work -- they're just broadcasting values.

Instead, it seems like using a key path might be what you're after:

class MyClass {
    
    struct MyData {
        enum MyDataType {
            case big
            case yellow
            case bird
        }
        var type: MyDataType
        var isEnabled: Bool
    }
    
    private var cancellables = Set<AnyCancellable>()
    
    @Published var isBig: Bool = false
    @Published var isYellow: Bool = false
    @Published var isBird: Bool = false
    
    var mySwitch = UISwitch()
    
    init() {
        // Add mySwitch and setupSubscribers...
    }

    func updatePublishers(from data: MyData) {
        let keypath = specificKeypathForData(data)
        self[keyPath: keypath] = data.isEnabled
    }

    func specificKeypathForData(_ data: MyData) -> ReferenceWritableKeyPath<MyClass,Bool> {
        switch data.type {
        case .big: return \.isBig
        case .yellow: return \.isYellow
        case .bird: return \.isBird
        }
     }
    
    func setupSubscribers() {
        $isBig.sink { [weak self] isBig in
            guard let self = self else { return }
            
            self.mySwitch.isOn = isBig
        }.store(in: &cancellables)
        
        // ... add subscribers for the other ones
    }
}

In a playground:

var myClass = MyClass()
myClass.setupSubscribers()
var data = MyClass.MyData(type: .big, isEnabled: true)
myClass.updatePublishers(from: data)
print(myClass.isBig)

Yields:

true

jnpdx
  • 45,847
  • 6
  • 64
  • 94
  • Genius, thanks! It also seems to work when using `CurrentValueSubject` instead of `@Published`. – Alex Jan 14 '22 at 22:40