Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the swift equivalent to setting properties on `id`?

I wonder what's the Swift equivalent in calling a method on id in which the availability of the method is determined at runtime. Specifically I'm looking to do this pattern in Swift:

-(IBAction) handleEvent:(id) sender {
    BOOL didDisable = NO;
    if([sender respondsToSelector:@selector(setEnabled:)]) {
        [sender setEnabled:NO];
        didDisable = YES;
    }
    [self doSomethingAsyncWithCompletionHandler:^{
        if(didDisable) {
            [sender setEnabled:YES];
        }
    }];
}

The biggest problem is that setEnabled: is imported in Swift as a property (e.g. UIBarItem) and none of the following constructs compile

func handleEvent(sender: AnyObject) {
    // Error: AnyObject does not have a member named "enabled"
    sender.enabled? = false

    // Error: (BooleanLiteralCompatible) -> _ is not identical to Bool
    sender.setEnabled?(false)
}
like image 981
adib Avatar asked Jun 16 '15 14:06

adib


People also ask

What is get set Swift?

Getters and setters in Swift are the methods a computed property uses to either set or get a value on-demand. A stored property stores a value for later use (for example, a variable that belongs to a class instance). A computed property does not store a value. Instead, it computes it only when requested.

What is property wrappers in Swift?

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.

What are property observers in Swift?

Property Observers For a stored property, the observer is called when the property is assigned to a new value. For a computed property, the observer is called when the property is read or written. We can use willSet and didSet to observe changes to a property's value. Example: Swift.


1 Answers

You can in fact do it exactly the same way you were doing it before: by calling respondsToSelector:. Indeed, that is exactly what your proposed expression does:

sender.setEnabled?(false)

That expression is actually a shorthand - it calls respondsToSelector: first, and then calls setEnabled: only if the respondsToSelector: test passes. Unfortunately, as you say, you can't get that code to compile. That, however, is merely a quirk of Swift's known repertory of available methods. The fact is that, although it is a little tricky to get it to compile, it can be done - and once you get it to compile, it behaves just as you would expect.

However, I'm not going to explain how to make it compile, because I don't want to encourage this kind of trickery. This sort of dynamic messaging is discouraged in Swift. In general, dynamic messaging tricks such as key-value coding, introspection, and so forth are not needed in Swift and are not consonant with Swift's strong typing approach. It would be better to do things the Swift way, by casting optionally to something that you have reason to believe this thing might be and that has an enabled property. For example:

@IBAction func doButton(sender: AnyObject) {
    switch sender {
    case let c as UIControl: c.enabled = false
    case let b as UIBarItem: b.enabled = false
    default:break
    }
}

Or:

@IBAction func doButton(sender: AnyObject) {
    (sender as? UIControl)?.enabled = false
    (sender as? UIBarItem)?.enabled = false
}
like image 173
matt Avatar answered Sep 30 '22 17:09

matt