0

I have an @Published field that dictates whether the user is currently in offline mode.

class RequestStatus: ObservableObject {
    
    static var shared = RequestStatus()
    
    @Published var offlineModeActive: Bool = false
    
    private init() {}
    
}

Throughout my app, I use the Combine framework to subscribe to changes in this field and perform some work.

import Combine

class ExampleClass {
        
     var offlineModeCancellable: AnyCancellable

     init() {

          self.offlineModeCancellable = RequestStatus.shared.$offlineModeActive.sink(receiveValue: { offlineModeActive in
                
               // Perform work
                
          })
     }
}

This works fine, however, when I run unit tests I do not want changes on this field to be published because I want to set up the environment of the test manually.

Therefore, I added in a field called shouldPublishChanges that defines whether changes should be published. If this is set to true, only then will I manually call the objectWillChange.send() method.

class RequestStatus: ObservableObject {
    
    static var shared = RequestStatus()
    
    var shouldPublishChanges: Bool = false
    
    var offlineModeActive: Bool = false {
        willSet {
            if shouldPublishChanges {
                objectWillChange.send()
            }
        }
    }
    
    
    private init() {}
    
}

I then updated my Combine subscriber to subscribe to changes on the RequestStatus.shared.objectwillChange value meaning it should only receive a value when shouldPublishChanges is set to true (or so I thought).

import Combine

class ExampleClass {
        
     var offlineModeCancellable: AnyCancellable

     init() {

          self.offlineModeCancellable = RequestStatus.shared.objectWillChange.sink(receiveValue: {
                
               // Perform work
                
          })
     }
}

However, even though shouldPublishChanges is set to shouldPublishChanges, if I change the offlineModeActive value, the value is published and the subscriber still receives the update and performs the work.

So, how do I conditionally publish changes to a Combine subscriber?

The only other idea I have is to have one field that stores the value and another field for the publisher. Then, only when I want to publish changes to this field will I update the published value.

import Combine

class RequestStatus: ObservableObject {
    
    private init() {}
    
    static var shared = RequestStatus()
    
    var shouldPublishChanges: Bool = false
    
    private(set) var offlineModeActivePublisher = CurrentValueSubject<Bool, Never>(false)
    
    private(set) var offlineModeActive: Bool = false
    
    func updateOfflineModeActive(offlineModeActive: Bool) {
        
        self.offlineModeActive = offlineModeActive
        
        if shouldPublishChanges {
            offlineModeActivePublisher.value = offlineModeActive
        }
        
    }
    
}

However:

  1. Requires a lot of boiler-plate code for each field I want to do this for

  2. I have two values that can be out of sync (this is less of a concern as shouldPublishChanges is set once and never changed)

  3. I feel like there is a better way of achieving the same thing.

Any ideas?

Alex Marchant
  • 570
  • 6
  • 21
  • Can’t you just avoid changes on that published value? Where are those happening? Otherwise can’t you inject a mock in the test for this class, so that the mock would not produce values at all for this publisher? – valeCocoa Nov 18 '21 at 01:45
  • @valeCocoa I need to set up the environment (for instance in offline mode) to check certain code paths in the tests, so the published values have to be changed. ```RequestStatus``` is a singleton containing other published variables that get changed throughout the codebase as they have to be shared. I guess I could mock the singleton and store a variable on any class that uses it... – Alex Marchant Nov 18 '21 at 14:31
  • You might as well keep `RequestStatus` as a singleton with a `shared` or `default` or `current` shared unique static instance, and also make it possible to instantiate configurable instances. – valeCocoa Nov 18 '21 at 16:56

0 Answers0