Mostafa
Mostafa

Reputation: 1612

How to know what changed variable value in xcode swift

I have this case where i have a static variable that changes its value somewhere across the app which causing a bug. Without going into details of the code. I need to know if there is a way to to watch this variable. Because on Xcode every time try to watch a variable it gives me this error "error: no variable named 'backend' found in this frame".

So my question is, Is there is any workaround to know which place did change the value of this variable.

Anything other than using watches because its not working for me for some reason with the error mentioned above.

Upvotes: 1

Views: 2060

Answers (2)

nhgrif
nhgrif

Reputation: 62062

You can add a property observer to the variable (either didSet or willSet) and either set a breakpoint there and look at the frame when the breakpoint is hit, or you could log the current stack trace for the current thread.


For the first example, of a simple breakpoint in the property observers, I have set up the following very simple class:

class Observable {
    static var someProperty: String? {
        willSet {
            print("Some property will be set.")
        }
        didSet {
            print("Some property has been set.")
        }
    }
}

With breakpoints:

enter image description here

I'm simply running this code from a unit test, but you will get the same results anywhere. This is the code I'm triggering the changes with:

class ObservableTests: XCTestCase {
    func testObservable() {
        Observable.someProperty = "foo"
        Observable.someProperty = "bar"
        Observable.someProperty = "baz"
    }
}

When the code runs, we'll stop right here at the break point:

enter image description here

I've framed this screenshot specifically as such. We see the green line and the blue arrow highlighting the breakpoint we are stopping at, but look in the left. We can see which thread we're on and how we got to this specific point. Importantly, we can click up and down through the stack.

Here, most of the frames in the stack just show us mostly unhelpful assembly code. This is what we see if we look up one frame:

enter image description here

But if we go up one more frame, we get to the code that modified our variable:

enter image description here

To be explicitly clear, the section I'm clicking through to jump to the different parts of the code that lead down to this variable being set is right here:

enter image description here


As mentioned, printing the stack trace is also an option, although it's significantly less effective.

If we change our willSet and didSet to look more like this:

class Observable {
    static var someProperty: String? {
        willSet {
            for symbol: String in NSThread.callStackSymbols() {
                print(symbol)
            }
        }
        didSet {
            for symbol: String in NSThread.callStackSymbols() {
                print(symbol)
            }
        }
    }
}

then we will be able to see the full stack trace printed in the log, as shown here:

enter image description here

The lines of interest from the above screen shot are these two:

2   TTDSamplesTests                     0x00000001087603fa _TFC15TTDSamplesTests15ObservableTests14testObservablefS0_FT_T_ + 74
3   TTDSamplesTests                     0x0000000108760492 _TToFC15TTDSamplesTests15ObservableTests14testObservablefS0_FT_T_ + 34

It's hard to decipher, but you can pick the parts out of it.

_TFC15TTDSamplesTests15ObservableTests14testObservablefS0_FT_T_

  • The name of the target I ran this code in is TTDSamplesTests.
  • The name of the class the method is in is called ObservableTests.
  • The name of the method that called the setter is called testObservable.

Upvotes: 6

user887210
user887210

Reputation:

One thing you can do is replace your variable with a computed property and set a breakpoint in the setter:

class Foo {
  static var bar:Int = 0
}

class FooToo {
  static var _bar:Int = 0

  static var bar:Int {
    set {
      // put break point here
      print("FooToo bar is set")
      FooToo._bar = bar
    }
    get {
      return FooToo._bar
    }
  }
}

Foo.bar = 10
FooToo.bar = 10 // -> prints "FooToo bar is set"

Upvotes: 0

Related Questions