How to write a generic apply() function in Swift?

后端 未结 4 1427
你的背包
你的背包 2020-12-16 11:45

Is there any way to get the following working in Swift 3?

 let button = UIButton().apply {
        $0.setImage(UIImage(named: \"UserLocation\"), for: .normal         


        
相关标签:
4条回答
  • 2020-12-16 12:17

    The HasApply protocol

    First of all lets define the HasApply protocol

    protocol HasApply { }
    

    and related extension

    extension HasApply {
        func apply(closure:(Self) -> ()) -> Self {
            closure(self)
            return self
        }
    }
    

    Next let make NSObject conform to HasApply.

    extension NSObject: HasApply { }
    

    That's it

    Let's test it

    let button = UIButton().apply {
        $0.titleLabel?.text = "Tap me"
    }
    
    print(button.titleLabel?.text) // Optional("Tap me")
    

    Considerations

    I wouldn't use NSObject (it's part of the Objective-C way of doing things and I assume it will be removed at some point in the future). I would prefer something like UIView instead.

    extension UIView: HasApply { }
    
    0 讨论(0)
  • 2020-12-16 12:22

    Alain has a good answer if you're not allergic to custom operators. If you'd rather not use those, the best alternative I could come up with was:

    @discardableResult func apply<T>(_ it:T, f:(T)->()) -> T {
        f(it)
        return it
    }
    

    which then allows you to use:

    let button = apply(UIButton()) { $0.setTitleText("Button") }
    

    It's not quite the same, but works out pretty well in general and has the advantage that T is completely unrestrained. It's an obviously contrived example, but:

    func apply<T,R>(_ it:T, f:(T)->R) -> R {
        return f(it)
    }
    

    even allows:

    print("\(apply(32) { $0 + 4 })")
    
    0 讨论(0)
  • 2020-12-16 12:37

    There's a very good and simple Cocoapods library available called Then that does exactly that. Only that it uses then instead of apply. Simply import Then and then you can do as the OP asked for:

    import Then
    
    myObject.then {
        $0.objectMethod()
    }
    
    let label = UILabel().then {
        $0.color = ...
    }
    

    Here's how the protocol is implemented: https://github.com/devxoul/Then/blob/master/Sources/Then/Then.swift

    extension Then where Self: Any {
        public func then(_ block: (Self) throws -> Void) rethrows -> Self {
            try block(self)
            return self
        }
    
    0 讨论(0)
  • 2020-12-16 12:40

    I had the same issue and ended up solving it with an operator:

    infix operator <-< : AssignmentPrecedence
    func <-<<T:AnyObject>(left:T, right:(T)->()) -> T
    {
      right(left)
      return left
    }
    
    let myObject = UIButton() <-< { $0.isHidden = false }
    
    0 讨论(0)
提交回复
热议问题