Passing a value type as reference in Swift - swift

I have my model implemented as structs in Swift 3.0. Several of these structs have delegates that should be able to modify the model depending on the user's actions.
However, when I pass the struct to the delegate method, it gets copied.
How do you solve this? Can you force the compiler to pass this struct as a reference, or the only option is to use a class?

structs are always passed by value. The whole point of using a struct is to have it behave as a value type. If you need delegation (which usually implies mutable state), you should be using a class.
If you really really need to, you could force pass-by-reference by using an inout parameter, but that is not recommended in general. You could also use a box type to simulate passing by reference. But, in general, you should just use a class if you need reference behavior.

The whole point of using struct in the first place is that this is desirable behavior. It preserves the immutability of the data. inout can achieve this, but it's not recommended in the general case.
protocol Delegate {
func callback(_ oldValue: Int) -> Int
}
struct IncrementerDelegate: Delegate {
let step: Int
func callback(_ oldValue: Int) -> Int {
return oldValue + step
}
}
struct Model {
var i = 0
}
class Controller {
var model = Model()
var delegate: Delegate
init(delegate: Delegate) {
self.delegate = delegate
}
// To be called externally, such as by a button
func doSomething() {
// Delegate determains new value, but it's still the
// controller's job to perform the mutation.
model.i = delegate.callback(model.i)
}
}
let delegate = IncrementerDelegate(step: 5)
let controller = Controller(delegate: delegate)
print(controller.model.i)
controller.doSomething() // simulate button press
print(controller.model.i)
protocol CrappyDelegate {
func callback(_ model: inout Model)
}
struct CrappyIncrementerDelegate: CrappyDelegate {
let step: Int
func callback(_ model: inout Model) {
model.i = 9999999
// Just hijacked the models value,
// and the controller can't do anything about it.
}
}
class VulnerableController {
var model = Model()
var delegate: CrappyDelegate
init(delegate: CrappyDelegate) {
self.delegate = delegate
}
// To be called externally, such as by a button
func doSomething() {
// Controller leaks entire model, and has no control over what happens to it
delegate.callback(&model)
}
}
let crappyDelegate = CrappyIncrementerDelegate(step: 5)
let vulnerableController = VulnerableController(delegate: crappyDelegate)
print(controller.model.i)
controller.doSomething() // simulate button press
print(controller.model.i) // model hijacked

If you want to pass by reference, you should generally use a class not a struct.
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html states:
You can use both classes and structures to define custom data types to
use as the building blocks of your program’s code.
However, structure instances are always passed by value, and class
instances are always passed by reference. This means that they are
suited to different kinds of tasks. As you consider the data
constructs and functionality that you need for a project, decide
whether each data construct should be defined as a class or as a
structure.

Related

Memory access conflict with structs and observer pattern

I'm implementing an Observer design pattern on a Struct model object. The idea is that I will pass my model down a chain of UIViewController and as each controller modifies it, previous controllers will also be updated with changes to the object.
I'm aware this problem could be solved by using a class instead of struct and modifying the object directly through reference, however I'm trying to learn more about using structs.
struct ModelObject {
var data: Int = 0 {
didSet {
self.notify()
}
}
private var observers = [ModelObserver]()
mutating func attachObserver(_ observer: ModelObserver){
self.observers.append(observer)
}
private func notify(){
for observer in observers {
observer.modelUpdated(self)
}
}
}
protocol ModelObserver {
var observerID: Int { get }
func modelUpdated(_ model: ModelObject)
}
class MyViewController : UIViewController, ModelObserver {
var observerID: Int = 1
var model = ModelObject()
override func viewDidLoad() {
self.model.attachObserver(self)
self.model.data = 777
}
func modelUpdated(_ model: ModelObject) {
print("received updated model")
self.model = model //<-- problem code
}
}
Simply put, my model object notifies any observer when data changes by calling notify().
My problem right now is memory access: when data gets set to 777, self.model becomes exclusively accessed, and when it calls notify which calls modelUpdated and eventually self.model = model, we get an error:
Simultaneous accesses to 0x7fd8ee401168, but modification requires exclusive access.
How can I solve this memory access issue?
If you're observing "a thing," then that "thing" has an identity. It's a particular thing that you're observing. You can't observe the number 4. It's a value; it has no identity. Every 4 is the same as every other 4. Structs are values. They have no identity. You should not try to observe them any more than you'd try to observe an Int (Int is in fact a struct in Swift).
Every time you pass a struct to a function, a copy of that struct is made. So when you say self.model = model, you're saying "make a copy of model, and assign it to this property." But you're still in an exclusive access block because every time you modify a struct, that also makes a copy.
If you mean to observe ModelObject, then ModelObject should be a reference type, a class. Then you can talk about "this particular ModelObject" rather than "a ModelObject that contains these values, and is indistinguishable from any other ModelObject which contains the same values."

