I have a generic function inside a class in which a computed property is declared:
class CalculatorBrain {
var internalProgram = [AnyObject]()
var accumulator: Double = 0.0
var variableValues: Dictionary<String,Double> = [:]
func setOperand<T> (operand: T) {
if operand is Double {
accumulator = operand as! Double
internalProgram.append(operand as AnyObject)
}
else if variableName == operand as? String {
var dictionaryValue: Double? {
get {
return variableValues[variableName!]
}
set {
accumulator = newValue!
internalProgram.append(newValue! as AnyObject)
}
}
}
}
I want to set dictionaryValue to the value shown in the display from the view controller:
private var brain = CalculatorBrain()
#IBAction func setVariableValue(_ sender: UIButton) {
brain.dictionaryValue = displayValue
}
Obviously I can't, because dictionaryValue is locally defined and "Value of type CalculatorBrain has no memeber dictionaryValue" Now the question is, how can I make a computed property global, and make changes to it from inside a class method? Or, how can I access a computed property defined inside a class method from outside the function?
The problem is dictionaryValue is not a computed property of your class, it is just a variable declared in the setOperand function, so it is not accessible from outside the function.
You should declare it as a stored property of your class and change it when setOperand is called.
Related
Is it possible for multiple class methods to access and modify a single inout parameter that is set in the class constructor? For example something like this:
class X {
var mySwitch: Bool
init(mySwitch: inout Bool) {
self.mySwitch = mySwitch
}
func updateSwitch() {
self.mySwitch.toggle() // this should toggle the external Boolean value that was originally passed into the init
}
}
// usage
var myBool: Bool = false
let x = X(mySwitch: &myBool)
x.updateSwitch()
print(myBool) // this should read 'true'
Short Answer
No.
Long Answer
There are other approaches that can satisfy this.
Binding Variables
In SwiftUI we use Binding Variables to do stuff like this. When the Binding variable updates, it also updates the bound variable. I'm not sure if it will work in Sprite Kit.
class X {
var mySwitch: Binding<Bool>
init(_ someSwitch: Binding<Bool>) {
self.mySwitch = someSwitch
}
func toggle() { mySwitch.wrappedValue.toggle() }
}
struct Y {
#State var mySwitch: Bool = false
lazy var switchHandler = X($mySwitch)
}
Callbacks
We can add a callback to X and call it on didSet of the boolean.
class X {
var mySwitch: Bool {
didSet { self.callback(mySwitch) } // hands the new value back to the call site in Y
}
let callback: (Bool) -> Void
init(_ someSwitch: Bool, _ callback: #escaping (Bool) -> Void) {
self.mySwitch = someSwitch
self.callback = callback
}
func toggle() { mySwitch = !mySwitch } // explicitly set to trigger didSet
}
class Y {
var mySwitch: Bool = false
lazy var switchHandler = X(mySwitch) {
self.mySwitch = $0 // this is where we update the local value
}
}
Short answer: NO.
Your question does not make sense. An inout parameter allows you read/write access to the parameter during the call to function. It has no meaning once the function returns. You sem to think it creates some persistent link between the parameter that is passed as inout and some other variable. It DOES NOT.
You can't "link" a variable that way because inout only works in the scope it's declared.
If what you want is to have some kind of global state, you can wrap it in a class.
class Wrapper {
var value = false
init() {}
}
class Modifier {
let wrapper: Wrapper
init(wrapper: Wrapper) {
self.wrapper = wrapper
}
func updateSwitch() {
wrapper.value.toggle()
}
}
let wrapper = Wrapper()
let modifier = Modifier(wrapper: wrapper)
modifier.updateSwitch()
print(wrapper.value) // this will read 'true'
I need to track the update in a variable of struct type.
Is it possible to add an observer on struct variable in Swift?
Example:
struct MyCustomStruct {
var error:Error?
var someVar:String?
}
class MyClass{
var myCustomStruct:MyCustomStruct?
}
I want to add an observer on myCustomStruct variable.
The standard Swift “property observers” (didSet and willSet) are designed to let a type observe changes to its own properties, but not for letting external objects add their own observers. And KVO, which does support external observers, is only for dynamic and #objc properties NSObject subclasses (as outlined in Using Key-Value Observing in Swift).
So, if you want to have an external object observe changes within a struct, as others have pointed out, you have to create your own observer mechanism using Swift didSet and the like. But rather than implementing that yourself, property by property, you can write a generic type to do this for you. E.g.,
struct Observable<T> {
typealias Observer = String
private var handlers: [Observer: (T) -> Void] = [:]
var value: T {
didSet {
handlers.forEach { $0.value(value) }
}
}
init(_ value: T) {
self.value = value
}
#discardableResult
mutating func observeNext(_ handler: #escaping (T) -> Void) -> Observer {
let key = UUID().uuidString as Observer
handlers[key] = handler
return key
}
mutating func remove(_ key: Observer) {
handlers.removeValue(forKey: key)
}
}
Then you can do things like:
struct Foo {
var i: Observable<Int>
var text: Observable<String>
init(i: Int, text: String) {
self.i = Observable(i)
self.text = Observable(text)
}
}
class MyClass {
var foo: Foo
init() {
foo = Foo(i: 0, text: "foo")
}
}
let object = MyClass()
object.foo.i.observeNext { [weak self] value in // the weak reference is really only needed if you reference self, but if you do, make sure to make it weak to avoid strong reference cycle
print("new value", value)
}
And then, when you update the property, for example like below, your observer handler closure will be called:
object.foo.i.value = 42
It’s worth noting that frameworks like Bond or RxSwift offer this sort of functionality, plus a lot more.
With variables you can use two default observers
willSet - represents moment before variable will be set with new value
didSet - represents moment when variable was set
Also in observer you can work with two values. With current variable in current state, and with constant depending on observer
struct Struct {
var variable: String {
willSet {
variable // before set
newValue // after set, immutable
}
didSet {
oldValue // before set, immutable
variable // after set
}
}
}
And the same you can do for any other stored property, so you can use it for struct variable in your class too
class Class {
var myStruct: Struct? {
didSet {
...
}
}
}
Also you can for example in did set observer of variable post notification with certain name
didSet {
NotificationCenter.default.post(name: Notification.Name("VariableSet"), object: nil)
}
and then you can add certain class as observer for notification with this name
class Class {
init() {
NotificationCenter.default.addObserver(self, selector: #selector(variableSet), name: Notification.Name("VariableSet"), object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self, name: Notification.Name("VariableSet"), object: nil)
}
#objc func variableSet() {
...
}
}
Try this, first create a struct with an action variable and when you create an object of the struct set the action parameter on the action you want. ex.
struct testStruct {
var action: (()->())?
var variable: String? {
didSet {
self.action?()
}
}
}
And inside your main code - main class
var testS = testStruct()
testS.action = {
print("Hello")
}
testS.variable = "Hi"
When you set the testS.variabe = "Hi" it will call the print("Hello")
struct MyCustomStruct {
var error:Error?
var someVar:String?
}
class MyClass{
var myCustomStruct:MyCustomStruct? {
didSet{
print("my coustomeSruct changed")
}
}
}
let aClass = MyClass()
aClass.myCustomStruct?.someVar = " test"
//prints:my coustomeSruct changed
When the following code is run, the self inside of defaultModuleName is ReactViewController when one would expect it to be FooViewController. Why?
class ReactViewController: UIViewController {
var moduleName: String = defaultModuleName
static var defaultModuleName: String {
let t = String(reflecting: self) // Also tried NSStringFromClass
guard let s = t.split(separator: ".").last else { return "" }
guard let r = s.range(of: "ViewController") else { return "" }
return String(s.prefix(upTo: r.lowerBound))
}
}
class FooViewController: ReactViewController {
override func viewDidLoad() {
super.viewDidLoad();
print(moduleName); // Prints "React"
}
}
This is pretty interesting; it appears that the self available in a property initialiser is merely the type that the property is defined in, rather than the dynamic type of the instance being constructed.
A more minimal example would be:
class C {
static var foo: String { return "\(self)" }
let bar = foo // the implicit 'self' in the call to 'foo' is always C.
}
class D : C {}
print(D().bar) // C
In the property initialiser for bar, the implicit self is C.self, not D.self; despite the fact that we're constructing a D instance. So that's what the call to foo sees as self.
This also prevents class member overrides from being called from property initialisers:
class C {
class var foo: String { return "C" }
let bar = foo
}
class D : C {
override class var foo: String { return "D" }
}
print(D().bar) // C
Therefore I regard this as a bug, and have filed a report here.
Until fixed, a simple solution is to use a lazy property instead, as now self is the actual instance (upon the property being accessed for the first time), which we get can get the dynamic type of with type(of: self).
For example:
class C {
static var foo: String { return "\(self)" }
// private(set) as the property was a 'let' in the previous example.
lazy private(set) var bar = type(of: self).foo
}
class D : C {}
print(D().bar) // D
Applied to your example:
class ReactViewController : UIViewController {
lazy var moduleName = type(of: self).defaultModuleName
static var defaultModuleName: String {
let t = String(reflecting: self) // Also tried NSStringFromClass
guard let s = t.split(separator: ".").last else { return "" }
guard let r = s.range(of: "ViewController") else { return "" }
return String(s.prefix(upTo: r.lowerBound))
}
}
class FooViewController : ReactViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(moduleName) // Prints "Foo"
}
}
You just need to pass self instead of type(of: self), and use the String(describing:) initializer.
class ClassA {
static var className: String {
return String(describing: self)
}
}
class ClassB: ClassA { }
print(ClassB.className) // prints "ClassB"
EDIT: clarification on the var moduleName: String = defaultModuleName update. Suppose I add this line to the above example (same idea):
class ClassA {
// This is a property of ClassA -> it gets implicitly initialized
// when ClassA does -> it uses ClassA.className for its value
var instanceClassName = className
static var className: String {
return String(describing: self)
}
}
class ClassB: ClassA { }
print(ClassB().instanceClassName) // prints "ClassA"
This new instanceClassName is not static, so it is an instance property on ClassA. It is therefore initialized when ClassA is initialized (not when ClassB is initialized). Ergo, a property being set within ClassA, using a reference to className, will print out ClassA.
When creating a stored property with Observing Accessors, I can specify a default value. However, when overriding a stored property and its Accessors I cannot specify a default value.
Variable with getter/setter cannot have initial value.
Which seems very strange, as this is NOT a computed property with a getter/setter, but a set of Observing Accessors on a stored property!
class FirstViewController: UIViewController {
internal var test: Float = 32.0 {
willSet {
}
didSet {
}
}
The first view controller compiles fine, with a stored property initialized to 32.0
class SecondViewController: FirstViewController {
override var test: Float = 64.0 {
willSet {
}
didSet {
}
}
The second view controller does not compile, as the 'computed property' is being given an initial value
In swift you are able to override properties only with computed properties (which are not able to have default values) with same type. In your case, if you wish override test property in SecondViewController you need write something like this:
override var test: Float {
get {
return super.test
}
set {
super.test = newValue
}
}
And there is no way to override didSet/willSet observers directly; you may do this by write other methods invoked in observers and just override them:
FirstViewController:
internal var test: Float = 32.0 {
willSet {
test_WillSet(newValue)
}
didSet {
test_DidSet(oldValue)
}
}
func test_WillSet(newValue: Float) {}
func test_DidSet(oldValue: Float) {}
SecondViewController:
override func test_WillSet(newValue: Float) {
super.test_WillSet(newValue)
}
override func test_DidSet(oldValue: Float) {
super.test_DidSet(oldValue)
}
I know that this has been asked a long time ago but I came up with a slightly different solution and it works exactly as you wanted. You have a property in the first ViewController then in the inherited one you override it and have observers set on it in the form of didSet.
So in the FirstViewController you have a property like in the example below:
var myNumber: Double = 20.00
Then in the SecondViewController which inherits from FirstViewController you override it as follows:
override var myNumber: Double {
didSet {
//Here you can update UI or whatever you want to do once the property changes
//Print its value
print("Value of myNumber is : \(myNumber)")
}
I hope this will help someone with the above issue as this is a nice and easy way to solve the problem mentioned above.
Why this code will trigger didSet when init
final public class TestDidSet {
static var _shared: TestDidSet! = TestDidSet()
func testA() { }
private var test = true {
didSet {
print("didSet test when initing!!!!!:\(test)")
}
}
private var _value: Bool! {
didSet {
print("didSet when initing!!!!!:\(_value)")
}
}
private init() {
testSet()
_value = false
test = false
}
private func testSet() {
_value = true
}
}
TestDidSet._shared.testA()
any idea?
should it not trigger didSet?
someone help!
update:
My point of view is this,
testSet() and _value = false is doing the same thing, but testSet() is outside init(), so testSet() will trigger didSet while _value = false not. Why?!
It's not optional type or other reason, that cause 'didSet', I suppose.
When you declare a property with an implicitly unwrapped optional type (Bool! in your case), it gets implicitly assigned a default value of nil. Then afterwards if you assign it with some other value in your initializer then the didSet observer gets triggered because it's already a second assignment. didSet is supposed to not be triggered only on a first one.
The didSet{} closure is called every time you assign a new value to your properties (even if you assign it at the declaration (inline) or at the initialisation).