What's the main difference between property observers and property wrappers? - ios

What's the main difference between property observers and property wrappers? They seem to be very similar in that they manage how the properties are stored. The only thing I can think of is that you can reuse property wrappers since there is a layer of separation between code that manages how a property is stored and the code that defines a property.
Property Wrapper
#propertyWrapper
struct TwelveOrLess {
private var number: Int
init() { self.number = 0 }
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
struct Rectangle {
#TwelveOrLess var height: Int
#TwelveOrLess var width: Int
}
Property Observer
struct Rectangle {
var height: Int {
didSet {
if oldValue > 12 {
height = 12
} else {
height = oldValue
}
}
}
var width: Int {
didSet {
if oldValue > 12 {
width = 12
} else {
width = oldValue
}
}
}
}
The two cases above accomplish pretty much the same thing, which is to set the properties to be equal to or less than 12.

You say:
The only thing I can think of is that you can reuse property wrappers since there is a layer of separation between code that manages how a property is stored and the code that defines a property.
Your example (and some of your text) appears to be lifted from the Swift Programming Language: Property Wrapper manual:
A property wrapper adds a layer of separation between code that manages how a property is stored and the code that defines a property. For example, if you have properties that provide thread-safety checks or store their underlying data in a database, you have to write that code on every property. When you use a property wrapper, you write the management code once when you define the wrapper, and then reuse that management code by applying it to multiple properties.
So, yes, the virtue of the property wrapper is the reuse achieved by separating the “code that manages how a property is stored and the code that defines a property.” This resulting reuse is the whole m.o. of property wrappers.
You clearly, you can write your own setters and getters (which is better, IMHO, than a pattern of writing an observer that mutates itself), too, but you lose the reuse and abstraction that the property wrappers offer.
You go on to say:
The two cases above accomplish pretty much the same thing, which is to set the properties to be equal to or less than 12.
Sure, but if you want to do this for ten different properties, the wrapper avoids you from needing to repeat this code ten times. It also abstracts the details of this “equal to or less than 12” logic away from where you declare the property.

Another big difference betwixt property observers and property wrappers is that property observers can access self, whilst property wrappers cannot yet (as of this writing) access self using a stable, documented interface.
You can work around this limitation by manually passing self to the property wrapper in init. This workaround is described in the property wrapper proposal.
You can access self in a property wrapper using an undocumented, unstable interface which you can learn about by typing “property wrapper _enclosingInstance” into your favorite search engine.

Related

Singleton variable not updating

The value of the variable 'switcheroo' in the view controller below is always the same when I attempt to access it via a singleton. I am trying to access its value from a custom label class that prints the characters of the label one by one. When the label is set, I try to get the updated value of switcheroo in the Viewcontroller singleton. However it always returns the initial value of switcheroo, not the updated value (which I can trace in the viewcontroller). Am I doing something wrong?
class TheViewController: UITableViewController, UIGestureRecognizerDelegate, UITabBarControllerDelegate {
static let shared = TheViewController()
var switcheroo = 0
... various operations that change the value of switcheroo...
}
class CustomLabel: UILabel {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override var attributedText: NSAttributedString? {
didSet {
DispatchQueue.main.async {
let characterDelay = TimeInterval(0.01 + Float(arc4random()) / Float(UInt32.max)) / 100
for (index, _) in attributedText.string.enumerated() {
DispatchQueue.main.asyncAfter(deadline: .now() + characterDelay * Double(index)) {
print("switcheroo value in TheViewController is now: \(TheViewController.shared.switcheroo)")
super.attributedText = attributedText.attributedSubstring(from: NSRange(location: 0, length: index+1))
}
}
}
}
I would not suggest making a view controller a singleton solely for the purpose of some shared state or model property. View controllers have their own life cycle patterns (e.g. instantiated from storyboard scenes, released when they are dismissed, recreated when presented again, etc.), and you’re likely to have issues arising from dealing with separate instances of your view controller(s).
Instead, don’t try to fight the standard view controller life cycle, but rather just move this property into a separate object, e.g.
final class StateManager {
static let shared = StateManager()
var switcheroo = 0
private init() { }
}
Then your view controllers can use that:
class ViewController: UIViewController {
...
func examineSwitcheroo() {
print(StateManager.shared.switcheroo)
}
func updateSwitcheroo(to value: Int) {
StateManager.shared.switcheroo = value
}
}
This way, you enjoy this shared state, without entangling normal view controller life cycles in this process.
Now, what the right name for this singleton, StateManager in my example, depends entirely upon what this shared property is. But there’s not enough information about what this switcheroo object really is to offer better counsel on this matter.
And, probably needless to say, it’s a separate question as to whether you really should be using singletons at all for state variables and model objects, but that’s beyond the scope of this question.
If you have determined that having a ViewController singleton is the right decision, the likely answer is that you are not using that shared instance every time, instead accidentally calling the initializer at some point in your project (possibly Xcode is doing it automatically through interfaces).
To search through your entire project, you can use cmd + shift + F and then type TheViewController(). There should only be one occurrence (the shared instance). Be sure to also check for TheViewController.init(). That will find any time you do it.
If the issue persists, perhaps try setting the shared instance to self in the viewDidLoad method of TheViewController?
Hope this helps!
Don't manage your application's data in your view controller(s). The Cocoa and Cocoa Touch frameworks use the MVC paradigm, where the M is meant to stand for model, i.e. the application's data model. Any data that needs to be preserved, or that's relevant beyond the scope of the view controller, should be stored and managed in a model object. If you give your view controller's a reference to the model when you create them, you never need to worry about passing data from one view controller to another; instead, they each operate on the model, and any data they need comes from the model.

How to init my subclass with an instance of its superclass?

In my app I read calendar events of type EKEvent, and I've made an extension with a lot of computed vars so I can easily get the duration, number of man-hours etc. for each event in the calendar. But in large scale, the performance is bad - so I want to use lazy vars instead, to cache all my extra data.
Therefore, I want to make a subclass of EKEvent - called CustomEvent, which adds the lazy vars, but my problem is that the EKEventStore always returns EKEvents, and I need to convert that to instances of my CustomEvent subclass, in order to be able to access the lazy vars etc.
A simple typecast is not enough, and I've tried in a playground, to see what could work, but got nothing useful. I need a special constructor for CustomRectangle, which can initialize a CustomRectangle from a NativeRectangle. An alternative solution is to make a wrapper class that holds the original object as a property, but that wouldn't be my favorite solution, since I'd then have to map all methods and properties
class NativeRectangle: NSObject {
var width: Int
var height: Int
init(width: Int, height: Int) {
self.width = width
self.height = height
super.init()
}
}
class CustomRectangle: NativeRectangle {
var area: Int { return width * height}
}
let rect = NativeRectangle(width: 100, height: 20)
let customRect = CustomRectangle(rect) // This fails, i need a constructor
print(customRect.area)
There is no way in Swift (and in general in most Object Oriented languages) to use an existing instance of a base class object when creating a child class instance.
From a general programming stand-point you have the two options in this situation:
Use composition: Make the CustomRectangle contain a NativeRectangle and forward all methods to it that you need.
Use a map to link NativeRectangles to additional information. In Objective C and Swift you can you objc_AssociationPolicy to have such an internal map most easily. See https://stackoverflow.com/a/43056053/278842
Btw. There is no way that you will see any speed-up from "caching" a simple computation as width * height.
If you already work in the Objective-C land, there’s an option to wrap the native class and forward all (except the added) messages automatically:
- (NSMethodSignature*) methodSignatureForSelector: (SEL) selector
{
NSMethodSignature *ours = [super methodSignatureForSelector:selector];
return ours ?: [wrappedObject methodSignatureForSelector:selector];
}
I can’t remember if this is everything that was needed for the forwarding to work, but it should be pretty close. Also, I don’t know how this would play with Swift, so I guess we could consider this an interesting piece of trivia from the Objective-C days and look for a better solution…
A second, also slightly hacky option that comes to mind is using the associated objects feature to link the cached data to the original instance. That way you could keep your extensions approach.
You created your own CustomRectangle(object: rect) , so swift will not provide default init() any more. You explicitly need to call one of your own holding your property and make call to super.init(), as your class also inherits from super class. –
class NativeRectangle: NSObject {
var width: Int
var height: Int
// Super class custom init()
init(width: Int, height: Int) {
self.width = width
self.height = height
super.init()
}
}
class CustomRectangle: NativeRectangle {
// computed property area
var area: Int { return width * height}
// Sub class Custom Init
init(object:NativeRectangle) {
// call to super to check proper initialization
super.init(width: object.width, height: object.height)
}
}
let rect = NativeRectangle(width: 100, height: 20)
let customRect = CustomRectangle(object: rect)
print(customRect.area) //2000

Swift: property observers for computed properties

As far as I know, Swift allows us to set property observers for either stored and computed properties. But if computed property value depends on some backing store, property observers are not fired when these backing store values are changed:
public class BaseClass {
private var privateVar1: Int = 0
private var privateVar2: Int = 0
public var property: Int {
get {
return privateVar1 * privateVar2
}
set {
print("some setter without effect")
}
}
private func changeSomeValues() {
privateVar1 = 1
privateVar2 = 2
}
}
public class SubClass : BaseClass {
override var property: Int {
didSet {
print("didSet \(property)")
}
}
}
didSet of SubClass isn't called when changeSomeValues is called.
Let's consider a case: we have such BaseClass in a third-party framework. We define SubClass in our app. The question is: how can we rely on SubClass observers without knowledge about property nature: is it stored (and we can rely on observers) or computed (and then we can't expect firing observers each time when we expect it)? Is it possible? If no, is it an incapsulation violation?
That behaviour is perfectly normal. There is no way for the compiler to know which backing store really corresponds to which computed property. Your backing store in this case is made up of private variables that will not be accessible outside the class itself. So the only place where an "under the hood" change can occur is in the base class. It is that class's prerogative to use its calculated properties (which will trigger the observers) or the backstore (which will not).
In your example, assuming you never want to allow "invisible" changes, the changeSomeValues() function is breaking its own rules and not respecting the contract it promised to its subclasses and callers.

Observing object properties with ReactiveCocoa 4 in Swift

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
}

initializing class properties before use in Swift/iOS

I'm having trouble grasping the proper way of instantiating variables that always need to be set before an object is fully functional but may need to be instantiated after the constructor. Based on Swift's other conventions and restrictions it seems like there is a design pattern I'm unaware of.
Here is my use case:
I have a class that inherits from UIViewController and will programmatically create views based on user actions
I need to attach these views to this class, but to do so I need to retrieve their content based on configuration data supplied by another controller
I don't care if this configuration data is passed to the constructor (in which case it would always be required) or supplied by a secondary call to this object before it is used
My problem seems to be that both of the approaches in bullet 3 seem flawed.
In the first case, there is only one legitimate constructor this class can be called with, yet I'm forced to override other constructors and initialize member variables with fake values even if the other constructors are never intended to be used (I'm also trying to keep these variables as let types based on Swift's best practices).
In the second case, I'm effectively splitting my constructor into two parts and introduce an additional point of failure in case the second part fails to be called prior to class being used. I also can't move this second part to a method that's guaranteed to be called prior to usage (such as viewDidLoad) because I still need to pass in additional arguments from the config. While I can make sure to call the initPartTwo manually, I'd prefer to have a mechanism that better groups it with the actual constructor. I can't be the first one to run into this and it seems like there is a pattern I'm not seeing to make this cleaner.
UPDATE:
I ended up going with a modified version of the pattern matt suggested:
struct Thing {
let item1: String
let item2: String
struct Config {
let item3: String
let item4: String
}
var config:Config! {
willSet {
if self.config != nil {
fatalError("tried to initialize config twice")
}
}
}
init() {
self.item1 = ...
self.item2 = ...
...
}
public func phaseTwoInit(item3: String, item4: String) {
self.item3 = item3
self.item4 = item4
...
}
}
var t = Thing()
...
t.phaseTwoInit(...)
...
// start using t
If an initial instance variable property value can't be supplied at object initialization time, the usual thing is to declare it as an Optional. That way it doesn't need to be initialized by the class's initializers (it has a value - it is nil automatically), plus your code subsequently can distinguished uninitialized (nil) from initialized (not nil).
If the Optional if an implicitly unwrapped Optional, this arrangement need have no particular effect on your code (i.e. it won't have to be peppered with unwrappings).
If your objection is that you are forced to open the door to multiple settings of this instance variable because now it must be declared with var, then close the door with a setter observer:
struct Thing {
var name:String! {
willSet {
if self.name != nil {
fatalError("tried to set name twice")
}
}
}
}
var t = Thing()
t.name = "Matt" // no problem
t.name = "Rumplestiltskin" // crash

Resources