In Swift3, combine responds#to and calling in one fell swoop?

For example,

superview?.subviews.filter{
    $0 != self &&
    $0.responds(to: #selector(setter: Blueable.blue))
  }.map{
    ($0 as! Blueable).blue = false
  }

Is there a concept like ..

  • Swift: use property observers while defining variables one per line
  • Using an UIView as a Mask in another UIView on Swift
  • How to apply custom styling to NSTableHeaderView?
  • Characteristic.value from Bluetooth reading in Swift
  • Swift 4 CTRubyAnnotation don't work
  • How to read from a plist with Swift 3 iOS app
  • x.blue??? = false
    

    ‘???’ meaning ‘if it responds to blue, call blue’…

    Note – I fully appreciate I could write an extension, callIfResponds:to, or a specific extension, blueIfBlueable.

    I’m wondering if there’s some native swiftyness here, which I don’t know about. It seems to be a pretty basic concept.


    Footnote:

    in the ensuing heated discussion, there is mention of using a protocol. Just for the benefit of anyone reading, here’s one approach to using a protocol:

    protocol Blueable:class {
        var blue:Bool { get set }
    }
    
    extension Blueable where Self:UIView {
        func unblueAllSiblings() { // make this the only blued item
            superview?.subviews.filter{$0 != self}
                .flatMap{$0 as? Blueable}
                .forEach{$0.blue = false}
        }
    }
    
    // explanation: anything "blueable" must have a blue on/off concept.
    // you get 'unblueAllSiblings' for free, which you can call from
    // any blueable item to unblue all siblings (likely, if that one just became blue)
    

    To use it, for example…

    @IBDesignable
    class UILabelStarred: UILabel, Blueable {
    
        var blueStar: UIView? = nil
        let height:CGFloat = 40
        let shinyness:CGFloat = 0.72
        let shader:Shader = Shaders.Glossy
        let s:TimeInterval = 0.35
    
        @IBInspectable var blue:Bool = false {
            didSet {
                if (blue == true) { unblueAllSiblings() }
                blueize()
            }
        }
    
        func blueize() {
    
            if (blueStar == nil) {
                blueStar = UIView()
                self.addSubview(blueStar!)
                ... draw, say, a blue star here
                }
            if (blue) {
                UIView.animate(withDuration: s) {
                    self. blueStar!.backgroundColor = corporateBlue03
                    self.textColor = corporateBlue03
                }
            }
            else {
                UIView.animate(withDuration: s) {
                    self. blueStar!.backgroundColor = UIColor.white
                    self.textColor = sfBlack5
                }
            }
        }
    }
    

    Just going back to the original question, that’s all fine. But you can’t “pick up on” an existing property (a simple example is isHidden) in existing classes.

    Furthermore as long as we’re discussing it, note that in that example protocol extension, you unfortunately can NOT have the protocol or extension automatically, as it were, call unblueAllSiblings from “inside” the protocol or extension, for exactly this reason: why you can’t do it

    4 Solutions Collect From Internet About “In Swift3, combine responds#to and calling in one fell swoop?”

    Somehow I think what you want is:

    superview?.subviews.filter {
       $0 != self // filter out self
    }.flatMap {
      $0 as? Blueable
    }.forEach {
      $0.blue = false
    }
    

    Why should you be checking whether a class conforms to a setter when you can check the type?

    Checking selectors should not be used in pure Swift. You will need it mostly for interacting with Obj-C APIs – dynamic methods, optional methods in protocols or informal protocols.

    As others have said, a better way of going about this is by conditionally type-casting rather than using responds(to:). However, don’t overlook just using a for in loop – they’re pretty powerful, allowing you to use pattern matching and where clauses, allowing you to iterate over a given subset of elements.

    for case let blueable as Blueable in superview?.subviews ?? [] where blueable !== self {
        blueable.blue = false
    }
    
    • case let blueable as Blueable uses the type-casting pattern in order to only match elements that are Blueable, and bind them to blueable if successful.

    • where blueable !== self excludes self from being matched.

    Why not check conformance to the type directly? Something like:

    superview?.subviews.forEach {
        guard $0 !== self else { return }
        ($0 as? Blueable)?.blue = false
    }
    

    You can add this extension

    extension UIView {
        var siblings: [UIView] { return self.superview?.subviews.filter { $0 != self } ?? [] }
    }
    

    Now pick the solution that you prefer among the followings

    Solution 1

    siblings.flatMap { $0 as? Blueable }.forEach { $0.blue = false  }
    

    Solution 2

    siblings.forEach { ($0 as? Blueable)?.blue = false }