Converting Older KVO to Swift 4

I'm trying to convert some old WWDC swift code to Swift 4. I think that I have everything done, except for this last bit that does some KVO. This has been pretty difficult to narrow it down to this last bit because everything appears to function like the example code - but these KVO methods do not get called in Swift 4. I found that out here: Open Radar Bug
What would be the Swift 4 way to represent the following?
// use the KVO mechanism to indicate that changes to "state" affect other properties as well
class func keyPathsForValuesAffectingIsReady() -> Set<NSObject> {
return ["state" as NSObject]
}
class func keyPathsForValuesAffectingIsExecuting() -> Set<NSObject> {
return ["state" as NSObject]
}
class func keyPathsForValuesAffectingIsFinished() -> Set<NSObject> {
return ["state" as NSObject]
}
And here are the variable definitions from the example:
override var isReady: Bool {
switch state {
case .initialized:
// If the operation has been cancelled, "isReady" should return true
return isCancelled
case .pending:
// If the operation has been cancelled, "isReady" should return true
guard !isCancelled else {
return true
}
// If super isReady, conditions can be evaluated
if super.isReady {
evaluateConditions()
}
// Until conditions have been evaluated, "isReady" returns false
return false
case .ready:
return super.isReady || isCancelled
default:
return false
}
}
override var isExecuting: Bool {
return state == .executing
}
override var isFinished: Bool {
return state == .finished
}
If more code is needed, please let me know.
If this is a duplicate question, please link to the duplicate here. I've been unable to find a solution.
The keyPathsForValuesAffecting… members can be properties instead of methods.
They must be declared #objc because the KVO system accesses the properties using the Objective-C runtime.
The properties should have type Set<String>.
If you use the #keyPath directive, the compiler can tell you when you've used an invalid key path (for example because of a spelling error or a change to the property name).
Thus:
#objc class var keyPathsForValuesAffectingIsReady: Set<String> {
return [#keyPath(state)]
}
#objc class var keyPathsForValuesAffectingIsExecuting: Set<String> {
return [#keyPath(state)]
}
#objc class var keyPathsForValuesAffectingIsFinished: Set<String> {
return [#keyPath(state)]
}
You also need to make sure your state property is declared #objc dynamic.
The main problem is that KVO is built using Objective-C, and it uses the Objective-C runtime to detect the existence of the keyPathsForValuesAffecting methods. In Swift 4, methods are no longer exposed to Objective-C by default if you don't include an #objc annotation on them. So, in a nutshell, adding the #objc annotation will probably fix your problem.
Another thing that I do—not strictly necessary, but it makes the code look a bit nicer—is to declare these as static constants. The #objc will cause these to get exposed to Objective-C as class methods, so it all works, and it's slightly cleaner. I like to put private on them, too, since these will never get called by Swift code, and there's no point cluttering your class's internal and/or public interface.
You also need to make sure that your state property is KVO-compliant, and sending the notifications when it is changed. You can either do this by making the property dynamic, which will cause the KVO system to automatically generate the notification calls for you, or you can manually call willChangeValue(for:) and didChangeValue(for:) (or the string-based versions, willChangeValue(forKey:) and didChangeValue(forKey:)) in your willSet and didSet handlers for the property.
Finally, don't use raw string key paths in Swift if you can avoid it. The #keyPath() mechanism is the preferred way to get string-based key paths (and for uses other than these legacy Objective-C methods that need to take strings, you should use the new KeyPath type which is better still). If your state property is not an Objective-C-compatible type, though, you're stuck with the old string key paths (in which case you'll fire the notifications in your willSet and didSet as described in the previous paragraph). Alternatively, you can create a dummy Any-typed object that mirrors your state property, purely for KVO purposes.
So, something like this:
#objc private static let keyPathsForValuesAffectingIsReady: Set<String> = [
#keyPath(state)
]
Now, the state property. If it's an Objective-C-compatible type, it's easy:
#objc dynamic var state: ...
Or, if it's not:
#objc var state: SomeNonObjCThing {
willSet { self.willChangeValue(forKey: "state") }
didSet { self.didChangeValue(forKey: "state") }
}
OR:
#objc private var _stateKVO: Any { return self.state }
var state: SomeNonObjCThing {
willSet { self.willChangeValue(for: \.stateKVO) }
didSet { self.didChangeValue(for: \.stateKVO) }
}
// you can now use #keyPath(_stateKVO) in keyPathsForValuesAffecting...
(NS)Operation relies heavily on NSObject-KVO
The closest Swift syntax is
#objc private class func keyPathsForValuesAffectingIsReady() -> Set<String> {
return [#keyPath(state)]
}
#objc private class func keyPathsForValuesAffectingIsExecuting() -> Set<String> {
return [#keyPath(state)]
}
#objc private class func keyPathsForValuesAffectingIsFinished() -> Set<String> {
return [#keyPath(state)]
}
Side note: You might need to make state thread-safe.

