how to modify UIButton's accessibilityLabel in an extension - ios

I'm using an analytics tool which logs the accessibilityLabel of buttons.
I'm trying to find a way to update the accessibilityLabel without changing my existing code.
For normal buttons I use the titleLabel.text. For iconButtons which use their the name coming from image assets I use accessibilityLabel itself.
Some issues I faced:
can't access accessibilityLabel within its getter. Because that would recursively look for accessibilityLabel.
So I had to use another property for backing and since this was an extension I wasn't able to use stored properties. Computed properties didn't work either because it would get stuck in the same feedback loop.
Eventually I hacked my way by using accessibilityHint. It's a stored property that I have no use of...
This works! Yet I've been told and read that I shouldn't override functions in an extension as that's not reliable. So I'm wondering what I should do?
And if Swift has any mechanism that doesn't involve overriding in UIButton's extension?!
here is my code:
extension UIButton{
private var adjustAccessibilityLabel : String{
if titleLabel?.text?.isEmpty == false{
return titleLabel!.text!
}else if accessibilityHint?.isEmpty == false{
return accessibilityHint!
}else{
return "Empty"
}
}
open override var accessibilityLabel: String?{
get{
return "\(self.adjustAccessibilityLabel))"
}
set{
accessibilityHint = newValue // Hacking my way through!
}
}
}

You're fighting the system. You can achieve this using subclassing.
To avoid similar problems in the future, always have ALL your UIButton, UITableViewCell, UIViewController, etc subclassed from your own base class so you can easily make such universal changes.

Related

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

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.

Subclass/Extend UIView so all other Views (UIImageView, UITextField ...) inherit upon creation

what is the suggested approach when I want to add a functionality to UIView so all views inside my app get those? As a matter of fact I need to add some stored properties too so an Extension is not possible. Since I need to deal with Textfields, ImageViews, Views (and who knows what else will come) I dont want to subclass every each of the too add that functionality, so the goal would be to make a subclass of UIView and all my controls (if its possible) get that functionality out of the box.
With an extension it would be easy, but as I said, I need to store some stuff too, so is this goal achievable with a subclass? Or what would be the right approach (maybe there is a third option)
Thanks
Why don't you define a protocol and provide default implementations in the protocol extension, then have UIView conform to that protocol? Here is an example:
protocol MyProto {
var someVar: Bool { get set }
func someFunc() -> Void
}
extension MyProto {
var someVar: Bool {
get {
// provide default implementation
return true
}
set {
}
}
func someFunc() -> Void {
// provide common implementation
}
}
extension UIView: MyProto {}
You can also use the where clause to constrain the default behaviour for a type.
extension MyProto where Self: UIControl {
var someVar: Bool {
get {
return isUserInteractionEnabled
}
set {
isUserInteractionEnabled = newValue
}
}
}
extension MyProto where Self: UITextField {
var someVar: Bool {
get {
return isFirstResponder
}
set {
newValue ? becomeFirstResponder() : resignFirstResponder()
}
}
}
TLDR; You can't do this and you will need to subclass each UI element that you want to introduce new properties to.
You can't do this (without access to the source code) as you would effectively be changing the class inheritance tree by injecting your own class between UIView and its subclasses.
Consider the implications if a language allowed this:
Class A defines a property, a
Class Binherits from Class A and defines a property b, which is fine because Class A does not have this property.
Class C inherits from Class B and has both a and b properties.
Now, what could happen if you could 'inject' Class A1 somehow 'below' Class A?
Class A1 could define a property, b, which is fine because Class A does not have this property
Class B now has a problem though, because its b clashes with the superclass b
Class C has a multiple-inheritance diamond-problem with property b
Of course, you only intend to add properties that don't clash (although you can't know this because you don't know of all possible subclass implementations) and don't need the subclasses to access your property, so the multiple inheritance
isn't an issue, but if such a feature were in a language, these potential issues would need to be addressed because you can't rely on everyone having the same intentions as you.

Enabling two-way communication between two classes

So I have a custom view touchableView inside a ViewController.
touchableView informs ViewController of changes in its properties through a delegate protocol (ViewController being the delegate).
What is the best method to change properties of touchableView from ViewController (so the other way around)?
Is there a way to create a two-way delegate relationship between two classes?
Simply:
Assuming that you are already have touchableView instance in the ViewController, you should be able to set -or get-/call its properties and methods.
For instance, assume that you have the following method in touchableView class:
class func fromNib() -> TouchableView {
return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)![0] as! TouchableView
}
You should simply be able to:
let touchableView = TouchableView.fromNib()
thus:
// for example
touchableView.myProperty = "Hello"
I assumed that TouchableView has a string property called myProperty...
Remark:
For some reason, I suggest to implement some of property observers in TouchableView:
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.
They might be -somehow- useful to be implemented in your custom class. For clarity, let's consider -for instance- that if editing the value of myProperty should be effecting the background color of the view, it might be implemented as:
var myProperty: String = "Initial Value" {
willSet {
print("About to set value to: \(newValue)")
}
didSet {
backgroundColor = UIColor.red
}
}
Further Reading:
If you are looking for an advanced approach for two way bindings (Implementing MVVM), you might want to check this article.
I would also suggest to take a look at some frameworks that will be so helpful for such an approach, such as RxSwift, for a more simple framework, you might want to check ReactiveKit/Bond.

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
}

Pass a Class (UILabel) with its properties through a function Swift

I am wondering if anyone knows how to (if possible) pass a UILabel through a function while being able to access and change its properties? Here's what I have:
func plusMinusChange(minus: UILabel, plus: UILabel) {
if (minus.hidden) {
minus.hidden=false
plus.hidden=true
} else {
minus.hidden=true
plus.hidden=false
}
}
And here's how I am calling it:
plusMinusChange(firstMinus, firstPlus)
I know this is probably really illogical but I want to give it a try anyways. If you were wondering, firstMinus and firstPlus are linked to UILabels on the storyboard.
Calls to methods (that is, funcs defined within a class or other type) require parameter labels for the second (and subsequent) parameter but not the first. If you want to change which labels are required at the call site, you change the declaration.
To require a label on the first parameter:
func plusMinusChange(#minus: UILabel, plus: UILabel) {
To require no label on the second:
func plusMinusChange(minus: UILabel, _ plus: UILabel) {
There is no need to use a conditional if there. You can use ! in front of the property to toggle its value as follow:
minus.hidden = !minus.hidden
plus.hidden = !plus.hidden

Resources