NSCalendarUnitYear
NSHipster.com was launched 2 years ago to the day, with a little article about NSIndexSet. Each week since has featured a new article on some obscure topic in Objective-C or Cocoa (with only a couple gaps), which have been read by millions of visitors in over 180 different countries.
This is actually the 101st article, which means that by television industry standards, this site is now suitable for broadcast syndication. (Coming soon to TBS!)
Let’s celebrate with some cake:
Cute, right? Let’s see what this looks like in code:
var cake Path = UIBezier Path()
cake Path.move To Point(CGPoint Make(31.5, 32.5))
cake Path.add Curve To Point(CGPoint Make(6.5, 66.1), control Point1: CGPoint Make(31.5, 32.5), control Point2: CGPoint Make(6.9, 46.3))
cake Path.add Curve To Point(CGPoint Make(6.5, 66.5), control Point1: CGPoint Make(6.5, 66.2), control Point2: CGPoint Make(6.5, 66.3))
cake Path.add Line To Point(CGPoint Make(6.5, 95))
...
Wait, hold up. What is this, Objective-C? Manipulating UIBezier
s isn’t exactly ground-breaking stuff, but with a few dozen more lines to go, we can make this a bit easier for ourselves.
How about we put some syntactic icing on this cake with some custom operators?
infix operator ---> { associativity left }
func ---> (left: UIBezier Path, right: (CGFloat, CGFloat)) -> UIBezier Path {
let (x, y) = right
left.move To Point(CGPoint Make(x, y))
return left
}
infix operator +- { associativity left }
func +- (left: UIBezier Path, right: (CGFloat, CGFloat)) -> UIBezier Path {
let (x, y) = right
left.add Line To Point(CGPoint Make(x, y))
return left
}
infix operator +~ { associativity left }
func +~ (left: UIBezier Path, right: ((CGFloat, CGFloat), (CGFloat, CGFloat), (CGFloat, CGFloat))) -> UIBezier Path {
let ((x1, y1), (x2, y2), (x3, y3)) = right
left.add Curve To Point(CGPoint Make(x1, y1), control Point1: CGPoint Make(x2, y2), control Point2: CGPoint Make(x3, y3))
return left
}
Get it?
--->
replacesmove
, whileTo Point +-
replacesadd
, andLine To Point +~
replacesadd
. This declaration also does away with all of the redundant calls toCurve To Point CGPoint
, opting instead for simple coordinate tuples.Make
Swift offers a great deal of flexibility in how a programmer structures their code. One feature that exemplifies this mantra of minimal constraints is the ability to add custom prefix, infix, and postfix operators. Swift’s syntax limits custom operators to be one or more of any of the following characters (provided an operator does not conflict with a reserved symbols, such as the ?
or !
used for optional values):
/ = - + * % < > ! & | ^ . ~.
Custom operators offer a powerful tool for cutting through cruft, redundancy, unnecessary repetition, and so on and so forth, et cetera. Combine them with other language features like patterns or chaining to craft DSLs perfectly suited to the task at hand.
Just… you know, don’t let this power go to your head.
After full Emoji support (let 🐶🐮
), custom operators are perhaps the shiniest new feature for anyone coming from Objective-C. And like any shiny new feature, it is destined to provide the most snark fodder for the “get off my lawn” set.
A Dramatization of the Perils of Shiny Swift Features
SCENE: SAN FRANCISCO, THE YEAR IS 2017
GREYBEARD:
So I inherited an old Swift codebase today, and I found this line of code—I swear to$DEITY
—it just reads😾 |--~~> 💩
.
BROGRAMMER
: shakes head
GREYBEARD
: What the hell am I supposed to make of that? Is, like, the piece of poo throwing a Hadouken, or is it about to get the business end of a corkscrew?
BROGRAMMER
: Truly, a philosophical quandary if ever there was one.
GREYBEARD
: Anyway, turns out, that statement just reloads nearby restaurants.
BROGRAMMER:
Dude, AFNetworking got weird with its 4.0 release.
GREYBEARD:
Indeed.
The moral of that cautionary tale: use custom operators and emoji sparingly.
(Or whatever, the very next code sample totally ignores that advice)
// Happy 2nd Birthday, NSHipster
// 😗💨🎂✨2️⃣
var 🍰 = UIBezier Path()
🍰 ---> ((31.5, 32.5))
+~ ((6.5, 66.1), (31.5, 32.5), (6.9, 46.3))
+~ ((6.5, 66.5), (6.5, 66.2), (6.5, 66.3))
+- ((6.5, 95))
+~ ((8.5, 96.9), (6.5, 96.1), (7.4, 97))
+- ((92, 93.1))
+~ ((94.2, 93), (93.1, 93), (94.1, 93))
+~ ((94.4, 91), (94.3, 93), (94.4, 92.1))
+~ ((94.4, 64.5), (94.4, 91), (94.4, 65.5))
+~ ((92.4, 61.5), (94.4, 62.5), (92.4, 61.5))
---> ((92.5, 89.4))
+~ ((90.5, 91.4), (92.5, 90.4), (91.6, 91.3))
+- ((10.5, 94.9))
+~ ((8.5, 93.1), (9.4, 94.9), (8.5, 94.1))
+~ ((8.5, 83), (8.5, 93.1), (8.5, 88.4))
+- ((92.5, 79.1))
+~ ((92.5, 89.4), (92.5, 84.5), (92.5, 89.4))
🍰.close Path()
🍰 ---> ((92.5, 76))
+- ((8.5, 80))
+~ ((8.5, 68.2), (8.5, 74.2), (8.5, 68.7))
+~ ((9.5, 67.1), (8.5, 67.3), (9.5, 67.1))
+- ((91.5, 63.5))
+~ ((92.5, 64.4), (91.5, 63.5), (92.5, 63.5))
+~ ((92.5, 76), (92.5, 64.9), (92.5, 70.3))
🍰.close Path()
var 📍 = UIBezier Path()
📍 ---> ((46, 47.5))
+~ ((41.5, 52), (46, 50), (44, 52))
+- ((41.5, 52))
+~ ((37, 47.5), (39, 52), (37, 50))
+- ((37, 19.8))
+~ ((41.5, 15.3), (37, 17.3), (39, 15.3))
+- ((41.5, 15.3))
+~ ((46, 19.8), (44, 15.3), (46, 17.3))
+- ((46, 47.5))
📍.close Path()
var 🔥 = UIBezier Path()
🔥.miter Limit = 4
🔥 ---> ((45.8, 8.4))
+~ ((41.7, 14), (45.8, 12.5), (44, 14))
+~ ((37.6, 8.4), (39.4, 14), (37.6, 12.5))
+~ ((41.8, 1), (37.6, 4.3), (41.8, 1))
+~ ((45.8, 8.4), (41.8, 1), (45.8, 4.3))
🔥.close Path()
UIColor.black Color().set Fill()
🍰.fill()
🔥.fill()
UIColor.white Color().set Fill()
UIColor.black Color().set Stroke()
📍.fill()
📍.stroke()
I’m as amazed as anyone that this actually compiles.
Everything is terrible.
Anyway, Happy 2nd Birthday, NSHipster!
Thank you for helping to make these last couple years the insanely great experience it’s been. I’ll do my part to keep things up for years to come.