Swift 5.8 using 'any generic' causes strange error (_ErrorCodeProtocol)
Hello! In my Pet project I want to reduce layout code for UIKit (I'm using programmatic UI layout). The idea is to make enum for most used cases in my pet like:
// view is a view controller's view
let searchArea = UIView()
view.addSubview(searchArea)
searchArea.applyConstraints(
.alignTopWithTop(of: guide),
.alignLeadingWithLeading(of: view),
.alignTrailingWithTrailing(of: view),
.alignHeight(with: view, mult: 0.1)
)
I really like it much more than:
let searchArea = UIView()
view.addSubview(searchArea)
object.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
searchArea.topAnchor.constraint(equalTo: view.topAnchor, constant: 0),
searchArea.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
searchArea.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0),
searchArea.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.1, constant: 0)
])
Because UIViews should be aligned to other UIView childs as well as UIViewController safe Area (UILayoutGuide) I implemented this using generics:
protocol UISnapable {
var leadingAnchor: NSLayoutXAxisAnchor { get }
// Here are other anchors that UIView and UILayoutGuide have at the same time
var centerYAnchor: NSLayoutYAxisAnchor { get }
}
extension UIView: UISnapable {}
extension UILayoutGuide: UISnapable {}
enum Constraints<T: UISnapable> {
case alignTopWithTop(of: T, const: CGFloat = 0)
// Other align cases here
case setAspectWidth(ratio: CGFloat)
}
func applyConstraints(object: inout UIView, constraints: any UISnapable...) {
object.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(
// Transforming input variadic to constraints
constraints.map {
switch $0 {
case .alignTopWithTop(of: let of, const: let const):
return object.topAnchor.constraint(
equalTo: of.topAnchor,
constant: const
)
// Other align cases here
case .setAspectWidth(ratio: let ratio):
return object.heightAnchor.constraint(
equalTo: object.widthAnchor,
multiplier: ratio
)
}
}
)
}
I use "any" keyword for generic UISnapable as it's was recently introduced in Swift for such cases.
But unfortunately, I get error "Type _ErrorCodeProtocol has no member alignTopWithTop"
What I am doing wrong? I dunno how _ErrorCodeProtocol is connected to my generic case.
I've checked source of _ErrorCodeProtocol and it lead to Foundation. But I didn't find its declaration somewhere in foundation. Also I didn't find information on it in Apple docs (https://developer.apple.com/search/?q=_ErrorCodeProtocol&type=Documentation)
You're passing a list of UISnapable, but you mean to pass a list of Constraints. In this case you're putting any
in the wrong place.
What I believe you mean is this:
// Constraints are not generic, but they can hold any UISnapable in some cases
enum Constraints {
// Use `any UISnapable` rather than T
case alignTopWithTop(of: any UISnapable, const: CGFloat = 0)
// Other align cases here
case setAspectWidth(ratio: CGFloat)
}
And then the method is (note that the inout
here is unnecessary; UIView is a reference type):
func applyConstraints(object: inout UIView, constraints: Constraints...) {
^^^^^^^^^^^
As for the error, the compiler is just getting confused because you're trying to switch
on a non-enum ($0
here is any UISnapable
). The error is not particularly helpful, and is not related to the underlying problem.