Swift Double vs. CGFloat: what the heck is the guidance, here?

I was inspired by Swift going open source with the prospect of Swift's Foundation library becoming platform neutral, so I took a look at one of my flagship apps, and contemplating making portions of that open source as well. Though I started nobly, I became dismayed.


I fell into a kind of Double vs. CGFloat "****" that I had been conveniently avoiding until now.


First, I refactored a ton of shipping code into a framework (you know, because iOS 8, Apple Watch, etc.), and that framework is capable of being mostly platform neutral, like Swift itself is becoming, with just a small shim for each platform. Platforms for the moment being iOS, OS X, watchOS and tvOS. But now I have Linux in my sights, with only a dependency on Foundation. I have been able to sort-of factor out UIFont, UIColor, and the like, into little, separate extensions that complete the circle for the existing platforms. But what I haven't been able to do is reconcile the floating point type for the new platform. The default in Swift is Double, which is great, because it's the same as CGFloat.


Except that it isn't.


Here's the trap I fell into. Initially, to convert to Swift faster, I polluted my framework source code with CGFloat in order to eliminate conversion ****, but that requires CoreGraphics everywhere, which is not part of Foundation. And now faced with the prospect of removing CoreGraphics, I see myself adding one conversion function after another.


This feels wrong.


Apple engineers and third party developers:


What is the general guidance for converting between the default Swift Double floating point type and CGFloat? Am I resigned to implementing converters and type coercions for every little thing?

Replies

Use Double wherever possible in Swift. The only time you should go back to CGFloat is when you're calling one of the existing frameworks, like graphics, that explicitly the CGFloat...In which case you can just do a cast:


let myCGFloat = CGFloat(myDouble)

OK. But I had to overcome some hesitation, and add a little bit of utility conversion help, before I could commit.


I was afraid at first of adding CGFloat conversion everywhere. This fear was based on the past experience I described: the last time I tried to replace CGFloat with Double in this code, I was in mid-port to Swift, and it was a big mess (owing in part to the flexibility of implict conversion in the Objective-C code), conversions galores. However, since this code has been fully ported and tested, it now has a much cleaner architecture. So, a full-on Find/Replace inside the core code left only a small handful of places in UI-specific code where I really needed the CGFloat(myDouble) conversion, right at the points where UIKit classes needed them. So it ended up a lot cleaner than I expected. Any math that happens inside the model classes are using Double with no conversions, and what's left in the views or controllers is using CGFloat, with only conversions at the points of handoff.


Related: in order to fully abstract away UIColor etc. in the core code, I decided to go with named tuples of Doubles (say that three times fast), e.g., ColorRGBValue = (red: Double, green: Double, blue: Double, alpha: Double), and implemented a UIColor extension that constructs from this. That way, the Double to CGFloat conversion also happens from a small number of calls. I don't know if that's the best approach, but it is working for me. "Mmm, tuples."

Not sure why you did your tuple thing. UIColor's constructor takes either CGFloat or Double, whichever you want to pass into it.