Search code examples
iosswiftxcodereactive-programmingcombine

Why can't I call .send on a publisher in a class from a second class and have it update the subscription in third class?


I have this class in LRVDetails.swift

class LRVDetails {
    var heading = CurrentValueSubject<String, Never>(K.LRV.Register)
    // other publishers
}

I have this assign in LRVViewController.swift

private var details = LRVDetails()
private var subs = Set<AnyCancellable>()

override func viewDidLoad() {
    super.viewDidLoad()

    details
        .heading
        .assign(to: \.text!, on: heading)
        .store(in: &subs)
}

and in LRVCoordinator.swift I have

private var details = LRVDetails()

func UserDidPressButton() { // this is a delegate method from the VC
    details.heading.send("New Heading")
}

I thought that because LRVDetails is a class, it only stores a reference. so if I simply instantiate it in each file and then call send() on it, the publisher should update, and then emit the value to the LRVViewController subscription and update the heading.

But it does nothing. The subscriber never receives a value ( I checked with a .print() operator ). When i call lrvController?.details.send() -- without the private tag -- in the coordinator, it works fine.

Is this because the subscriber is stored in the 'subs' variable in LRVCoordinator, and thus it has to be updated with the subscriber in LRVViewController? That's my best bet.

If not, why doesn't this work?.

Thanks!

Edit:

If they are different instances, why does this code print two?

class myClass {
 var int = 1
}

let test1 = myClass()
let test2 = myClass()

test1.int = 2
print(test2.int)
// prints 2

Solution

  • I thought that because LRVDetails is a class, it only stores a reference. so if I simply instantiate it in each file and then call send() on it, the publisher should update

    Your understanding is incorrect. You have created two different LRVDetails objects. (The syntax LRVDetails() creates a new object) One in the VC, and the other in the coordinator. You are correct that it stores references though, so it would work if you actually make the two details variable refer to the same LRVDetails.

    But actually, you don't seem to need a details variable in the coordinator. Since the coordinator is the delegate of the VC, you could just pass the LRVDetails via the delegate method:

    func UserDidPressButton(_ viewController: LRVViewController) {
        viewController.details.heading.send("New Heading")
    }
    

    Needless to say, you should also change the caller of UserDidPressButton to pass self as the parameter.