ExpressibleByIntegerLiteral with parameter pack "Cannot convert value of type 'Int' to expected argument type"

I'm trying to create an extension for the UIView helping to activate the constraints easily.

Using the new Swift 5.9 feature Parameter Pack, I have the value of the constraint that confirms to ExpressibleByIntegerLiteral and the active function tasks parameter pack of UIViewConstraint.

protocol UIViewConstraint {
    
    associatedtype Anchor: AnyObject
    
    var anchor: NSLayoutAnchor<Anchor>? { get }
    var attribute: NSLayoutConstraint.Attribute { get }
    var value: UIView.ConstraintValue { get }
}

extension UIView {
    
    struct Constraint<Anchor: AnyObject>: UIViewConstraint {
        
        var anchor: NSLayoutAnchor<Anchor>?
        var attribute: NSLayoutConstraint.Attribute
        var value: UIView.ConstraintValue
    }
}

extension UIViewConstraint where Anchor == NSLayoutYAxisAnchor {
    
    static func top(_ value: UIView.ConstraintValue, to anchor: NSLayoutYAxisAnchor? = nil) -> UIView.Constraint<NSLayoutYAxisAnchor> {
        UIView.Constraint(anchor: anchor, attribute: .top, value: value)
    }
    
    static func bottom(_ value: UIView.ConstraintValue, to anchor: NSLayoutYAxisAnchor? = nil) -> UIView.Constraint<NSLayoutYAxisAnchor> {
        UIView.Constraint(anchor: anchor, attribute: .bottom, value: value)
    }
}

extension UIView {
   
    struct ConstraintValue {
        
        var constant: CGFloat
    }
}

extension UIView.ConstraintValue: ExpressibleByIntegerLiteral {
    
    init(integerLiteral value: Int) {
        self.init(constant: CGFloat(value))
    }
}

extension UIView {
    
    func active<each Anchor>(_ constraint: repeat UIView.Constraint<each Anchor>) {
        (repeat _active(each constraint))
    }
    
    func active2<each Constraint: UIViewConstraint>(_ constraint: repeat each Constraint) {
        (repeat _active(each constraint))
    }
    
    private func _active<T: UIViewConstraint>(_ anchor: T) {
        //
    }
}

When I tried to pass an int literal to the generic functions active(_:) or active2(_:), I got the error:

Cannot convert value of type 'Int' to expected argument type 'UIView.ConstraintValue'.

But creating an object directly from UIView.Constraint* works fine.

let superView = UIView()
let view = UIView()

superView.addSubview(view)
view.active(
    .top(10), // ❌ Cannot convert value of type 'Int' to expected argument type 'UIView.ConstraintValue'
    .bottom(20) // ❌ Cannot convert value of type 'Int' to expected argument type 'UIView.ConstraintValue'
)

view.active2(
    .top(10), // ❌ Cannot convert value of type 'Int' to expected argument type 'UIView.ConstraintValue'
    .bottom(20) // ❌ Cannot convert value of type 'Int' to expected argument type 'UIView.ConstraintValue'
)

let top: UIView.Constraint<NSLayoutYAxisAnchor> = .top(10) // ✅
let bottom: UIView.Constraint<NSLayoutYAxisAnchor> = .bottom(20) // ✅
view.active(top, bottom) // ✅
view.active2(top, bottom) // ✅
extension UIView.ConstraintValue {
    
    public static prefix func >= (value: UIView.ConstraintValue) -> UIView.ConstraintValue {
        //
    }
}

anchor(.top(>=1)) // ✅

Looks like a missing feature. As a workaround you may currently use:

    view.active(
        .top(.init(integerLiteral: 10)),
        .bottom(.init(integerLiteral: 20))
    )

BTW, compared to built-in NSLayoutConstraint.activate(...) the API prettifier you created doesn't seem to allow activating constraints on several views at once.

Alternative API suggestion

One of the possible approaches is to have explicit "activate" calls on individual constraints that would be applied at a later stage (all at once). Example:

func baz() {
    withConstraints { // a better name wanted
        constraint1.activate()
    }
}
func bar() {
    constraint2.deactivate()
    baz()
}
func foo() {
    constraint3.activate() // activates right away
    constraint4.deactive() // deactivates right away
    withConstraints {
        bar()
    }
}
foo()

In this example constraint1 will not activate immediately or upon exiting the block in baz() as there's a nested open constraint at the "foo" level. Internally withConstraints could maintain an "openLevel" variable and the actual constraint activation/deactivation happens when this variable goes from 1 to 0. This variable could be a global variable (presumably constraint (de)activation could be done from the main thread only so thread safety is not an issue here).

The question here is about the parameter pack not the UI constraints.

here is another example for the issue

protocol ImageFilter {
    
    associatedtype Processor: ImageFilterProcessor
    /**/
}

protocol ImageFilterProcessor { /**/ }

struct ImageBlurFilter: ImageFilter {
        
    init(radius: Radius) { /**/ }
    
    struct Processor: ImageFilterProcessor { /**/ }
    
    struct Radius: ExpressibleByIntegerLiteral {
        
        init(integerLiteral value: Int) { /**/ }
    }
}

struct ImageExposureAdjust: ImageFilter {
    
    init(scalar: Scalar) { /**/ }
    
    struct Processor: ImageFilterProcessor { /**/ }
    
    struct Scalar: ExpressibleByFloatLiteral {
        
        init(floatLiteral value: Double) { /**/ }
    }
}

extension ImageFilter where Self == ImageBlurFilter {
    
    static func blur(radius: ImageBlurFilter.Radius) -> ImageBlurFilter {
        ImageBlurFilter(radius: radius)
    }
}

extension ImageFilter where Self == ImageExposureAdjust {
    
    static func exposureAdjust(scalar: ImageExposureAdjust.Scalar) -> ImageExposureAdjust {
        ImageExposureAdjust(scalar: scalar)
    }
}

func applyFilters<each Filter: ImageFilter>(_ filter: repeat each Filter) {
    let _ = (repeat (each filter))
}

func test() {
    applyFilters(
        .blur(radius: 10), // ❌ Cannot convert value of type 'Int' to expected argument type 'ImageBlurFilter.Radius'
        .exposureAdjust(scalar: 0.5) // ❌ Cannot convert value of type 'Double' to expected argument type 'ImageExposureAdjust.Scalar'
    )
    
    applyFilters(
        .blur(radius: ImageBlurFilter.Radius(10)), // ✅
        .exposureAdjust(scalar: ImageExposureAdjust.Scalar(0.5)) // ✅
    )
    
    let blurRadius: ImageBlurFilter.Radius = 10 
    let exposureAdjustScalar: ImageExposureAdjust.Scalar = 0.5
    
    applyFilters(
        .blur(radius: blurRadius), // ✅
        .exposureAdjust(scalar: exposureAdjustScalar) // ✅
    )
}

Yes, I understand. Same workaround possible:

        .blur(radius: .init(integerLiteral: 10)),
        .exposureAdjust(scalar: .init(floatLiteral: 0.5))

which you could make slightly shorter by providing another init:

        .blur(radius: .init(10)),
        .exposureAdjust(scalar: .init(0.5))

And same explanation: looks like a missing feature.