Setting a delegate generates a compile error

I want to use a strategy pattern to register a set of objects that implement a protocol. When I set this up, I get a compile error when trying to set the delegate that is part of the protocol.
For discussion purposes, I have slightly reworked the DiceGame from the Swift eBook's Delegation chapter. The changes of significance are:
protocol DiceGame - declares a delegate
class SnakesAndLadders implements DiceGame (and therefore the protocol and delegate)
class Games holds 3 instances of SnakesAndLadders as
1) a concrete class of SnakesAndLadders
2) a 'let' constant of protocol DiceGame
3) a 'var' variable of protocol DiceGame
We can set the delegate fine if we use the concrete class (snakesAndLadders). However, there is a compile error if we use 'let' to hold it as a protocol (diceGameAsLet) but it compiles if we hold the variable as a 'var' (diceGameAsVar).
It is easy to work around, however, the delegate itself never changes so should be held as a 'let' constant, as it is only the internal property that changes. I must not understand something (possibly subtle but significant) about protocols and how they work and should be used.
class Dice
{
func roll() -> Int
{
return 7 // always win :)
}
}
protocol DiceGame
{
// all DiceGames must work with a DiceGameDelegate
var delegate:DiceGameDelegate? {get set}
var dice: Dice {get}
func play()
}
protocol DiceGameDelegate
{
func gameDidStart( game:DiceGame )
func gameDidEnd( game:DiceGame )
}
class SnakesAndLadders:DiceGame
{
var delegate:DiceGameDelegate?
let dice = Dice()
func play()
{
delegate?.gameDidStart(self)
playGame()
delegate?.gameDidEnd(self)
}
private func playGame()
{
print("Playing the game here...")
}
}
class Games : DiceGameDelegate
{
let snakesAndLadders = SnakesAndLadders()
// hold the protocol, not the class
let diceGameAsLet:DiceGame = SnakesAndLadders()
var diceGameAsVar:DiceGame = SnakesAndLadders()
func setupDelegateAsClass()
{
// can assign the delegate if using the class
snakesAndLadders.delegate = self
}
func setupDelegateAsVar()
{
// if we use 'var' we can assign the delegate
diceGameAsVar.delegate = self
}
func setupDelegateAsLet()
{
// DOES NOT COMPILE - Why?
//
// We are not changing the dice game so want to use 'let', but it won't compile
// we are changing the delegate, which is declared as 'var' inside the protocol
diceGameAsLet.delegate = self
}
// MARK: - DiceGameDelegate
func gameDidStart( game:DiceGame )
{
print("Game Started")
}
func gameDidEnd( game:DiceGame )
{
print("Game Ended")
}
}
DiceGame is a heterogeneous protocol that you're using as a type; Swift will treat this type as a value type, and hence (just as for a structures), changing its mutable properties will mutate also the instance of the protocol type itself.
If you, however, add the : class keyword to the DiceGame protocol, Swift will treat it as a reference type, allowing you to mutate members of instances of it, without mutating the instance itself. Note that this will constraint the protocol as conformable to only by class types.
protocol DiceGame: class { ... }
With the addition of the above, the mutation of immutable diceGameAsLet:s properties will be allowed.
In this context, it's worth mentioning that the : class keyword is usually used to constrain protocols used as delegates (e.g., DiceGameDelegate in your example) as conformable to only by class types. With this additional constraint, the delegates can be used as types to which the delegate owner (e.g. some class) only hold a weak reference, useful in contexts where a strong reference to the delegate could create a retain cycle.
See e.g. the 2nd part of this answer for details.
The issue is that when you store something as a Protocol, even if it is a class, swift considers them to be a value type, instead of the reference type you are expecting them to be. Therefore, no part of it is allowed to be changed. Take a look at this reference for more information.

