Norway


As a number of people pointed out, using keypath initialization for uniform type members has its bright spots and its drawbacks. The particular use-case I wrote about (UIEdgeInsets) has four same-type members, and two common approaches: set all insets to the same value and set the vertical and horizontal inset values in tandem.

Because of this specific extension (adding vertical, etc members), you  open the door to potential bugs, namely:

let insets: UIEdgeInsets = [.horizontal: 8, .left: 4]

In this example, the developer intent is unclear. Should the left inset override the horizontal choice? Or should this be disallowed or warned? This occurs almost entirely to (my) bad in letting .horizontal and its fellow compound members act as initializer choices. Get rid of the compound settable members, get rid of the problem.

A similar bug occurs like this:

let insets: UIEdgeInsets = [.left: 4, .right: 4, .left: 8]

Since there’s no checking in this approach, you can easily duplicate entries without the compiler warning or emitting an error. The Swift compiler does not complain about duplicate keys in dictionary literals (SR-7066). Aside from that, it’s a cute way to do an unconventional memberwise initialization but one that probably should never make its way into production code.

So what would be a better choice for this example? A custom initializer wouldn’t hurt. For example (and I’m not sure the tests against zero are helping or hurting performance here):

extension UIEdgeInsets {
    public init(all: CGFloat = 0) {
        self.init()
        if all != 0 { (, bottom, left, right) = (all, all, all, all) }
    }
     public init(vertical: CGFloat = 0, horizontal: CGFloat = 0) {
        self.init()
        if vertical != 0 { (, bottom) = (vertical, vertical) }
        if horizontal != 0 { (left, right) = (horizontal, horizontal }
    }
}

This gives you two convenience specific to common use cases. You could alternatively write some kind of static method that builds an instance (something like UIEdgeInsets.inset(by:)) but I don’t think that gets you anything big beyond normal initializers.

Getting back to using dictionary literals, someone asked if you can use the same approach to create ad-hoc, unordered, type-erased member initialization. I’m leaning towards “no” but you might be able to play with some kind of Initializable protocol (ensuring init()) and then looking up the value for each keypath and grab a type from that. It would be a pain.  If you do get something like that working, drop me a note. At the least, it would be “unSwiftlike” and antithetical to type safety.



Source link
Based Blockchain Network

LEAVE A REPLY

Please enter your comment!
Please enter your name here