Trying to write cross-platform code for drawing routines is ridiculously frustrating. It’s never just a matter of creating cross-platform type aliases. UIColor
and NSColor
are cousins, not brothers. UIBezierPath
and NSBezierPath
are slightly more distantly related, especially when it comes to adding curves and retrieving an underlying cgPath
.
Although a lot of tech debuts on Cocoa, it often gets refined in Cocoa Touch. That refinement doesn’t prioritize cross-platform development, so the “second time, better design” features that leap into iOS land are hard to support with a single code-base. (And yes, I’m looking at you layout constraints with their different dictionary types for metrics.)
Starting last year, UIKit drawing really improved. It already had several advantages over Cocoa drawing, but last year’s update pushed the APIs to a new level.
NSImage
has its init(size:flipped:drawingHandler:)
initializer (introduced in 10.8). in iOS 10, UIImage
adopted and refined that closure-based drawing approach to UIGraphicsImageRenderer
, UIGraphicsImageRendererFormat
, etc. A renderer can return an image or PNG/JPEG data. Very handy, very convenient, wide-color aware, nifty. And it phases out a lot of home-built solutions that devs had been using to get exactly those results.
The new features are not fully refined. For example, there isn’t a single doc comment across the entire feature suite. Which, you know, sigh. But the architecture is there if not perfect or final, and it’s several steps beyond the corresponding init
on macOS.
With this new tech in place, I’m starting to wonder about some older APIs like Cocoa Touch’s drawing stack, which lets you push and pop contexts for immediate drawing. It’s handy for using UIKit calls to draw into custom Core Graphics bitmap contexts. Is that still going to be around and used after WWDC this year? Or next year? Or will it be subsumed into UIGraphicsImageRenderer
? It looks ripe for redesign and replacement.
I also don’t think UIGraphicsBegin/EndImageContext
is long for the world. It was first introduced in iOS 2. It may be deprecated in iOS 11. The new APIs feel more appropriate to the design and philosophy of iOS, and these calls always stood out as a very odd approach.
But let me get back to the issues of writing “Swift Drawing”. When it comes to book, platform dichotomy hits hard. To get a sense of how much effort it would take to write about drawing for both platforms, I worked on my blend mode appendix sample code this weekend.
This is extremely simple code. It just iterates through the standard Core Graphics blend modes to create stock samples. The appendix describes each mode We’re talking about maybe a page of code. Getting it to Cocoa? Not so easy.
I put together a very rough skeleton granting Cocoa the basic abilities of Cocoa Touch. I’m not very happy with this code and I’m not sure I’m going to keep pushing on this code or developing its capabilities, which is why it’s in a gist and not a repo.
I suspect we may see new image APIs — both Cocoa and Cocoa Touch — this WWDC. I don’t want to compete with Apple APIs and I also don’t want to have to write two completely distinct drawing books: one for iOS and one for macOS.
So I’m still struggling because while most basic calls for CoreGraphics are interchangeable across platforms, the Cocoa and Cocoa Touch layers are not. If I go with samples that support both platforms, I have to pick one of the following approaches:
- create an artificial UIKit for Cocoa (that is a disconnect for macOS readers),
- create an unrealistic platform-independent backbone to unify Cocoa and Cocoa Touch, which means I can’t talk to Cocoa Touch readers in their native language (that is objectively bad), or
- stick with Cocoa Touch like I did in the original book (simplest but excludes macOS readers).
After this weekend’s experimenting, I’m leaning towards keeping things mostly iOS-centric. I may mix in a bit of “fake UIKit”.
I know I don’t want to go the route of “Color”/”Font”/”BezierPath”/”LayoutConstraint” cross platform types that I use in my own development work. It’s fine to use pseudo-types and typealiases internally but it’s not a way to introduce native tech in a book. Books should teach to the platform with the least distance between the reader and the APIs. No dependencies, few workarounds, minimal obfuscation.
I could build a small set of “fake UIKit” calls for macOS samples. This would allow me to focus discussions on the true cross-platform features like gradients, paths, colors, and bitmaps, without having to write two entire books at once, but I’m not sure it’s worth the effort or the hardship on macOS readers.
The costs look like this:
- Full macOS support would double my work.
- Adding compatibility discussions and partial macOS code would add about 60% or more work.
- A minimal “fake UIKit” and a few references in-book to this approach, I could probably squeak by with about 25-35%.
- Based on initial feedback, macOS sales would be about 10% at most of my readers.
Bottom line: macOS is a big old monkey wrench for this particular project. After investing a weekend, I’m not sure I want to go there but I’m interested in hearing your feedback after looking at my samples and prototype gists.