1

What is wrong with the following test code ? When I enter a character into the field, the didSet goes into a recursive loop. If the inout + & are removed, the code functions as expected with didSet being triggered for each keystroke. Similarly, if I remove the @Published, but leave the inout and & the didSet recursion does not occur.

Why am I trying to do this? I have a form field type (a 4 char hex field) that requires common processing which is then re-encoded back to a base structure. Thus, the intent, is to abstract all the common code into a function that was triggered after each var had been set in the ModelView zone. This code is just a minimal example to reproduce the issue.

It looks like merely taking the address of an @published var triggers the associated didSet. An interpretation of this would be that using inout always re-writes the target var even if no change is made.

class A: ObservableObject
{
  @Published var publishedVar = "abc" {
    didSet {
      print("didSet Triggered")
      doNothing(commonText: &publishedVar)
    }
  }
  
  private func doNothing(commonText: inout String)
  {
  }

}

struct ContentView: View {
  @ObservedObject var a = A()
    var body: some View {
      TextField("dummy", text: $a.publishedVar)
    }
}

In case it is relevant this is being run on Mac running Catlina as a Mac App (not IOS, or emulators). Xcode Version 12.4 (12D4e)

OK, I've tried done reading on sink and tried to follow the advice (updated class A below) and now I find that it is recursing within the doNothing function. Looks like I am missing something basic 8-(

class A: ObservableObject
{
  var cancellable: AnyCancellable?
  @Published var publishedVar: String = "def"
  
  private func doNothing(commonText: inout String)
  {
    print("In doNothing \(commonText)")
  }

  init()
  {
    cancellable = $publishedVar.sink { value in
      self.doNothing(commonText: &self.publishedVar)
      print("saw sink \(value)")
    }
  }
}

2 Answers 2

1

The recursion will occur because of the inout argument to doNothing. Each time it is called it will set the result back to publishedVar.

Please also note, that $publishedVar.sink will be activated on willSet, not didSet, and so is not interchangeable with your original code.

Sign up to request clarification or add additional context in comments.

Comments

0

If you want to be notified when a Published variable changes you shouldn’t be using didSet instead you should use combine and observe the variable.

I.E

$publishedVar.sink {
    print("didSet Triggered")
    doNothing(commonText: &publishedVar)
}...

7 Comments

Being very new to Combine....can you please elaborate as to where in the code should that line go?
This would go in the init of your class A. Apologies on a mobile device now so not easy to update my example above.
Thanks, updated the class as shown above. Now recursing on doNothing. Am I following you advice correctly?
I would guess that the issue is with the inout parameter. Maybe marking is as inout „sets“ it so the didChange will be triggered? Try to see if it works when you don‘t mark it as inout
Yes, the inout is the issue. However, the address is needed if the extracted common code is to optionally update the reference. I expected recursion (needed) when updating the value. What has raised my WT# response is that merely referencing the address without any assignment is triggering recursion to occur.
|

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.