Swift: Protocol Based Type Construction

I'm trying to create a protocol in Swift I can use for object construction. The problem I'm running into is that I need to store the type information so the type can be constructed later and returned in a callback. I can't seem to find a way to store it without either crashing the compiler or creating build errors. Here's the basics (a contrived, but working example):
protocol Model {
init(values: [String])
func printValues()
}
struct Request<T:Model> {
let returnType:T.Type
let callback:T -> ()
}
We have a simple protocol that declares a init (for construction) and another func printValues() (for testing). We also define a struct we can use to store the type information and a callback to return the new type when its constructed.
Next we create a constructor:
class Constructor {
var callbacks: [Request<Model>] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest<T:Model>(request: Request<T>) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
let model = request.returnType(values: ["value1", "value2"])
request.callback(model)
}
}
}
A couple things to note: This causes a compiler crash. It can't figure this out for some reason. The problem appears to be var callbacks: [Request<Model>] = []. If I comment out everything else, the compiler still crashes. Commenting out the var callbacks and the compiler stops crashing.
Also, the func construct works fine. But it doesn't store the type information so it's not so useful to me. I put in there for demonstration.
I found I could prevent the compiler from crashing if I remove the protocol requirement from the Request struct: struct Request<T>. In this case everything works and compiles but I still need to comment out let model = request.returnType(values: ["value1", "value2"]) in func next(). That is also causing a compiler crash.
Here's a usage example:
func construct() {
let constructor = Constructor()
let request = Request(returnType: TypeA.self) { req in req.printValues() }
//This works fine
constructor.construct(TypeA.self) { a in
a.printValues()
}
//This is what I want
constructor.queueRequest(request)
constructor.next() //The callback in the request object should be called and the values should print
}
Does anyone know how I can store type information restricted to a specific protocol to the type can later be constructed dynamically and returned in a callback?
If you want the exact same behavior of next I would suggest to do this:
class Constructor {
// store closures
var callbacks: [[String] -> ()] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest<T:Model>(request: Request<T>) {
// some code from the next function so you don't need to store the generic type itself
// **EDIT** changed closure to type [String] -> () in order to call it with different values
callbacks.append({ values in
let model = request.returnType(values: values)
request.callback(model)
})
}
func next(values: [String]) {
callbacks.first?(values)
}
}
Now you can call next with your values. Hopefully this works for you.
EDIT: Made some changes to the closure type and the next function
Unfortunately there is no way to save specific generic types in an array and dynamically call their methods because Swift is a static typed language (and Array has to have unambiguous types).
But hopefully we can express something like this in the future like so:
var callbacks: [Request<T: Model>] = []
Where T could be anything but has to conform to Model for example.
Your queueRequest method shouldn't have to know the generic type the Request it's being passed. Since callbacks is an array of Request<Model> types, the method just needs to know that the request being queued is of the type Request<Model>. It doesn't matter what the generic type is.
This code builds for me in a Playground:
class Constructor {
var callbacks: [Request<Model>] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest(request: Request<Model>) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
let model = request.returnType(values: ["value1", "value2"])
request.callback(model)
}
}
}
So I found an answer that seems to do exactly what I want. I haven't confirmed this works yet in live code, but it does compile without any errors. Turns out, I needed to add one more level of redirection:
I create another protocol explicitly for object construction:
protocol ModelConstructor {
func constructWith(values:[String])
}
In my Request struct, I conform to this protocol:
struct Request<T:Model> : ModelConstructor {
let returnType:T.Type
let callback:T -> ()
func constructWith(values:[String]) {
let model = returnType(values: values)
callback(model)
}
}
Notice the actual construction is moved into the Request struct. Technically, the Constructor is no longer constructing, but for now I leave its name alone. I can now store the Request struct as ModelConstructor and correctly queue Requests:
class Constructor {
var callbacks: [ModelConstructor] = []
func queueRequest(request: Request<Model>) {
queueRequest(request)
}
func queueRequest(request: ModelConstructor) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
request.constructWith(["value1", "value2"])
callbacks.removeAtIndex(0)
}
}
}
Note something special here: I can now successfully "queue" (or store in an array) Request<Model>, but I must do so indirectly by calling queueRequest(request: ModelConstructor). In this case, I'm overloading but that's not necessary. What matters here is that if I try to call callbacks.append(request) in the queueRequest(request: Request<Model>) function, the Swift compiler crashes. Apparently we need to hold the compiler's hand here a little so it can understand what exactly we want.
What I've found is that you cannot separate Type information from Type Construction. It needs to be all in the same place (in this case it's the Request struct). But so long as you keep construction coupled with the Type information, you're free to delay/store the construction until you have the information you need to actually construct the object.

