I'm currently learning ios developement using swift and I was just wondering if there is a way in xcode to
1) set a breakpoint on a variable whenever the value of the variable changes
OR
2) somehow track the change to variable value over time
Method with Swift didSet and willSet
You can use the print in the console:
class Observable {
static var someProperty: String? {
willSet {
print("Some property will be set.")
}
didSet {
print("Some property has been set.")
}
}
}
Method with watchpoints
Watchpoints are a tool you can use to monitor the value of a variable
or memory address for changes and trigger a pause in the debugger when
changes happen. They can be very helpful in identifying problems with
the state of your program that you might not know precisely how to
track down.
You can find a great guide here
I think you should learn about willSet and didSet concepts.
Swift has a simple and classy solution called property observers, and it lets you execute code whenever a property has changed. To make them work, you need to declare your data type explicitly, then use either didSet to execute code when a property has just been set, or willSet to execute code before a property has been set.
Update the value whenever the value was changed. So, change property to this:
var score: Int = 0 {
didSet {
scoreLabel.text = "Score: \(score)"
}
}
There is already a good question and answers which expalin this concept.
What is the purpose of willSet and didSet in Swift?
Related
So I'm digging into combine and this question came up.
Is there any real difference between using CurrentValueSubject (and setting its value using currentValueSubject.value) or using a #Published var and accessing its publisher with a $? I mean I know one returns a Subject instead of a Publisher, but the only real difference I could find is that CurrentValueSubject is way more useful because you can declare it on a protocol.
I really don't understand how #Published can be useful if we can just use PassthroughSubject, am I missing something here?
Mind you, this is using UIKit, it may have other uses for SwiftUI.
Thank you.
CurrentValueSubject is a value, a publisher and a subscriber all in one.
Sadly it doesn’t fire objectWillChange.send() when used inside an ObservableObject.
You can specify an error type.
#Published is a property wrapper, thus:
It is not yet supported in top-level code.
It is not supported in a protocol declaration.
It can only be used within a class.
#Published automatically fires objectWillChange.send() when used inside an ObservableObject.
Xcode will emit a warning if your try to publish to #Published wrapped property from a background queue. Probably because objectWillChange.send() must be called from the main thread.
The error type of its publisher is Never
My biggest beef against #Published is that it can’t behave as a subscriber and setting up Combine pipelines requires additional plumbing compared to a Current Value Subject.
We can declare a #Published property inside a protocol. Not very pretty...
protocol TestProtocol {
var isEnabled: Bool { get }
var isEnabledPublished: Published<Bool> { get }
var isEnabledPublisher: Published<Bool>.Publisher { get }
}
class Test: ObservableObject, TestProtocol {
#Published var isEnabled: Bool = false
var isEnabledPublished: Published<Bool> { _isEnabled }
var isEnabledPublisher: Published<Bool>.Publisher { $isEnabled }
}
#Published is just a quick way to use CurrentValueSubject a little neater. When I debug one of my apps and look at the type returned by $paramName , it's actually just a CurrentValueSubject:
po self.$books
▿ Publisher
▿ subject : <CurrentValueSubject<Array<Book>, Never>: 0x6000034b8910>
I guess one benefit of using CurrentValueSubject instead of #Published may be to allow you to use the error type?
Note: Despite being a CurrentValueSubject right now I'd never rely on that assumption.
I found myself coming back to this post so felt I'd add some extra insight in to the difference between #Published and CurrentValueSubject.
One main difference can be found in the documentation for #Published:
When the property changes, publishing occurs in the property’s willSet block, meaning subscribers receive the new value before it’s actually set on the property.
Additionally, conversation on the Swift Forums note that #Published is intended for use with SwiftUI.
With regards to #Published publishing in the willSet block of it's property, consider the following example:
class PublishedModel {
#Published var number: Int = 0
}
let pModel = PublishedModel()
pModel.$number.sink { number in
print("Closure: \(number)")
print("Object: \(pModel.number) [read via closure]")
}
pModel.number = 1
print("Object: \(pModel.number) [read after assignment]")
This produces the following output:
Closure: 0
Object: 0 [read via closure]
Closure: 1
Object: 0 [read via closure]
Object: 1 [read after assignment]
Contrast this with another example where we keep everything the same, except replacing #Published with CurrentValueSubject:
class CurrentValueSubjectModel {
var number: CurrentValueSubject<Int, Never> = .init(0)
}
let cvsModel = CurrentValueSubjectModel()
cvsModel.number.sink { number in
print("Closure: \(number)")
print("Object: \(cvsModel.number.value) [read via closure]")
}
cvsModel.number.send(1)
print("Object: \(cvsModel.number.value) [read after assignment]")
Output:
Closure: 0
Object: 0 [read via closure]
Closure: 1
Object: 1 [read via closure] // <— Here is the difference
Object: 1 [read after assignment]
After updating number to 1, reading the object's CurrentValueSubject's value property within the closure prints the new value instead of the old value as with #Published.
In summary, use #Published within your ObservableObjects for your SwiftUI views. If you're looking to create some sort of model object with an instance property that holds a current value and also publishes it's changes after they are set, use CurrentValueSubject.
One advantage on #Published is that it can act as a private-mutable, public-immutable CurrrentValueSubject.
Compare:
#Published private(set) var text = "someText"
with:
let text = CurrentValueSubject<String, Never>("someText")
When designing APIs you often want to allow clients to read the current value and subscribe to updates but prevent them from setting values directly.
There is one limitation when using #Published.
You can only use #Published on properties of a Class whereas CurrentValueSubject can be used for struct as well
I have a subclass of NSManagedObject Folder with a state of Availability
#objc enum Availability: Int16 {
case unknown
case available
case unavailable
}
Folder has to do extra stuff (like delete related files) whenever it's availability changes. So I have
internalAvailability saved in core data
Computed property availability using above property
`
extension Folder {
#NSManaged private var internalAvailability: Availability
}
extension Folder {
private func deleteFiles(...) {
...
}
#objc dynamic public var availability: Availability {
get {
return internalAvailability
}
set {
willChangeValue(forKey: "availability")
deleteFiles()
internalAvailability = newValue
didChangeValue(forKey: "availability")
}
}
}
Using Reactive, I want to change navigation item's title based on availability but the signal is never called after once!
```
let property = DynamicProperty<NSNumber>(object: folder, keyPath: "availability")
internalVariable = property // To have a reference of property
navigationItem.reactive.title <~ property.map { (stateNumber) -> String in
guard let a = Availability(rawValue: stateNumber.int16Value) else {
assertionFailure()
return ""
}
let prefix = a == .available ? "" : "(Nope) "
return "\(prefix)\(folder.name)"
}
I have explicitly added KVO compliance to the property in hopes that this starts working, but alas no results.
Edit: if I create the DynamicProperty on internalAvailability instead of availability, everything works smoothly..
Adding as an answer since it became a learning exercise. Hopefully someone else too would be benefitted.
The app uses a multiple managedObjectContext(moc) architecture. 1 private moc to make changes and 1 main thread moc that synchronises itself using mergeChanges.
In above code, navigationItem is using the folder instance kept with main-moc. The DynamicProperty is listening to KVO changes on this main-moc's folder instance. Let's call this main-folder. When I make changes, I modify the folder instance we have on private-moc. Let's call it private-folder.
On modifying private-folder and calling save on private-moc, a notification of name NSManagedObjectContextDidSave is broadcasted. main-moc synchronizes itself using mergeChanges.
mergeChanges changes main-folder, but notice that it would never call the computed-property-setter availability. It directly changes internalAvailability.
And thus, no KVO notifications are posted of our computed property.
TL;DR When doing KVO on a NSManagedObject subclass, use a stored property instead of computed one. In case you have a multi-moc (managed object context) scenario and use mergeChanges to synchronise, setter for your computed property is not called when synchronising.
Edit (Solution): add method of the pattern keyPathsForValuesAffecting<KeyName> KVO relevant documentation
#objc class func keyPathsForValuesAffectingAvailability() -> Set<NSObject> {
return [#keyPath(Folder.internalAvailability) as NSObject]
}
When using Core Data we use the NSManagedObjectContextObjectsDidChange notification instead of KVO. This brings many advantages including coalescing of change events and undo support. If we need to know what attributes changed on an object we can examine changedValuesForCurrentEvent which even includes transient attributes that have a matching keyPathsForValuesAffecting.... These advantages likely outweigh those from a KVO binding framework aka reactive.
I have been working with willSet and didSet for a variable for a while. Its used to get notified and perform some action before and after a variable is changed.
I recently came across KVO which does similar thing, but with few more steps to setup.
Is there any difference between these two approaches?
Does one have any advantage over the other?
KVO is Objective C approach to observe properties is based on dynamic dispatch
While didSet/willSet are pure Swift methods which doesn't exist in ObjC
The latter in theory should be more efficient since Swift is trying to use static dispatch where possible for performance gains.
I'd go with ObjC approach only if you need compatibility with some ObjC dependencies or legacy code.
KVO lets you implement the common “Observer” pattern. With KVO, you can attach any number of observers to a property, at run time, without modifying the source code of the property you're observing.
Swift's willSet and didSet “observers” are essentially functions that are called before and after a property is set, and have to be written into the property's source code at compile time. They serve a very different purpose than KVO.
KVO : it is a mechanism that allows one object to observe properties of another object. KVO allows objects to be notified when the state of another object changes
willSet is called just before the value is stored.
didSet is called immediately after the new value is stored.
Example:
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
As a preface, this might be an incredibly simple and/or ignorant question.
In ReactiveCocoa 2.x, we were able to use RACObserve and RAC to observe properties of an object. From the documentation I can find in Reactive 3 and 4, we now use PropertyType to observe changes to an object property. I have so far been unsuccessful in being able to observe any property change when using MutableProperty or DynamicProperty.
class TempObject {
var property: String
}
let tempObject = TempObject()
let propertyObserver: MutableProperty<String> = MutableProperty(tempObject.property)
From what I understand, I should be able to use propertyObserver to view changes to tempObject.property. I tried adding a map function to the signal producer from propertyObserver to see if it was firing, but don't see anything when updating tempObject.property. Again, could be a trivial thing that I am missing, thanks so much.
Edit
NachoSoto nailed it - I needed to make my property KVO compliant. I also ended doing this:
let tempObjectSignal: MutableProperty<TempObject> = MutableProperty(tempObject)
let propertyObserver: MutableProperty<String> <~ tempObjectSignal.producer.map({ $0.property })
And whenever tempObject.property is updated I make sure to call
tempObjectSignal.value = tempObject
This fires off all the necessary signals. I don't know if this breaks any best practices, though. Let me know what you think!
MutableProperty(value) creates a mutable property but only with value that as the initial value.
What you want to use is DynamicProperty, which will use the Objective-C runtime and KVO to detect changes to an object's property:
let property = DynamicProperty(tempObject, "property")
For that reason, however, you need to make sure that the property you want to observe is part of the Objective-C runtime, by making the class a subclass of NSObject, and by either using the dynamic keyword:
class TempObject: NSObject {
dynamic var property: String
}
Or using #objc to ensure that it gets exported to the runtime:
class TempObject: NSObject {
#objc var property: String
}
I would like to set a willSet statement to isResting property. Whenever isResting is changed some function is called. This is what I mean:
starNode.physicsBody?.isResting: Bool = false{
willSet{
if newValue == true{
print("GOOGOO")
}else{
print("STOP")
}
}
}
This is not working. Can you assign a willSet or didSet to an already defined iOS property? If not, how do I get similar effect?
This is the documentation:
https://developer.apple.com/library/mac/documentation/SceneKit/Reference/SceneKit_Framework/index.html#//apple_ref/doc/uid/TP40012283
https://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNPhysicsBody_Class/index.html#//apple_ref/occ/instp/SCNPhysicsBody/isResting
If not read-only:
One option would be to subclass the library object and override the property you'd like to set. Then, you can implement willSet within your subclass:
class Object {
var property: Float = 0.0
}
class ChildObject: Object {
override var property: Float {
willSet {
print("Set in child class")
}
}
}
This is the way that Apple recommends in the documentation (see "Overriding Property Observers"):
You can use property overriding to add property observers to an
inherited property. This enables you to be notified when the value of
an inherited property changes, regardless of how that property was
originally implemented.
If read-only:
You might be able to use Key-Value Observing, if the object inherits from NSObject, or if it has been marked dynamic in Swift.
Is this property an IBInspectable property?
In Objetive-C this is automatically if this is a property trough getter and setter, but in Swift it isn't. You can make this property as private and create methods for set this property and inside this method, add a call for another method.
func setResting(resting:Bool) {
self.isResting = resting
//call another method here
}