Code structure and organization is a matter of pride for developers. Clear and consistent code signifies clear and consistent thought. Even though the compiler lacks a discerning palate when it comes to naming, whitespace, or documentation, it makes all of the difference for human collaborators.
Readers of NSHipster will no doubt remember the article about documentation published last year, but a lot has changed with Xcode 6 (fortunately, for the better, in most cases). So this week, we'll be documenting the here and now of documentation for aspiring Swift developers.
Let's dive in.
Ironically, much of the following is currently undocumented, and is subject to change or correction.
Since the early 00's, Headerdoc has been the documentation standard preferred by Apple. Starting off as little more than a Perl script parsing trumped-up Javadoc comments, Headerdoc would eventually be the engine behind Apple's developer documentation online and in Xcode.
With the announcements of WWDC 2014, the developer documentation was overhauled with a sleek new design that could accommodate switching between Swift & Objective-C. (If you've checked out any of the new iOS 8 APIs online, you've seen this in action)
What really comes as a surprise is that the format of documentation appears to have changed as well.
In the latest Xcode 6 beta, Headerdoc comments are not parsed correctly when invoking Quick Documentation (⌥ʘ
):
/** Lorem ipsum dolor sit amet. @param bar Consectetur adipisicing elit. @return Sed do eiusmod tempor.*/funcfoo(bar:String)->AnyObject{...}
What is parsed, however, is something markedly different:
/** Lorem ipsum dolor sit amet. :param: bar Consectetur adipisicing elit. :returns: Sed do eiusmod tempor.*/funcfoo(bar:String)->AnyObject{...}
It gets weirder.
Jump into a bridged Swift header for an Objective-C API, like say, HomeKit's HMCharacteristic
, and option-clicking does work. (Also, the documentation there uses /*!
to open documentation, rather than the conventional /**
).
Little is known about this new documentation format... but in these Wild West times of strict typing and loose morals, that's not enough to keep us from using it ourselves:
Update: Sources from inside Cupertino have confirmed that SourceKit (the private framework powering Xcode that y'all probably know best for crashing in Playgrounds) includes a primitive parser for reStructuredText. How reST is, well, re-structured and adapted to satisfy Apple's use case is something that remains to be seen.
importFoundation/// 🚲 A two-wheeled, human-powered mode of transportation.classBicycle{/** Frame and construction style. - Road: For streets or trails. - Touring: For long journeys. - Cruiser: For casual trips around town. - Hybrid: For general-purpose transportation. */publicenumStyle{caseRoad,Touring,Cruiser,Hybrid}/** Mechanism for converting pedal power into motion. - Fixed: A single, fixed gear. - Freewheel: A variable-speed, disengageable gear. */publicenumGearing{caseFixedcaseFreewheel(speeds:Int)}/** Hardware used for steering. - Riser: A casual handlebar. - Café: An upright handlebar. - Drop: A classic handlebar. - Bullhorn: A powerful handlebar. */enumHandlebar{caseRiser,Café,Drop,Bullhorn}/// The style of the bicycle.letstyle:Style/// The gearing of the bicycle.letgearing:Gearing/// The handlebar of the bicycle.lethandlebar:Handlebar/// The size of the frame, in centimeters.letframeSize:Int/// The number of trips travelled by the bicycle.private(set)varnumberOfTrips:Int/// The total distance travelled by the bicycle, in meters.private(set)vardistanceTravelled:Double/** Initializes a new bicycle with the provided parts and specifications. :param: style The style of the bicycle :param: gearing The gearing of the bicycle :param: handlebar The handlebar of the bicycle :param: centimeters The frame size of the bicycle, in centimeters :returns: A beautiful, brand-new, custom built just for you. */init(style:Style,gearing:Gearing,handlebar:Handlebar,frameSizecentimeters:Int){self.style=styleself.gearing=gearingself.handlebar=handlebarself.frameSize=centimetersself.numberOfTrips=0self.distanceTravelled=0.0}/** Take a bike out for a spin. :param: meters The distance to travel in meters. */functravel(distancemeters:Double){ifmeters>0.0{self.distanceTravelled+=metersself.numberOfTrips++}}}
Option-click on the Style
enum
declaration, and the description renders beautifully with a bulleted list:
Open Quick Documentation for the method travel
, and the parameter is parsed out into a separate field, as expected:
Again, not much is known about this new documentation format yet... as it's currently undocumented. But this article will be updated as soon as more is known. In the meantime, feel free to adopt the conventions described so far, as they're at least useful for the time being.
MARK / TODO / FIXME
In Objective-C, the pre-processor directive #pragma mark
is used to divide functionality into meaningful, easy-to-navigate sections. In Swift, there are no pre-processor directives (closest are the similarly-octothorp'd build configurations), but the same can be accomplished with the comment // MARK:
.
As of Xcode 6β4, the following comments will be surfaced in the Xcode source navigator:
// MARK:
(As with#pragma
, marks followed by a single dash (-
) will be preceded with a horizontal divider)// TODO:
// FIXME:
Other conventional comment tags, such as
NOTE
andXXX
are not recognized by Xcode.
To show these new tags in action, here's how the Bicycle
class could be extended to adopt the Printable
protocol, and implement description
.
// MARK: PrintableextensionBicycle:Printable{vardescription:String{vardescriptors:[String]=[]switchself.style{case.Road:descriptors+="A road bike for streets or trails"case.Touring:descriptors+="A touring bike for long journeys"case.Cruiser:descriptors+="A cruiser bike for casual trips around town"case.Hybrid:descriptors+="A hybrid bike for general-purpose transportation"}switchself.gearing{case.Fixed:descriptors+="with a single, fixed gear"case.Freewheel(letn):descriptors+="with a \(n)-speed freewheel gear"}switchself.handlebar{case.Riser:descriptors+="and casual, riser handlebars"case.Café:descriptors+="and upright, café handlebars"case.Drop:descriptors+="and classic, drop handlebars"case.Bullhorn:descriptors+="and powerful bullhorn handlebars"}descriptors+="on a \(self.frameSize)\" frame"// FIXME: Use a distance formatterdescriptors+="with a total of \(self.distanceTravelled) meters traveled over \(self.numberOfTrips) trips"// TODO: Allow bikes to be named?returnjoin(", ",descriptors)+"."}}
Bringing everything together in code:
letbike=Bicycle(style:.Road,gearing:.Freewheel(speeds:8),handlebar:.Drop,frameSize:53)bike.travel(distance:1_500)// Trip around the townbike.travel(distance:200)// Trip to the storeprintln(bike)// "A road bike for streets or trails, with a 8-speed freewheel gear, and classic, drop handlebars, on a 53" frame, with a total of 1700.0 meters traveled over 2 trips."
Although the tooling and documentation around Swift is still rapidly evolving, one would be wise to adopt good habits early, by using the new light markup language conventions for documentation, as well as MARK:
comments in Swift code going forward.
Go ahead and add it to your TODO:
list.