What is the purpose of willSet and didSet in Swift?

Swift has a property declaration syntax very similar to C#'s:
var foo: Int {
get { return getFoo() }
set { setFoo(newValue) }
}
However, it also has willSet and didSet actions. These are called before and after the setter is called, respectively. What is their purpose, considering that you could just have the same code inside the setter?
The point seems to be that sometimes, you need a property that has automatic storage and some behavior, for instance to notify other objects that the property just changed. When all you have is get/set, you need another field to hold the value. With willSet and didSet, you can take action when the value is modified without needing another field. For instance, in that example:
class Foo {
var myProperty: Int = 0 {
didSet {
print("The value of myProperty changed from \(oldValue) to \(myProperty)")
}
}
}
myProperty prints its old and new value every time it is modified. With just getters and setters, I would need this instead:
class Foo {
var myPropertyValue: Int = 0
var myProperty: Int {
get { return myPropertyValue }
set {
print("The value of myProperty changed from \(myPropertyValue) to \(newValue)")
myPropertyValue = newValue
}
}
}
So willSet and didSet represent an economy of a couple of lines, and less noise in the field list.
My understanding is that set and get are for computed properties (no backing from stored properties)
if you are coming from an Objective-C bare in mind that the naming conventions have changed. In Swift an iVar or instance variable is named stored property
Example 1 (read only property) - with warning:
var test : Int {
get {
return test
}
}
This will result in a warning because this results in a recursive function call (the getter calls itself).The warning in this case is "Attempting to modify 'test' within its own getter".
Example 2. Conditional read/write - with warning
var test : Int {
get {
return test
}
set (aNewValue) {
//I've contrived some condition on which this property can be set
//(prevents same value being set)
if (aNewValue != test) {
test = aNewValue
}
}
}
Similar problem - you cannot do this as it's recursively calling the setter.
Also, note this code will not complain about no initialisers as there is no stored property to initialise.
Example 3. read/write computed property - with backing store
Here is a pattern that allows conditional setting of an actual stored property
//True model data
var _test : Int = 0
var test : Int {
get {
return _test
}
set (aNewValue) {
//I've contrived some condition on which this property can be set
if (aNewValue != test) {
_test = aNewValue
}
}
}
Note The actual data is called _test (although it could be any data or combination of data)
Note also the need to provide an initial value (alternatively you need to use an init method) because _test is actually an instance variable
Example 4. Using will and did set
//True model data
var _test : Int = 0 {
//First this
willSet {
println("Old value is \(_test), new value is \(newValue)")
}
//value is set
//Finaly this
didSet {
println("Old value is \(oldValue), new value is \(_test)")
}
}
var test : Int {
get {
return _test
}
set (aNewValue) {
//I've contrived some condition on which this property can be set
if (aNewValue != test) {
_test = aNewValue
}
}
}
Here we see willSet and didSet intercepting a change in an actual stored property.
This is useful for sending notifications, synchronisation etc... (see example below)
Example 5. Concrete Example - ViewController Container
//Underlying instance variable (would ideally be private)
var _childVC : UIViewController? {
willSet {
//REMOVE OLD VC
println("Property will set")
if (_childVC != nil) {
_childVC!.willMoveToParentViewController(nil)
self.setOverrideTraitCollection(nil, forChildViewController: _childVC)
_childVC!.view.removeFromSuperview()
_childVC!.removeFromParentViewController()
}
if (newValue) {
self.addChildViewController(newValue)
}
}
//I can't see a way to 'stop' the value being set to the same controller - hence the computed property
didSet {
//ADD NEW VC
println("Property did set")
if (_childVC) {
// var views = NSDictionaryOfVariableBindings(self.view) .. NOT YET SUPPORTED (NSDictionary bridging not yet available)
//Add subviews + constraints
_childVC!.view.setTranslatesAutoresizingMaskIntoConstraints(false) //For now - until I add my own constraints
self.view.addSubview(_childVC!.view)
let views = ["view" : _childVC!.view] as NSMutableDictionary
let layoutOpts = NSLayoutFormatOptions(0)
let lc1 : AnyObject[] = NSLayoutConstraint.constraintsWithVisualFormat("|[view]|", options: layoutOpts, metrics: NSDictionary(), views: views)
let lc2 : AnyObject[] = NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: layoutOpts, metrics: NSDictionary(), views: views)
self.view.addConstraints(lc1)
self.view.addConstraints(lc2)
//Forward messages to child
_childVC!.didMoveToParentViewController(self)
}
}
}
//Computed property - this is the property that must be used to prevent setting the same value twice
//unless there is another way of doing this?
var childVC : UIViewController? {
get {
return _childVC
}
set(suggestedVC) {
if (suggestedVC != _childVC) {
_childVC = suggestedVC
}
}
}
Note the use of BOTH computed and stored properties. I've used a computed property to prevent setting the same value twice (to avoid bad things happening!); I've used willSet and didSet to forward notifications to viewControllers (see UIViewController documentation and info on viewController containers)
If I've made a mistake anywhere, please edit to fix it!
You can also use the didSet to set the variable to a different value. This does not cause the observer to be called again as stated in Properties guide. For example, it is useful when you want to limit the value as below:
let minValue = 1
var value = 1 {
didSet {
if value < minValue {
value = minValue
}
}
}
value = -10 // value is minValue now.
These are called Property Observers:
Property observers observe and respond to changes in a property’s
value. Property observers are called every time a property’s value is
set, even if the new value is the same as the property’s current
value.
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/ca/jEUH0.l
I suspect it's to allow for things we would traditionally do with KVO such as data binding with UI elements, or triggering side effects of changing a property, triggering a sync process, background processing, etc, etc.
NOTE
willSet and didSet observers are not called when a property is set in an initializer before delegation takes place
The many well-written existing answers cover the question well, but I'll mention, in some detail, an addition that I believe is worth covering.
The willSet and didSet property observers can be used to call delegates, e.g., for class properties that are only ever updated by user interaction, but where you want to avoid calling the delegate at object initialization.
I'll cite Klaas up-voted comment to the accepted answer:
willSet and didSet observers are not called when a property is first
initialized. They are only called when the property’s value is set
outside of an initialization context.
This is a quite neat as it means e.g. the didSet property is a good choice of launch point for delegate callbacks & functions, for your own custom classes.
As an example, consider some custom user control object, with some key property value (e.g. position in rating control), implemented as a subclass of UIView:
// CustomUserControl.swift
protocol CustomUserControlDelegate {
func didChangeValue(value: Int)
// func didChangeValue(newValue: Int, oldValue: Int)
// func didChangeValue(customUserControl: CustomUserControl)
// ... other more sophisticated delegate functions
}
class CustomUserControl: UIView {
// Properties
// ...
private var value = 0 {
didSet {
// Possibly do something ...
// Call delegate.
delegate?.didChangeValue(value)
// delegate?.didChangeValue(value, oldValue: oldValue)
// delegate?.didChangeValue(self)
}
}
var delegate: CustomUserControlDelegate?
// Initialization
required init?(...) {
// Initialise something ...
// E.g. 'value = 1' would not call didSet at this point
}
// ... some methods/actions associated with your user control.
}
After which your delegate functions can be used in, say, some view controller to observe key changes in the model for CustomViewController, much like you'd use the inherent delegate functions of the UITextFieldDelegate for UITextField objects (e.g. textFieldDidEndEditing(...)).
For this simple example, use a delegate callback from the didSet of the class property value to tell a view controller that one of it's outlets have had associated model update:
// ViewController.swift
Import UIKit
// ...
class ViewController: UIViewController, CustomUserControlDelegate {
// Properties
// ...
#IBOutlet weak var customUserControl: CustomUserControl!
override func viewDidLoad() {
super.viewDidLoad()
// ...
// Custom user control, handle through delegate callbacks.
customUserControl = self
}
// ...
// CustomUserControlDelegate
func didChangeValue(value: Int) {
// do some stuff with 'value' ...
}
// func didChangeValue(newValue: Int, oldValue: Int) {
// do some stuff with new as well as old 'value' ...
// custom transitions? :)
//}
//func didChangeValue(customUserControl: CustomUserControl) {
// // Do more advanced stuff ...
//}
}
Here, the value property has been encapsulated, but generally: in situations like these, be careful not to update the value property of the customUserControl object in the scope of the associated delegate function (here: didChangeValue()) in the view controller, or you'll end up with infinite recursion.
The willSet and didSet observers for the properties whenever the property is assigned a new value. This is true even if the new value is the same as the current value.
And note that willSet needs a parameter name to work around, on the other hand, didSet does not.
The didSet observer is called after the value of property is updated. It compares against the old value. If the total number of steps has increased, a message is printed to indicate how many new steps have been taken. The didSet observer does not provide a custom parameter name for the old value, and the default name of oldValue is used instead.
Getter and setter are sometimes too heavy to implement just to observe proper value changes. Usually this needs extra temporary variable handling and extra checks, and you will want to avoid even those tiny labour if you write hundreds of getters and setters. These stuffs are for the situation.
In your own (base) class, willSet and didSet are quite reduntant , as you could instead define a calculated property (i.e get- and set- methods) that access a _propertyVariable and does the desired pre- and post- prosessing.
If, however, you override a class where the property is already defined, then the willSet and didSet are useful and not redundant!
One thing where didSet is really handy is when you use outlets to add additional configuration.
#IBOutlet weak var loginOrSignupButton: UIButton! {
didSet {
let title = NSLocalizedString("signup_required_button")
loginOrSignupButton.setTitle(title, for: .normal)
loginOrSignupButton.setTitle(title, for: .highlighted)
}
I do not know C#, but with a little guesswork I think I understand what
foo : int {
get { return getFoo(); }
set { setFoo(newValue); }
}
does. It looks very similar to what you have in Swift, but it's not the same: in Swift you do not have the getFoo and setFoo. That is not a little difference: it means you do not have any underlying storage for your value.
Swift has stored and computed properties.
A computed property has get and may have set (if it's writable). But the code in the getter and setter, if they need to actually store some data, must do it in other properties. There is no backing storage.
A stored property, on the other hand, does have backing storage. But it does not have get and set. Instead it has willSet and didSet which you can use to observe variable changes and, eventually, trigger side effects and/or modify the stored value. You do not have willSet and didSet for computed properties, and you do not need them because for computed properties you can use the code in set to control changes.