Quantcast
Channel: NSHipster
Viewing all 379 articles
Browse latest View live

MKGeodesicPolyline

$
0
0

We knew that the Earth was not flat long before 1492. Early navigators observed the way ships would dip out of view over the horizon many centuries before the Age of Discovery.

For many iOS developers, though, a flat MKMapView was a necessary conceit until recently.

What changed? The discovery of MKGeodesicPolyline, which is the subject of this week's article.


MKGeodesicPolyline was introduced to the Map Kit framework in iOS 7. As its name implies, it creates a geodesic—essentially a straight line over a curved surface.

On the surface of a sphereoblate spheroidgeoid, the shortest distance between two points on appears as an arc on a flat projection. Over large distances, this takes a pronounced, circular shape.

A MKGeodesicPolyline is created with an array of of 2 MKMapPoints or CLLocationCoordinate2Ds:

Creating an MKGeodesicPolyline

CLLocation*LAX=[[CLLocationalloc]initWithLatitude:33.9424955longitude:-118.4080684];CLLocation*JFK=[[CLLocationalloc]initWithLatitude:40.6397511longitude:-73.7789256];CLLocationCoordinate2Dcoordinates[2]={LAX.coordinate,JFK.coordinate};MKGeodesicPolyline*geodesicPolyline=[MKGeodesicPolylinepolylineWithCoordinates:coordinatescount:2];[mapViewaddOverlay:geodesicPolyline];

Although the overlay looks like a smooth curve, it is actually comprised of thousands of tiny line segments (true to its MKPolyline lineage):

NSLog(@"%d",geodesicPolyline.pointsCount)// 3984

Like any object conforming to the <MKOverlay> protocol, an MKGeodesicPolyline instance is displayed by adding to an MKMapView with -addOverlay: and implementing mapView:rendererForOverlay::

Rendering MKGeodesicPolyline on an MKMapView

#pragma mark - MKMapViewDelegate-(MKOverlayRenderer*)mapView:(MKMapView*)mapViewrendererForOverlay:(id<MKOverlay>)overlay{if(![overlayisKindOfClass:[MKPolylineclass]]){returnnil;}MKPolylineRenderer*renderer=[[MKPolylineRendereralloc]initWithPolyline:(MKPolyline*)overlay];renderer.lineWidth=3.0f;renderer.strokeColor=[UIColorblueColor];renderer.alpha=0.5;returnrenderer;}

MKGeodesicPolyline on an MKMapView

For comparison, here's the same geodesic overlaid with a route created from MKDirections:

MKGeodesicPolyline on an MKMapView compared to MKDirections Polyline

As the crow flies, it's 3,983 km.
As the wolf runs, it's 4,559 km—nearly 15% longer.
...and that's just distance; taking into account average travel speed, the total time is ~5 hours by air and 40+ hours by land.

Animating an MKAnnotationView on a MKGeodesicPolyline

Since geodesics make reasonable approximations for flight paths, a common use case would be to animate the trajectory of a flight over time.

To do this, we'll make properties for our map view and geodesic polyline between LAX and JFK, and add new properties for the planeAnnotation and planeAnnotationPosition (the index of the current map point for the polyline):

@interfaceMapViewController()<MKMapViewDelegate>@propertyMKMapView*mapView;@propertyMKGeodesicPolyline*flightpathPolyline;@propertyMKPointAnnotation*planeAnnotation;@propertyNSUIntegerplaneAnnotationPosition;@end

Next, right below the initialization of our map view and polyline, we create an MKPointAnnotation for our plane:

self.planeAnnotation=[[MKPointAnnotationalloc]init];self.planeAnnotation.title=NSLocalizedString(@"Plane",nil);[self.mapViewaddAnnotation:self.planeAnnotation];[selfupdatePlanePosition];

That call to updatePlanePosition in the last line ticks the animation and updates the position of the plane:

-(void)updatePlanePosition{staticNSUIntegerconststep=5;if(self.planeAnnotationPosition+step>self.flightpathPolyline.pointCount){return;}self.planeAnnotationPosition+=step;MKMapPointnextMapPoint=self.flightpathPolyline.points[self.planeAnnotationPosition];self.planeAnnotation.coordinate=MKCoordinateForMapPoint(nextMapPoint);[selfperformSelector:@selector(updatePlanePosition)withObject:nilafterDelay:0.03];}

We'll perform this method roughly 30 times a second, until the plane has arrived at its final destination.

Finally, we implement mapView:viewForAnnotation: to have the annotation render on the map view:

-(MKAnnotationView*)mapView:(MKMapView*)mapViewviewForAnnotation:(id<MKAnnotation>)annotation{staticNSString*PinIdentifier=@"Pin";MKAnnotationView*annotationView=[mapViewdequeueReusableAnnotationViewWithIdentifier:PinIdentifier];if(!annotationView){annotationView=[[MKAnnotationViewalloc]initWithAnnotation:annotationreuseIdentifier:PinIdentifier];};annotationView.image=[UIImageimageNamed:@"plane"];returnannotationView;}

MKAnnotationView without Rotation

Hmm.... close but no SkyMall Personalized Cigar Case Flask.

Let's update the rotation of the plane as it moves across its flightpath.

Rotating an MKAnnotationView along a Path

To calculate the plane's direction, we'll take the slope from the previous and next points:

MKMapPointpreviousMapPoint=self.flightpathPolyline.points[self.planeAnnotationPosition];self.planeAnnotationPosition+=step;MKMapPointnextMapPoint=self.flightpathPolyline.points[self.planeAnnotationPosition];self.planeDirection=XXDirectionBetweenPoints(previousMapPoint,nextMapPoint);self.planeAnnotation.coordinate=MKCoordinateForMapPoint(nextMapPoint);

XXDirectionBetweenPoints is a function that returns a CLLocationDirection (0 – 360 degrees, where North = 0) given two MKMapPoints.

We calculate from MKMapPoints rather than converted coordinates, because we're interested in the slope of the line on the flat projection.

staticCLLocationDirectionXXDirectionBetweenPoints(MKMapPointsourcePoint,MKMapPointdestinationPoint){doublex=destinationPoint.x-sourcePoint.x;doubley=destinationPoint.y-sourcePoint.y;returnfmod(XXRadiansToDegrees(atan2(y,x)),360.0f)+90.0f;}

That convenience function XXRadiansToDegrees (and its partner, XXDegreesToRadians) are simply:

staticinlinedoubleXXRadiansToDegrees(doubleradians){returnradians*180.0f/M_PI;}staticinlinedoubleXXDegreesToRadians(doubledegrees){returndegrees*M_PI/180.0f;}

That direction is stored in a new property, @property CLLocationDirection planeDirection; , calculated from self.planeDirection = CLDirectionBetweenPoints(currentMapPoint, nextMapPoint); in updatePlanePosition (ideally renamed to updatePlanePositionAndDirection with this addition). To make the annotation rotate, we apply a transform on annotationView:

annotationView.transform=CGAffineTransformRotate(self.mapView.transform,XXDegreesToRadians(self.planeDirection));

MKAnnotationView with Rotation

Ah much better! At last, we have mastered the skies with a fancy visualization, worthy of any travel-related app.


Perhaps more than any other system framework, MapKit has managed to get incrementally better, little by little with every iOS release [1][2]. For anyone with a touch-and-go relationship to the framework, returning after a few releases is a delightful experience of discovery and rediscovery.

I look forward to seeing what lies on the horizon with iOS 8 and beyond.


IBAction / IBOutlet / IBOutletCollection

$
0
0

In programming, what often begins as a necessary instruction eventually becomes a vestigial cue for humans. In the case of Objective-C, #pragma directives, method type encodings, and all but the most essential storage classes have been rendered essentially meaningless, as the compiler becomes increasingly sophisticated. Discarded and disregarded during the compilation phase, they nonetheless remain useful to the development process as a whole, insofar as what they can tell other developers about the code itself.

For developers just starting with Cocoa & Cocoa Touch, the IBAction, IBOutlet, and IBOutletCollection macros are particularly bewildering examples of this phenomenon. With their raison d'être‎ has been obscured by decades of change, confusion by anyone without sufficient context is completely understandable.

As we'll learn in this week's article, though having outgrown their technical necessity, they remain a vibrant tradition in the culture of Objective-C development.


Unlike other two-letter prefixes, IB does not refer to a system framework, but rather Interface Builder.

Interface Builder can trace its roots to the halcyon days of Objective-C, when it and Project Builder comprised the NeXTSTEP developer tools (circa 1988). Before it was subsumed into Xcode 4, Interface Builder remained remarkably unchanged from its 1.0 release. An iOS developer today would feel right at home on a NeXTSTEP workstation, control-dragging views into outlets.

Back when they were separate applications, it was a challenge to keep the object graph represented in a .nib document in Interface Builder synchronized with its corresponding .h& .m files in Project Builder (what would eventually become Xcode). IBOutlet and IBAction were used as keywords, to denote what parts of the code should be visible to Interface Builder.

IBAction and IBOutlet are, themselves, computationally meaningless, as their macro definitions (in UINibDeclarations.h) demonstrate:

#define IBAction void#define IBOutlet

Well actually, there's more than meets the eye. Scrying the Clang source code, we see that they're actually defined by attribute-backed attributes:

#define IBOutlet __attribute__((iboutlet))#define IBAction __attribute__((ibaction))

IBAction

As early as 2004 (and perhaps earlier), IBAction was no longer necessary for a method to be noticed by Interface Builder. Any method with a signature -(void){name}:(id)sender would be visible in the outlets pane.

Nevertheless, many developers find it useful to still use the IBAction return type in method declarations to denote that a particular method is connected to an outlet. Even for projects not using Storyboards / XIBs may choose to employ IBAction to call out target / action methods.

Naming IBAction Methods

Thanks to strong, and often compiler-enforced conventions, naming is especially important in Objective-C, so the question of how to name IBAction methods is one not taken lightly. Though there is some disagreement, the preferred convention is as follows:

  • Return type of IBAction.
  • Method name of an active verb, describing the specific action performed. Method names like didTapButton: or didPerformAction: sound more like things a delegate might be sent.
  • Required sender parameter of type id. All target / action methods will pass the sender of the action (usually the responder) to methods that take a parameter. If omitted in the method signature, things will still work.
  • Optional event parameter of type UIEvent *, named withEvent:(iOS only). In UIKit, a second UIEvent * parameter, corresponding to the touch, motion, or remote control event triggering the responder, will be passed to target / action methods accepting this second parameter. The convention is to use withEvent: in the method signature, to match the UIResponder APIs.

For example:

// YES-(IBAction)refresh:(id)sender;-(IBAction)toggleVisibility:(id)senderwithEvent:(UIEvent*)event;// NO-(IBAction)peformSomeAction;-(IBAction)didTapButton:(id)sender;

IBOutlet

Unlike IBAction, IBOutlet is still required for hooking up properties in code with objects in a Storyboard or XIB.

An IBOutlet connection is usually established between a view or control and its managing view controller (this is often done in addition to any IBActions that a view controller might be targeted to perform by a responder). However, an IBOutlet can also be used to expose a top-level property, like another controller or a property that could then be accessed by a referencing view controller.

When to use @property or ivar

As with anything in modern Objective-C, properties are preferred to direct ivar access. The same is true of IBOutlets:

// YES@interfaceGallantViewController : UIViewController@property(nonatomic,weak)IBOutletUISwitch*switch;@end// NO@interfaceGoofusViewController : UIViewController{IBOutletUISwitch*_switch}@end

Since properties are the conventional way to expose and access members of a class, both externally and internally, they are preferred in this case as well, if only for consistency.

When to use weak or strong

One unfortunate consequence (if you want to call it that) of ARC is the ambiguity of when a IBOutlet@property should be declared as weak or strong. The ambiguity arises from the fact that most outlets have no discernible behavioral differences between weak or strong—it just works.

...except when it doesn't... and things crash, or the compiler warns about weak or strong use.

So what should one do? Always declare IBOutlet properties as weak, except when they need to be strong, as explained by Apple in their Resource Programming Guide section on Nib Files:

Outlets should be changed to strong when the outlet should be considered to own the referenced object:

  • This is often the case with File’s Owner—top level objects in a nib file are frequently considered to be owned by the File’s Owner.
  • You may in some situations need an object from a nib file to exist outside of its original container. For example, you might have an outlet for a view that can be temporarily removed from its initial view hierarchy and must therefore be maintained independently.

The reason why most IBOutlet views can get away with weak ownership is that they are already owned within their respective view hierarchy, by their superview. This chain of ownership eventually works its way up to the view owned by the view controller itself. Spurious use of strong ownership on a view outlet has the potential to create a retain cycle.

IBOutletCollection

IBOutlet's obscure step-cousin-in-law-once-removed is IBOutletCollection. Introduced in iOS 4, this pseudo-keyword allows collections of IBOutlets to be defined in Interface Builder, by dragging connections to its collection members.

IBOutletCollection is #define'd in UINibDeclarations.h as:

#define IBOutletCollection(ClassName)

...which is defined in a much more satisfying way, again, in the Clang source code:

#define IBOutletCollection(ClassName) __attribute__((iboutletcollection(ClassName)))

Unlike IBAction or IBOutlet, IBOutletCollection takes a class name as an argument, which is, incidentally, as close to Apple-sanctioned generics as one gets in Objective-C.

As a top-level object, an IBOutletCollection@property should be declared strong, with an NSArray * type:

@property(nonatomic,strong)IBOutletCollection(UIButton)NSArray*buttons;

There are two rather curious things to note about an IBOutletCollection array:

  • Its order is not necessarily guaranteed. The order of an outlet collection appears to be roughly the order in which their connections are established in Interface Builder. However, there are numerous reports of that order changing across versions of Xcode, or as a natural consequence of version control. Nonetheless, having code rely on a fixed order is strongly discouraged.
  • No matter what type is declared for the property, an IBOutletCollection is always an NSArray. In fact, any type can be declared: NSSet *, id <NSFastEnumeration>—heck, even UIColor * (depending on your error flags)! No matter what you put, an IBOutletCollection will always be stored as an NSArray, so you might as well have that type match up in your declaration to avoid compiler warnings.

With the advent of Objective-C object literals, IBOutletCollection has fallen slightly out of favor—at least for the common use case of convenience accessors, as in:

for(UILabel*labelinlabels){label.font=[UIFontsystemFontOfSize:14];}

Since declaring a collection of outlets is now as easy as comma-delimiting them within @[], it may make just as much sense to do that as create a distinct collection.

Where IBOutletCollection really shines is how it allows for multiple to define a unique collection of outlets under a shared identifier. Another advantage over a code-defined NSArray literal is that a collection can contain outlets that themselves are not connected to File's Owner.

The next time you're managing a significant or variable number of outlets in an iOS view, take a look at IBOutletCollection.


IBAction, IBOutlet, and IBOutletCollection play important roles in development, on both the compiler level and human level. As Objective-C continues to rapidly evolve as a platform, it is likely that they may someday be as completely vestigial as the wings of flightless birds or eyes of cavefish.

For now, though, it's important to understand what they are, and how to use them, if you plan on creating apps in any capacity.

NSHipster Quiz #5

$
0
0

This past weekend, I had the honor of speaking at NSNorth, in Ottawa, Ontario. The conference focused on the aspects of community, culture, and family in programming, and I cannot think of a conference in recent memory that better exemplified these themes, both in its speakers and attendees.

With the help of NSNorth's organizers, Dan Byers and Philippe Casgrain, we were able to put together an NSHipster Pub Quiz. Because of time constraints, the format was a bit different than usual, with just 2 rounds instead of 4, and an activity sheet.

Nevertheless, the content was as challenging and the competition was as fierce as always, with the team led by (and named after) Jessie Char taking first place with an impressive 24 points.

As always, you can play along at home or at work with your colleagues. Here are the rules:

  • There are 2 Rounds and an activity sheet, with 10 questions each
  • Record answers on a separate sheet of paper
  • Each correct answer to a question gets you 1 point (unless otherwise specified)
  • Play with up to 5 friends for maximum enjoyment
  • Don't be lame and look things up on the Internet or in Xcode

Round 1: General Knowledge

Current events, miscellaneous tidbits, and random trivia. Following a time-honored traditions for NSHipster quizzes, the first round is always a mis-mash of people, places, and pop culture.

  1. In 2011, Apple deprecated Mac OS X's Common Data Security Architecture, leaving them unaffected by what recent vulnerability.
  2. According to rumors, Apple will be partnering with which company to add song recognition functionality to Siri in iOS 8?
  3. The White House expressed disappointment over a "selfie" of Boston Red Sox player David Ortiz and President Obama, due to allegations that it was a promotional stunt for which company?
  4. In Sony's forthcoming Steve Jobs biopic, which actor was recently rumored to being approached by director Danny Boyle to play the lead role? For a bonus point: which actor was previously confirmed for this role, before director David Fincher backed out of the project?
  5. In Apple's Q2 Earnings call, Tim Cook announced the company had acquired 24 companies so far in 2014, including Burstly, which is better known for what service for iOS developers?
  6. After a rumored $3.2 billion acquisition bid by Apple, which American record producer, rapper and entrepreneur has described himself as "the first billionaire in hip hop"? For a bonus point: what is his legal (i.e. non-stage) name?
  7. A widespread (and recently debunked) rumor of Apple announcing Lightning-cable-powered biometric ear buds was originally disclosed on which social network for Silicon Valley insiders?
  8. Oracle won an important victory in the U.S. Court of Appeals against Google in their suit regarding copyright claims of what?
  9. What hot new social networking app allows you to anonymously chat with patrons of its eponymous, popular American chain restaurant?
  10. If one were to sit down at a NeXTstation and open "/NextLibrary/Frameworks/AppKit.framework/Resources/", they would find the file "NSShowMe.tiff". Who is pictured in this photo?

NSShowMe.tiff

Round 2: Core Potpourri

With the fluff out of the way, it's now time to dive into some hardcore Cocoa fundamentals. How well do you know the standard library?

  1. What unit does a Bluetooth peripheral measure RSSI, or received signal strength intensity in?
  2. What is the return value of the following code: UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, @"image/jpeg", NULL)?
  3. What function must be called before calling SecTrustGetCertificateCount on a SecTrustRef?
  4. What UIKit class can be used to show the definition of a word?
  5. An SCNetworkReachabilityRef can be created from three different sets of arguments. Fill in the blank SCNetworkReachabilityCreateWith_______. (1 pt. each)
  6. mach_absolute_time() returns a count of Mach absolute time units. What function can be used to convert this into something more useful, like nanoseconds?
  7. How many arguments does CGRectDivide take?
  8. What function would you call to generate a random integer between 1 and N
  9. What CoreFoundation function can, among other things, transliterate between different writing systems?
  10. What is LLVM's logo? And, for a bonus point: What is GCC's logo?

Activity Sheet: NSAnagram

First introduced in NSHipster Quiz #4, NSAnagram has become loved and hated, in equal parts, by those who have dared to take the challenge. Each question is an anagram, whose letters can be rearranged to form the name of a class or type in a well-known system framework (hint: Foundation, CoreFoundation, CoreLocation, StoreKit, and UIKit are represented here). Good luck!

  1. Farms To Rent
  2. Zest On Mine!
  3. A Stressful Nude
  4. Non-payment Attacks, Sir!
  5. Allegiant Ace, Conglomerated
  6. Mental Burlesque Ruts
  7. Ulcer Porn: OFF
  8. Forgive Traded Crap
  9. Cautionary Mini Dam
  10. Coil Infatuation... Coil

Answers

Round 1: General Knowledge

  1. Heartbleed
  2. Shazam
  3. Samsung
  4. Leonardo DiCaprio, previously Christian Bale)
  5. TestFlight
  6. Dr. Dre, a.k.a Andre Romelle Young
  7. Secret
  8. Java APIs
  9. WhatsApplebees
  10. A young Scott Forstall

Round 2: Core Potpourri

  1. decibels (dB)
  2. public.jpeg
  3. SecTrustEvaluate
  4. UIReferenceLibraryViewController
  5. Address, AddressPair, Name
  6. mach_timebase_info()
  7. 5
  8. arc4random_uniform
  9. CFStringTransform
  10. LLVM's Logo is a wyvern, or dragon. GCC's Logo is an egg (with a gnu bursting out of it)

Round 3: NSAnagram

  1. NSFormatter
  2. NSTimeZone
  3. NSUserDefaults
  4. SKPaymentTransaction
  5. CLLocationManagerDelegate
  6. NSMutableURLRequest
  7. CFRunLoopRef
  8. CGDataProviderRef
  9. UIDynamicAnimator
  10. UILocalNotification

How did you do this time? Tweet out your score to see how you stack up to your peers!

Benchmarking

$
0
0

Abstractions are necessary for doing meaningful work, but they come at a cost. To work at a high level is to turn a blind eye to nonessential details in order to reason with larger logical chunks. Determining what information is important within a particular context, however, is challenging, and is at the heart of performance engineering.

By benchmarking, a programmer can uncover the hidden performance characteristics of their code, and use this information to optimize accordingly. It is an essential tool for any developer interested in making their apps faster (which is to say every self-respecting developer).


The etymology of the word "benchmark" can be traced back to 19th century land surveying. In its original sense, a benchmark was a cut made into stone to secure a "bench", or kind of bracket, used to mount measuring equipment. Its figurative meaning of "a standard by which something is measured" was later repurposed to all walks of epistemology.

In programming, there is a minor semantic distinction between a benchmark and the act of benchmarking:

A benchmark is a program made specifically to measure and compare broad performance characteristics of hardware and software configurations. By contrast, benchmarking, is a general term for when code is used to measure the performance of a system.

Benchmarking Performance in Objective-C

Benchmarks should be treated like any other epistemological discipline, with a firm grasp of the Scientific Method as well as statistics.

The Scientific Method outlines a series of steps to logically deduce answers for questions:

  1. Ask a Question
  2. Construct a Hypothesis
  3. Predict the Outcome
  4. Test the Hypothesis
  5. Analyze the Results

In the case of programming, there are generally two kinds of questions to be asked:

  • What are the absolute performance characteristics of this code? Is the procedure bound by computation or memory? What is the limiting behavior across different sample sizes?
  • What are the relative performance characteristics of this code, as compared to its alternatives? Which is faster, methodA or methodB?

Because the underlying factors of everything from the operating system down to the metal itself are extremely variable, performance should be measured across a large number of trials. For most applications, something on the order of 105 to 108 samples should be acceptable.

First Pass: CFAbsoluteTimeGetCurrent

For this example, let's take a look at the performance characteristics of adding an object to a mutable array.

To establish a benchmark, we specify a count of objects to add, and the number of iterations to run this process.

staticsize_tconstcount=1000;staticsize_tconstiterations=10000;

Since we're not testing the stack allocation of objects, we declare the object to be added to the array once, outside of the benchmark.

idobject=@"🐷";

Benchmarking is as simple as taking the time before running, and comparing it against the time after. CACurrentMediaTime() is a convenient way to measure time in seconds derived from mach_absolute_time.

Unlike NSDate or CFAbsoluteTimeGetCurrent() offsets, mach_absolute_time() and CACurrentMediaTime() are based on the internal host clock, a precise, monatomic measure, and not subject to changes in the external time reference, such as those caused by time zones, daylight savings, or leap seconds

for loops are used to increment count and iterations. Each iteration is enclosed by an @autoreleasepool, to keep the memory footprint low.

Putting it all together, here's a simple way to benchmark code in Objective-C:

CFTimeIntervalstartTime=CACurrentMediaTime();{for(size_ti=0;i<iterations;i++){@autoreleasepool{NSMutableArray*mutableArray=[NSMutableArrayarray];for(size_tj=0;j<count;j++){[mutableArrayaddObject:object];}}}}CFTimeIntervalendTime=CACurrentMediaTime();NSLog(@"Total Runtime: %g s",endTime-startTime);

The extra code block between startTime and endTime in the example below is unnecessary, but helps improve legibility and acts as a sanity check for variable scope

At this point, your NSHipster sense is probably tingling—as if to say, "Surely, there must be a better, more obscure way to do this!"

It's good to trust your instincts.

Allow me to introduce you to dispatch_benchmark.

Second Pass: dispatch_benchmark

dispatch_benchmark is part of libdispatch, a.k.a Grand Central Dispatch. Curiously, though, this function is not publicly declared, so you'll have to do that yourself:

externuint64_tdispatch_benchmark(size_tcount,void(^block)(void));

In addition to not having a public function definition, dispatch_benchmark also lacks public documentation in Xcode. Fortunately, it does have a man page:

man dispatch_benchmark(3)

The dispatch_benchmark function executes the given block multiple times according to the count variable and then returns the average number of nanoseconds per execution. This function is for debugging and performance analysis work. For the best results, pass a high count value to dispatch_benchmark.

Please look for inflection points with various data sets and keep the following facts in mind:

  • Code bound by computational bandwidth may be inferred by proportional changes in performance as concurrency is increased.
  • Code bound by memory bandwidth may be inferred by negligible changes in performance as concurrency is increased.
  • Code bound by critical sections may be inferred by retrograde changes in performance as concurrency is increased.
    • Intentional: locks, mutexes, and condition variables.
    • Accidental: unrelated and frequently modified data on the same cache-line.

If you happened to skim all of that, be encouraged to read through that again—those are remarkably well-written docs. In addition to satisfying the prime directive of documentation of describing how to use the function, it also lays out rather comprehensive and insightful guidelines on how to best make use of the function.

Here's what the previous example looks like if we were to use dispatch_benchmark instead:

uint64_tt=dispatch_benchmark(iterations,^{@autoreleasepool{NSMutableArray*mutableArray=[NSMutableArrayarray];for(size_ti=0;i<count;i++){[mutableArrayaddObject:object];}}});NSLog(@"[[NSMutableArray array] addObject:] Avg. Runtime: %llu ns",t);

Ahhh, much better. Nanoseconds are a suitably precise time unit, and dispatch_benchmark has a much nicer syntax than manually looping and calling CFAbsoluteTimeGetCurrent().

NSMutableArray array vs. arrayWithCapacity:... FIGHT!

Now that we've settled on the preferred way to run an absolute benchmark in Objective-C, let's do a comparative test.

For this example, let's consider the age-old question of "What difference does passing a capacity parameter into collection initialization make?", or more succinctly, "to -arrayWithCapacity: or not to -arrayWithCapacity: (that is the question)".

Let's find out:

uint64_tt_0=dispatch_benchmark(iterations,^{@autoreleasepool{NSMutableArray*mutableArray=[NSMutableArrayarray];for(size_ti=0;i<count;i++){[mutableArrayaddObject:object];}}});NSLog(@"[[NSMutableArray array] addObject:] Avg. Runtime: %llu ns",t_0);uint64_tt_1=dispatch_benchmark(iterations,^{@autoreleasepool{NSMutableArray*mutableArray=[NSMutableArrayarrayWithCapacity:count];for(size_ti=0;i<count;i++){[mutableArrayaddObject:object];}}});NSLog(@"[[NSMutableArray arrayWithCapacity] addObject:] Avg. Runtime: %llu ns",t_1);

Results

Testing on an iPhone Simulator running iOS 7.1, the results are as follows:

[[NSMutableArray array] addObject:]: Avg. Runtime 26119 ns
[[NSMutableArray arrayWithCapacity] addObject:] Avg. Runtime: 24158 ns

Across a large number of samples, there is a roughly 7% performance difference between mutable arrays with and without a capacity.

Although the results are indisputable (our benchmark works beautifully), the real trick is in figuring out how to interpret these results. It would be incorrect to use this benchmark alone to conclude that passing a capacity is always a good idea. Rather, this first benchmark tells us what questions to ask next:

  • What does this cost mean in absolute terms? In the spirit of avoiding premature optimization, is the difference in performance negligible in the grand scheme of things?
  • What difference does changing the count of objects make? Since initial capacity is used, presumably, to prevent resizing the array as it grows, how expensive is this operation for larger values of n?
  • What impact does an initial capacity have on the performance characteristics of other collection classes, like NSMutableSet or NSMutableDictionary? The public deserves an answer!

Common-Sense Benchmarking Guidelines

  • Know what question you're trying to answer. Although we should always endeavor to replace magical thinking with understanding, we must protect against misappropriating scientific methodologies to support incomplete reasoning. Take time to understand what your results mean in terms of the bigger picture, before jumping to any conclusions.
  • Do not ship benchmarking code in apps. Never mind the fact that dispatch_benchmark may or may not warrant an app rejection, benchmarked code has no place in a shipping product. Benchmarking should be done in separate one-off projects or an isolated test case.
  • Use Instruments to gain additional insights. Knowing the absolute runtime of a series of computations is valuable, but may not offer much insight into how to make that number smaller. Use Instruments to spec the call stack and memory footprint of the code in question, in order to get a better sense of what's actually going on.
  • Benchmark on the device. Just like any performance measurement, it should ultimately be done on the actual device. In most cases, general performance characteristics will be consistent between the simulator and device, but it's always worth verifying.
  • Don't prematurely optimize.This cannot be stressed enough. One of the most pervasive tendencies for developers is to fixate on what they perceive to be "slow code", before there's any real evidence to support that. Even for veteran developers, it's very easy to incorrectly predict where bottlenecks will be in an application. Don't waste your time chasing shadows. Let Instruments show you where your app is spending most of its time.

Richard Feynman once likened experiments in particle physics to "[finding] out what a watch is made out of and how the mechanism works [by] smashing two watches together and seeing what kinds of gear wheels fly out". Benchmarking code can feel like this at times.

With so many implementation details of our computational universe hidden to us through layers of abstraction, sometimes the best we can do to try to understand what's at play is to magnify code by a few orders of magnitude and see what we get.

Through a disciplined application of science and statistics in benchmarking, a developer is able to create well-reasoned conclusions about the performance characteristics of their code. Apply these principles and practices in your own project to come to your own conclusions and optimize accordingly.

CocoaPods

$
0
0

Civilization is built on infrastructure: roads, bridges, canals, sewers, pipes, wires, fiber. When well thought-out and implemented, infrastructure is a multiplying force that drives growth and development. But when such formative structures are absent or ad hoc, it feels as if progress is made in spite of the situation.

It all has to do with solving the problem of scale.

No matter what the medium, whether it's accommodating millions of families into a region, or integrating a large influx of developers into a language ecosystem, the challenges are the same.

In the case of Objective-C, CocoaPods provided a much-needed tool for channeling and organizing open source participation, and served as a rallying point for the community at a time of rapid growth and evolution.

This week on NSHipster, we'll celebrate the launch of CocoaPods 0.33, an important milestone for the project, by taking a look back at where we came from, discussing where we are now, and thinking about what's to come.

The following historical look at the origins of CocoaPods is, admittedly, a bit wordy for this publication. So if you're looking for technical details, feel free to skip directly to that.


A Look Back

For the first twenty or so years of its existence, Objective-C was not a widely known language. NeXT and later OS X were marginal platforms, with a comparatively small user base and developer community. Like any community, there were local user groups and mailing lists and websites, but open source collaboration was not a widespread phenomenon. Granted, Open Source was only just starting to pick up steam at that time, but there was no contemporary Objective-C equivalent to, for example, CPAN, the Comprehensive Perl Archive Network. Everyone took SDKs from Redwood City and Cupertino as far as they could, (maybe sprinkling in some code salvaged from a forum thread), but ultimately rolling their own solutions to pretty much everything else.

Objective-C and the iPhone

This went on until the summer of 2008, when iPhone OS was first opened up to third party developers. Almost overnight, Objective-C went from being an obscure C++/C# also-ran to the one of the most sought-after programmer qualifications. Millions of developers flocked from all walks of code, bringing an influx of new ideas and influences to the language.

Around this same time, GitHub had just launched, and was starting to change the way we thought about open source by enabling a new distributed, collaborative workflow.

In those early years of iPhone OS, we started to see the first massively adopted open source projects, like ASIHTTPRequest and Facebook's Three20. These first libraries and frameworks were built to fill in the gaps of app development on iPhone OS 2.0 and 3.0, and although largely made obsolete by subsequent OS releases or other projects, they demonstrated a significant break from the tradition of "every developer for themselves".

Of this new wave of developers, those coming from a Ruby background had a significant influence on the code and culture of Objective-C. Ruby, a spiritual successor to Perl, had its own package manager similar to CPAN: RubyGems.

Why so much influence from Ruby? Here's my pet theory: Ruby started gaining popular traction because of Rails, which hit 1.0 at the end of 2005. Given that the average duration of a startup gig seems to be about 1½ – 2½ years, the timing works out such that those first and second waves of bored Rails developers itching to jump ship would find a place in the emerging app space.

As open source contributions in Objective-C began to get some traction, the pain points of code distribution were starting to become pretty obvious:

Lacking frameworks, code for iOS could be packaged as a static library, but getting that set up and keeping code and static distributions in sync was an arduous process.

Another approach was to use Git submodules, and include the source directly in the project. But getting everything working, with linked frameworks and build flags configured, was not great either—especially at a time when the body of code was split between ARC and non-ARC.

Enter CocoaPods

CocoaPods was created by Eloy Durán on August 12, 2011.

Taking inspiration from Bundler and RubyGems, CocoaPods was designed to resolve a list of dependencies, download the required sources, and configure the existing project in such a way to be able to use them. Considering the challenges of working with a sparsely documented Xcode project format and build system, it's kind of a miracle that this exists at all.

Another notable decision made early on was to use a central Git repository as the database for all of the available libraries. Although there were certain logistical considerations with this approach, bootstrapping on GitHub provided a stable infrastructure, that allowed the team to iterate on building out the tool chain.

Since its initial proof-of-concept, the project has grown to include 14 core team members along with over 100 additional contributors. At the time of writing, there are nearly 5000 open source projects available for anyone to add to their project.

A significant portion of these prolific contributions from the open source community for Objective-C has been directly enabled and encouraged by increased ownership around tooling. Everyone involved should be commended for their hard work and dedication.

To break the 4th wall for a moment: Seriously, thank you, ladies and gentlemen of CocoaPods. You've done an amazing job. Keep up the good work!


Using CocoaPods

CocoaPods is easy to get started with both as a consumer and a library author. It should only take a few minutes to get set up.

For the most up-to-date information on how to use CocoaPods, check out the official guides.

Installing CocoaPods

CocoaPods is installed through RubyGems, the Ruby package manager, which comes with a standard Mac OS X install.

To install, open Terminal.app and enter the following command:

$ sudo gem install cocoapods

Now you should have the pod command available in the terminal.

If you're using a Ruby versioning manager, like rbenv, you may need to run a command to re-link a binary shim to the library (e.g. $ rbenv rehash).

Managing Dependencies

A dependency manager resolves a list of software requirements into a list of specific tags to download and integrate into a project.

Declaring requirements in such a way allows for project setup to be automated, which is general best practice for software development practice, no matter what the language. Even if you don't include third-party libraries, CocoaPods is still an invaluable tool for managing code dependencies across projects.

Podfile

A Podfile is where the dependencies of a project are listed. It is equivalent to Gemfile for Ruby projects using Bundler, or package.json for JavaScript projects using npm.

To create a Podfile, cd into the directory of your .xcodeproj file and enter the command:

$ pod init

Podfile

platform:ios,'7.0'target"AppName"doend

Dependencies can have varying levels of specificity. For most libraries, pegging to a minor or patch version is the safest and easiest way to include them in your project.

pod'X','~> 1.1'

CocoaPods follows Semantic Versioning conventions.

To include a library not included in the public specs database, a Git, Mercurial, or SVN repository can be used instead, for which a commit, branch, or tag can be specified.

pod'Y',:git=>'https://github.com/NSHipster/Y.git',:commit=>'b4dc0ffee'

Once all of the dependencies have been specified, they can be installed with:

$ pod install

When this is run, CocoaPods will recursively analyze the dependencies of each project, resolving them into a dependency graph, and serializing into a Podfile.lock file.

For example, if two libraries require AFNetworking, CocoaPods will determine a version that satisfies both requirements, and links them with a common installation of it.

CocoaPods will create a new Xcode project that creates static library targets for each dependency, and then links them all together into a libPods.a target. This static library becomes a dependency for your original application target. An xcworkspace file is created, and should be used from that point onward. This allows the original xcodeproj file to remain unchanged.

Subsequent invocations of pod install will add new pods or remove old pods according to the locked dependency graph. To update the individual dependencies of a project to the latest version, do the following:

$ pod update

Trying Out a CocoaPod

One great, but lesser-known, feature of CocoaPods is the try command, which allows you to test-drive a library before you add it to your project.

Invoking $ pod try with the name of a project in the public specs database opens up any example projects for the library:

$ pod try Ono

Ono.xcworkspace

Creating a CocoaPod

Being the de facto standard for Objective-C software distribution, CocoaPods is pretty much a requirement for open source projects with the intention of being used by others

Yes, it raises the barrier to entry for sharing your work, but the effort is minimal, and more than justifies itself. Taking a couple minutes to create a .podspec file saves every user at least that much time attempting to integrate it into their own projects.

Remember: raising the bar for contribution within a software ecosystem lowers the bar for participation.

Specification

A .podspec file is the atomic unit of a CocoaPods dependency. It specifies the name, version, license, and source files for a library, along with other metadata.

The official guide to the Podfile has some great information and examples.

NSHipsterKit.podspec

Pod::Spec.newdo|s|s.name='NSHipsterKit's.version='1.0.0's.license='MIT's.summary="A pretty obscure library.                You've probably never heard of it."s.homepage='http://nshipster.com's.authors={'Mattt Thompson'=>'mattt@nshipster.com'}s.social_media_url="https://twitter.com/mattt"s.source={:git=>'https://github.com/nshipster/NSHipsterKit.git',:tag=>'1.0.0'}s.source_files='NSHipsterKit'end

Once published to the public specs database, anyone could add it to their project, specifying their Podfile thusly:

Podfile

pod'NSHipsterKit','~> 1.0'

A .podspec file can be useful for organizing internal or private dependencies as well:

pod'Z',:path=>'path/to/directory/with/podspec'

Publishing a CocoaPod

New in CocoaPods 0.33 is the new Trunk service.

Although it worked brilliantly at first, the process of using Pull Requests on GitHub for managing new pods became something of a chore, both for library authors and spec organizers like Keith Smiley. Sometimes podspecs would be submitted without passing $ pod lint, causing the specs repo build to break. Other times, rogue commits from people other than the original library author would break things unexpectedly.

The CocoaPods Trunk service solves a lot of this, making the process nicer for everyone involved. Being a centralized service, it also has the added benefit of being able to get analytics for library usage, and other metrics.

To get started, you must first register your machine with the Trunk service. This is easy enough, just specify your email address (the one you use for committing library code) along with your name.

$ pod trunk register mattt@nshipster.com "Mattt Thompson"

Now, all it takes to publish your code to CocoaPods is a single command. The same command works for creating a new library or adding a new version to an existing one:

$ pod trunk push NAME.podspec

Authors of existing CocoaPods can claim their libraries with a few simple steps.


A Look Forward

CocoaPods exemplifies the compounding effect of infrastructure on a community. In a few short years, the Objective-C community has turned into something that we can feel proud to be part of.

CocoaPods is just one example of the great work being done on Objective-C infrastructure. Other community tools, like Travis CI, CocoaDocs, and Nomad have dramatically improved the everyday experience iOS and Mac OS X development for the community.

It can be tempting to be snarky, contrarian, or grumpy about the direction of a community. No matter what, though, let us all try our best to enter into dialogue in good faith, offering constructive criticism where we can. We should help each other to be good stewards of what we share, and strive towards empathy in all our interactions.

CocoaPods is a good thing for Objective-C. And it's only getting better.

CFHipsterRef Update

$
0
0

Today's a big day for NSHipster, and I have some important and exciting news to share.

Before I get into all of that, though, I wanted to thank everyone for their enthusiasm about CFHipsterRef, and NSHipster in general. Thank you for your kind words and encouragement. It's such a pleasure to write for y'all each and every week.

Okay, so some updates:

Digital / Physical Edition Questions

A number of you have contacted me with some confusion about whether the pre-order was for the physical or digital editions. To be clear: the pre-order is for a digital copy only. A print edition will be available for purchase on Amazon in the coming weeks.

I wrote about this briefly in a recent post, but the reality is that the logistics of shipping to over 50 countries internationally was a bit over my head, so this time, I'm going to just leave it to Amazon.

Although this was spelled out in the product listing, I can completely understand how one might be confused. As such, I'm going to hold off on pushing through any pre-orders until everyone has a chance to cancel their pre-order, if they want. After that time, charges will go through, and you'll receive your digital copy of CFHipsterRef.

Beta Release

WWDC is right around the corner, so there is a high likelihood that some information will be outdated come next week. Therefore, CFHipsterRef will be initially released as a beta book, with updates as information is added and corrected. I'm aiming to have a final version ready for print by the end of the month.

Wait... Another Book?

Here's where the narrative goes a little, well, off-book.

Today, I'm announcing the release of another title: "The NSHipster Fake Book", which is now available for download.

Everyone who pre-ordered "CFHipsterRef" received a free copy of "The NSHipster Fake Book", as a way to say thanks for their continued support of NSHipster. I will honor this for any new or cancelled pre-orders, as well. (For new pre-orders, you can expect an email with a promo code within a few hours).

I intended for both books to go out at the same time, but with the hold on pre-order charges, I hope that this will help tide you over until CFHipsterRef is pushed out.


tl;dr

  • The pre-order period for "CFHipsterRef" has been extended to give anyone who might have been confused about distribution the opportunity to cancel their digital pre-order, if they want.
  • "CFHipsterRef" will be released initially as a Beta book, with the final edition coming out after WWDC.
  • Everyone who pre-ordered "CFHipsterRef" will receive a free copy of the "The NSHipster Fake Book".

Sorry for the confusion and inconvenience. It's been a chaotic couple of weeks for me, so please bear with me on this.

If you have any questions at all about your purchase, do not hesitate to get in touch.

NSHipster Quiz #6

$
0
0

On June 3rd, we organized the second annual WWDC edition of the NSHipster Pub Quiz. Keeping in its tradition, questions ranged from random Apple trivia to obscure technical questions. For the second year in a row, the event was graciously hosted by New Relic, whose beautiful downtown San Francisco offices gave the event a feeling of distinction and class. We're also very thankful to Spotify& thoughtbot sponsor libations for the evening. Cheers!

With dozens of teams, comprised of developers from all around the world, the competition was fierce. But ultimately, it was a team known simply as nil that took the day, with 54 points total.

For everyone that couldn't make it to the event, here's an opportunity to play along at home. Some ground rules:

  • There are 3 Rounds, with 10 questions each
  • Record answers on a separate sheet of paper
  • Each correct answer to a question gets you 1 point (unless otherwise specified)
  • Play with up to 5 friends for maximum enjoyment
  • Don't be lame and look things up on the Internet or in Xcode

Round 1: General Knowledge

Current events, miscellaneous tidbits, and random trivia. Following a time-honored traditions for NSHipster quizzes, the first round is always a mis-mash of people, places, and pop culture.

  1. On iOS 8, what magic phrase can be used to activate Siri, when the device is plugged in?
  2. What game, crestfallen by the runaway success of its clone, 2048, was at least slightly vindicated last night with an ADA win?
  3. Which alternative search engine was added to the latest release of Safari?
  4. Weeks after its announcement, Apple finally confirmed its $3B acquisition of Beats Electronics. What is the name of Dre’s Co-founder?
  5. Yosemite is, of course, the code name of Mac OS X 10.10, but this code name was used before. What was the product? (Hint: It was released in 1999 and had a top clock speed of 450MHz)
  6. What is the name of the valley in Yosemite that was flooded after construction of the O'Shaughnessy Dam in 1927, which provides drinking water to San Francisco?
  7. Much of the reason why Yosemite exists today is thanks to the Sierra Club and a Scottish-born naturalist. What is this gentleman's name?
  8. 20 years ago, Apple launched a new experimental language. It had a syntax like this: let x :: <integer> = 2;. What was this language’s name?
  9. What does a Swift eat?
  10. What is the birdwatching term for the overall impression or appearance of a bird based on its shape, posture, & flying style?

Round 2: So You Think You Can Swift?

Having only been introduced the day before, Swift was fresh on everyone's minds, and the hot topic of conversation. To put everyone's knowledge to the test, the following 10 exercises were posed to attendees.

For anyone revisiting this quiz months or years after the fact, this should be incredibly easy. But just imagine coming into these questions having only skimmed a few hundred pages of the Swift iBook (if at all). And now imagine that you're a beer and a half into a late night during the week of WWDC. Now you can start to appreciate how grumpy this round made a lot of people.

  1. Declare a constant d equal to 3.0.
  2. Declare a variable s of type String.
  3. Interpolate the value of d into a String literal.
  4. Set a var b to 28 using an octal literal.
  5. Declare an optional property of type Int named x.
  6. Declare a Highlander enum of type Int, with an element named "One".
  7. Override viewDidLoad in a UIViewController Subclass.
  8. Declare a class C that adopting the NSCoding protocol.
  9. Alias String as Rope.
  10. Declare a protocol method m, which returns both an Int, and a Dictionary, with String keys and any value.

Round 3: Music Round

Rounding out the competition was the venerable staple of pub trivia everywhere: the music round!

The theme of this music round is—you guessed it—songs used in Apple commercials. For each song, score yourself a point for the correct title, artist, and Apple product advertised.

Due to the peculiarities of embedded video, you may have to click through in order to hear the song. Don't worry—the link is to the song itself, not the Apple ad, so you'll at least have something to puzzle over.


Answers

Round 1: General Knowledge

  1. "Hey Siri"
  2. Threes
  3. DuckDuckGo
  4. Jimmy Iovine
  5. Power Macintosh G3 (Blue & White)
  6. Hetch Hetchy
  7. John Muir
  8. Dylan
  9. Insects
  10. Jizz

Round 2: So You Think You Can Swift?

  1. let d = 3.0
  2. var s: String
  3. "\(d)"
  4. var b = 0o34
  5. var x: Int?
  6. enum Highlander: Int { case One = 1}
  7. override func viewDidLoad() { ... }
  8. class C: NSCoding
  9. typealias Rope = String
  10. func m() -> (Int, Dictionary<String,Any>)

Round 3: Music Round

  1. iPod 3G: Jet — "Are You Gonna Be My Girl?"
  2. G4 Cube: The Jimi Hendrix Experience — "Purple Haze"
  3. iPod Shuffle: Caesars — "Jerk It Out"
  4. iTunes: Green Day — "I Fought The Law"
  5. iPod 4G (U2 Special Edition): U2 — "Vertigo"
  6. Mac OS X (First-Run Experience): Honeycut — "Exodus Honey"
  7. iPod 1G: The Propellerheads — "Take California"
  8. iPhone 5s: Pixies — "Gigantic"
  9. iBook: Miles Davis — "Flamenco Sketches" (with voiceover by Jeff Goldblum)
  10. iMac "Sage": Kermit The Frog — "(It’s Not Easy) Bein' Green"

How did you do this time? Tweet out your score to see how you stack up to your peers!

iOS 8

$
0
0

Ask anyone, and they'll tell you: WWDC 2014 was the one of the most exciting in recent memory. It was, first and foremost, a developer event, with nary a hardware announcement to upstage the latest software & developer tools.

And boy howdy, was there a lot to be excited about.

The announcements from iOS 8 & OS X Yosemite alone would have made 2014 a bellwether year for the Apple platform, with Extensions, Continuity, SpriteKit enhancements, SceneKit for iOS, Metal, Game HealthKit, HomeKit, Local Authentication, and a brand new Photos framework. Not to mention the dramatic improvements to Xcode & Interface Builder, a revamped iTunes Connect, TestFlight, Crash Reports, and CloudKit. And oh yeah—Swift.

The kicker? Apple has graciously relaxed its NDA for new technologies, meaning that we don't have to wait to talk about all of the shiny new toys we have to play with.

This week, we'll take a look beneath the headline features, and share some of the more obscure APIs that everyone should know about.

From here on out, NSHipster will primarily write code samples in Swift, with the occasional Objective-C throwback where appropriate. By the end of the summer, we hope to have all of the existing code samples ported to Swift, with the option to toggle between languages.


NSProcessInfo -isOperatingSystemAtLeastVersion

Forget [[UIDevice currentDevice] systemVersion] and NSFoundationVersionNumber, there's a new way to determine the current operating system in code: NSProcessInfo -isOperatingSystemAtLeastVersion

importFoundationletyosemite=NSOperatingSystemVersion(majorVersion:10,minorVersion:10,patchVersion:0)NSProcessInfo().isOperatingSystemAtLeastVersion(yosemite)// false

Keep in mind, however, that a test for capability, such as with SomeClass.class or respondsToSelector:, is preferable to checking the OS version. Compiler macros in C or Swift can be used to conditionally compile source based on the build configuration of the target.

New NSFormatter Subclasses

One the features most sorely lacking in Foundation was the ability to work with units for quantities like mass or length. In iOS 8 and OS X Yosemite, three new classes were introduced that fills the gap: NSEnergyFormatter, NSMassFormatter, & NSLengthFormatter.

This effectively doubles the number of NSFormatter subclasses in Foundation, which was previously limited to NSNumberFormatter, NSDateFormatter, & NSByteCountFormatter.

Although these new formatter classes are part of Foundation, they were added primarily for use in HealthKit.

NSEnergyFormatter

NSEnergyFormatter formats energy in Joules, the raw unit of work for exercises, and Calories, which is used when working with nutrition information.

letenergyFormatter=NSEnergyFormatter()energyFormatter.forFoodEnergyUse=trueletjoules=10_000.0println(energyFormatter.stringFromJoules(joules))// "2.39 Cal"

NSMassFormatter

Although the fundamental unit of physical existence, mass is pretty much relegated to tracking the weight of users in HealthKit. Yes, mass and weight are different, but this is programming, not science class, so stop being pedantic.

letmassFormatter=NSMassFormatter()letkilograms=60.0println(massFormatter.stringFromKilograms(kilograms))// "132 lb"

NSLengthFormatter

Rounding out the new NSFormatter subclasses is NSLengthFormatter. Think of it as a more useful version of MKDistanceFormatter, with more unit options and formatting options.

letlengthFormatter=NSLengthFormatter()letmeters=5_000.0println(lengthFormatter.stringFromMeters(meters))// "3.107 mi"

CMPedometer

Continuing on iOS 8's health kick, CMStepCounter is revamped in the latest release. CMPedometer is a strict improvement over its predecessor, with the ability to query from discrete points in time, track both steps and distance, and even calculate how many flights of stairs were climbed.

It's amazing what that M7 chip is capable of.

importCoreMotionletlengthFormatter=NSLengthFormatter()letpedometer=CMPedometer()pedometer.startPedometerUpdatesFromDate(NSDate(),withHandler:{data,errorinif!error{println("Steps Taken: \(data.numberOfSteps)")letdistance=data.distance.doubleValueprintln("Distance: \(lengthFormatter.stringFromMeters(distance))")lettime=data.endDate.timeIntervalSinceDate(data.startDate)letspeed=distance/timeprintln("Speed: \(lengthFormatter.stringFromMeters(speed)) / s")}})

CMAltimeter

On supported devices, a CMPedometer's stats on floorsAscended / floorsDescended can be augmented with CMAltimeter to get a more granular look at vertical distance traveled:

importCoreMotionletaltimeter=CMAltimeter()ifCMAltimeter.isRelativeAltitudeAvailable(){altimeter.startRelativeAltitudeUpdatesToQueue(NSOperationQueue.mainQueue(),withHandler:{data,errorinif!error{println("Relative Altitude: \(data.relativeAltitude)")}})}

CLFloor

CLFloor is a new API in iOS 8 that ties the new features in CoreMotion with Apple's ambitious plan to map the interiors of the largest buildings in the world. Look for this information to play a significant role in future hyperlocal mapping applications.

importCoreLocationclassLocationManagerDelegate:NSObject,CLLocationManagerDelegate{funclocationManager(manager:CLLocationManager!,didUpdateLocationslocations:AnyObject[]!){letlocation:CLLocation?=locations[0]as?CLLocationifletfloor:CLFloor?=location?.floor{println("Current Floor: \(floor?.level)")}}}letmanager=CLLocationManager()manager.delegate=LocationManagerDelegate()manager.startUpdatingLocation()

HKStatistics

As a framework, HealthKit covers a lot of ground, with dozens of new classes and constants. A good place to start, in terms of understanding what's possible is HKStatistics.

HealthKit manages your biometrics from all of your devices in a single unified API. Statistics on things like heart rate, caloric intake, and aerobic output can be tracked and aggregated in powerful ways.

The following example shows how statistics summed over the duration of the day can be grouped and interpreted individually:

importHealthKitletcollection:HKStatisticsCollection?=...letstatistics:HKStatistics?=collection!.statisticsForDate(NSDate())foritem:AnyObjectinstatistics!.sources{ifletsource=itemas?HKSource{ifletquantity:HKQuantity=statistics!.sumQuantityForSource(source){ifquantity.isCompatibleWithUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo)){letmassFormatter=NSMassFormatter()letkilograms=quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo))println(massFormatter.stringFromKilograms(kilograms))}ifquantity.isCompatibleWithUnit(HKUnit.meterUnit()){letlengthFormatter=NSLengthFormatter()letmeters=quantity.doubleValueForUnit(HKUnit.meterUnit())println(lengthFormatter.stringFromMeters(meters))}ifquantity.isCompatibleWithUnit(HKUnit.jouleUnit()){letenergyFormatter=NSEnergyFormatter()letjoules=quantity.doubleValueForUnit(HKUnit.jouleUnit())println(energyFormatter.stringFromJoules(joules))}}}}

NSHipster will be covering a lot more about HealthKit in future editions, so stay tuned!

NSStream +getStreamsToHostWithName

In many ways, WWDC 2014 was the year that Apple fixed their shit. Small things, like adding the missing NSStream initializer for creating a bound stream pair (without resorting to awkwardly-bridged CFStreamCreatePairWithSocketToHost call). Behold: +[NSStream getStreamsToHostWithName:port:inputStream:outputStream:]

varinputStream:NSInputStreamvaroutputStream:NSOutputStreamNSStream.getStreamsToHostWithName(hostname:"nshipster.com",port:5432,inputStream:&inputStream,outputStream:&outputStream)

NSString -localizedCaseInsensitiveContainsString

Also filed under: "small but solid fixes", is this convenience method for NSString:

letstring:NSString="Café"letsubstring:NSString="É"string.localizedCaseInsensitiveContainsString(substring)// true

CTRubyAnnotationRef

If you're a linguistics and typography nerd, this new addition to the CoreText framework may have you standing up on your chair and cheering. "What's with Jim? Is it me, or has he been acting kind of weird since his trip to San Francisco?", they'll say, looking at you atop your desk as you tear your clothes off your body in a frenzy of pure ecstasy. "Yeah, remind me not to stay in the Tenderloin for next year's conference."

...oh right. Ruby. No, not Ruby. Ruby. It's used to display the pronunciation of characters in certain Asian scripts.

@importCoreText;NSString*kanji=@"猫";NSString*hiragana=@"ねこ";CFStringReffurigana[kCTRubyPositionCount]={(__bridgeCFStringRef)hiragana,NULL,NULL,NULL};CTRubyAnnotationRefruby=CTRubyAnnotationCreate(kCTRubyAlignmentAuto,kCTRubyOverhangAuto,0.5,furigana);

Admittedly, the documentation isn't entirely clear on how exactly to incorporate this into the rest of your CoreText drawing calls, but the result would look something like this:

ねこ

New Calendar Identifiers

What's even nerdier than Ruby annotations? The new calendar identifiers added to iOS 8 & OS X Yosemite. This update brings Foundation up to the latest version of the CLDR:

(Sadly, the French Republican Calendar is still but a twinkle in the eyes of NSHipsters everywhere)

  • NSCalendarIdentifierCoptic: a.k.a Alexandrian calendar, is used by the Coptic Orthodox Church.
  • NSCalendarIdentifierEthiopicAmeteMihret: Ethiopic calendar, Amete Mihret (epoch approx. 8 C.E.)
  • NSCalendarIdentifierEthiopicAmeteAlem: Ethiopic calendar, Amete Alem (epoch approx. 5493 B.C.E.)
  • NSCalendarIdentifierIslamicTabular: A simple tabular Islamic calendar using the astronomical/Thursday epoch of CE 622 July 15.
  • NSCalendarIdentifierIslamicUmmAlQura: The Islamic Umm al-Qura calendar used in Saudi Arabia. This is based on astronomical calculation, instead of tabular behavior.

NSURLCredentialStorage

The Foundation URL Loading System has remained relatively unchanged since last year's NSURLSession blowout. However, NSURLCredentialStorage has been given some TLC, with new functions that get and set credentials for tasks in asynchronous, non-blocking fashion.

importFoundationletsession=NSURLSession()lettask=session.dataTaskWithURL(NSURL(string:"http://nshipster.com"),completionHandler:{data,response,errorin// ...})letprotectionSpace=NSURLProtectionSpace()NSURLCredentialStorage.getCredentialsForProtectionSpace(protectionSpace:protectionSpace,task:task,completionHandler:{credentialsin// ...})

kUTTypeToDoItem

Looking through the latest API diffs, one might notice the large number of new UTIs constants. One that caught my eye was kUTTypeToDoItem:

importMobileCoreServiceskUTTypeToDoItem// "public.to-do-item"

As a public type, iOS & OS X now provide a unified way to share tasks between applications. If you happen to work on a task management tool (and, let's be honest, the chances are extremely good, considering how damn many of them there are in the App Store), proper integration with this system type should be put at the top of your list.

kCGImageMetadataShouldExcludeGPS

Most users are completely unaware that most pictures taken with phones these days include GPS metadata. Countless individuals have had their privacy breached because of this small detail.

New to the Image I/O framework is a convenient new option for CGImageDestination: kCGImageMetadataShouldExcludeGPS, which does what you'd expect.

@importUIKit;@importImageIO;@importMobileCoreServices;UIImage*image=...;NSURL*fileURL=[NSURLfileURLWithPath:@"/path/to/output.jpg"];NSString*UTI=kUTTypeJPEG;NSDictionary*options=@{(__bridgeid)kCGImageDestinationLossyCompressionQuality:@(0.75),(__bridgeid)kCGImageMetadataShouldExcludeGPS:@(YES),};CGImageDestinationRefimageDestinationRef=CGImageDestinationCreateWithURL((__bridgeCFURLRef)fileURL,(__bridgeCFStringRef)UTI,1,NULL);CGImageDestinationAddImage(imageDestinationRef,[imageCGImage],(__bridgeCFDictionaryRef)options);CGImageDestinationFinalize(imageDestinationRef);CFRelease(imageDestinationRef);

WTF_PLATFORM_IOS

#define WTF_PLATFORM_IOS has been removed from JavaScriptCore. It will be missed.

WKWebView

UIWebView is dead. Long live WKWebView.

WKWebView offers Safari-level performance to your own app, and further improves on UIWebView with preferences and configurations:

importWebKitletpreferences=WKPreferences()preferences.javaScriptCanOpenWindowsAutomatically=falseletconfiguration=WKWebViewConfiguration()configuration.preferences=preferencesletwebView=WKWebView(frame:self.view.bounds,configuration:configuration)letrequest=NSURLRequest(URL:NSURL(string:"http://nshipster.com"))webView.loadRequest(request)

NSQualityOfService

Threads have been systematically de-emphasized from the conceptual foundation of Apple frameworks. This has been a good thing for developers.

Following this trend is a change to NSOperation in the latest APIs. A new qualityOfService property replaces the threadPriority. These new semantics allow an app to defer non-critical work to ensure a consistently great user experience.

The NSQualityOfService enum defines the following values:

  • UserInteractive: UserInteractive QoS is used when performing work that is related to graphically intensive work such as scrolling or animating.
  • UserInitiated: UserInitiated QoS is used for performing work that has been explicitly requested by the user, but does not require millisecond accuracy like animations. For example, if a user requests an email app to check for mail right now.
  • Utility: Utility QoS is used for performing work that has been requested by the user to happen automatically. For example, an email app may be configured to automatically check for mail every 5 minutes. It is not a problem if the email check is deferred by a few minutes if the system is extremely limited in resources.
  • Background: Background QoS is used for performing work that the user may not even be aware is happening on their behalf. For example, an email app may use this to perform indexing for a search.

Quality of Service is used throughout Foundation in iOS 8 & OS X Yosemite, so be on the lookout for opportunities to capitalize on this new feature.

LocalAuthentication

Finally, one of the most anticipated features of iOS 8: LocalAuthentication. Ever since TouchID was introduced with the iPhone 5s, developers have been salivating at the prospect of using that in their own app.

Imagine: with CloudKit and LocalAuthentication, nearly all of the friction to creating a user account is gone. Just scan your fingerprint, and you're in.

LocalAuthentication works in terms of an LAContext class, which evaluates a specified policy, and gives a thumbs up or thumbs down on user authentication. At no point is any biometric information made available to the application—everything is kept safe on the hardware itself.

LAContext*context=[[LAContextalloc]init];NSError*error=nil;if([contextcanEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometricserror:&error]){[contextevaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometricslocalizedReason:NSLocalizedString(@"...",nil)reply:^(BOOLsuccess,NSError*error){if(success){// ...}else{NSLog(@"%@",error);}}];}else{NSLog(@"%@",error);}

Although it seems like all that anyone can talk about these days is Swift, it'd be a shame if we ignored all of the neat things iOS 8 & OS X Yosemite allow us to actually do with this new language.

If you're feeling adventurous, dive into the iOS 7.1 to 8.0 API diffs to really appreciate the magnitude of new technologies to discover. Granted, of the 4000+ new APIs, at least half of those are slight changes to Accelerate functions or methods becoming properties, but still... Have at it!


Clang Diagnostics

$
0
0

Diagnostics combine logic with analytics to arrive at a conclusion. It's science and engineering at their purest. It's human reasoning at its most potent.

Within the medical profession, a diagnosis is made through instinct backed by lab samples. For industrial manufacturing, one diagnoses a product fault through an equal application of statistics and gumption.

For us developers, our medium of code informs the production of subsequent code, creating a positive feedback loop that has catapulted the development of technology exponentially over the last half century. For us Objective-C developers specifically, the most effective diagnostics come from Clang.

Clang is the C / Objective-C front-end to the LLVM compiler. It has a deep understanding of the syntax and semantics of Objective-C, and is much of the reason that Objective-C is such a capable language today.

That amazing readout you get when you "Build & Analyze" (⌘⇧B) is a function of the softer, more contemplative side of Clang: its code diagnostics.

In our article about #pragma, we quipped:

Pro tip: Try setting the -Weverything flag and checking the "Treat Warnings as Errors" box your build settings. This turns on Hard Mode in Xcode.

Now, we stand by this advice, and encourage other developers to step up their game and treat build warnings more seriously. However, there are some situations in which you and Clang reach an impasse. For example, consider the following switch statement:

switch(style){caseUITableViewCellStyleDefault:
    caseUITableViewCellStyleValue1:
    caseUITableViewCellStyleValue2:
    caseUITableViewCellStyleSubtitle:
        // ...default:return;}

When certain flags are enabled, Clang will complain that the "default label in switch which covers all enumeration values". However, if we know that, zooming out into a larger context, style is (for better or worse) derived from an external representation (e.g. JSON resource) that allows for unconstrained NSInteger values, the default case is a necessary safeguard. The only way to insist on this inevitability is to use #pragma to ignore a warning flag temporarily:

push& pop are used to save and restore the compiler state, similar to Core Graphics or OpenGL contexts.

#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wcovered-switch-default"switch(style){caseUITableViewCellStyleDefault:
    caseUITableViewCellStyleValue1:
    caseUITableViewCellStyleValue2:
    caseUITableViewCellStyleSubtitle:
        // ...default:return;}#pragma clang diagnostic pop

Again, and this cannot be stressed enough, Clang is right at least 99% of the time. Actually fixing an analyzer warning is strongly preferred to ignoring it. Use #pragma clang diagnostic ignored as a method of last resort.

The problem is that given a warning string, it is very difficult to determine what its corresponding warning flag is. Unless a random Stack Overflow question pops up for the given error string, you're pretty much out of luck.

So this week, as a public service, we've compiled a (mostly) comprehensive list of Clang warning strings and their associated flags:

Like our article on NSError, this is more of an article for future reference than a formal explanation. Keep this page bookmarked for the next time that you happen to run into this situation.


Clang Diagnostic Warning Flags & Messages

Lex (liblex)

Warning FlagMessage
`-W#pragma-messages""%0"
`-W#warnings""%0"
-Wambiguous-macro"ambiguous expansion of macro %0"
-Wauto-import"treating #%select{include
-Wbackslash-newline-escape"backslash and newline separated by space"
-Wc++11-compat"'%0' is a keyword in C++11"
-Wc++11-compat"identifier after literal will be treated as a user-defined literal suffix in C++11"
-Wc++98-c++11-compat-pedantic"binary integer literals are incompatible with C++ standards before C++1y"
-Wc++98-c++11-compat"digit separators are incompatible with C++ standards before C++1y"
-Wc++98-compat-pedantic"#line number greater than 32767 is incompatible with C++98"
-Wc++98-compat-pedantic"C++98 requires newline at end of file"
-Wc++98-compat-pedantic"empty macro arguments are incompatible with C++98"
-Wc++98-compat-pedantic"variadic macros are incompatible with C++98"
-Wc++98-compat"'::' is treated as digraph ':' (aka '[') followed by ':' in C++98"
-Wc++98-compat"raw string literals are incompatible with C++98"
-Wc++98-compat"specifying character '%0' with a universal character name is incompatible with C++98"
-Wc++98-compat"unicode literals are incompatible with C++98"
-Wc++98-compat"universal character name referring to a control character is incompatible with C++98"
-Wc++98-compat"using this character in an identifier is incompatible with C++98"
-Wc99-compat"%select{using this character in an identifier
-Wc99-compat"unicode literals are incompatible with C99"
-Wcomment"'/*' within block comment"
-Wcomment"escaped newline between */ characters at block comment end"
-Wdisabled-macro-expansion"disabled expansion of recursive macro", DefaultIgnore
-Wheader-guard"%0 is used as a header guard here, followed by #define of a different macro"
-Wignored-attributes"unknown attribute '%0'"
-Wincomplete-module"header '%0' is included in module '%1' but not listed in module map"
-Wincomplete-umbrella"umbrella header for module '%0' does not include header '%1'"
-Winvalid-token-paste"pasting formed '%0', an invalid preprocessing token", DefaultError
-Wmalformed-warning-check"__has_warning expected option name (e.g. \"-Wundef\")"
-Wnewline-eof"no newline at end of file"
-Wnull-character"null character ignored"
-Wnull-character"null character(s) preserved in character literal"
-Wnull-character"null character(s) preserved in string literal"
-Wtrigraphs"ignored trigraph would end block comment"
-Wtrigraphs"trigraph ignored"
-Wundef, DefaultIgnore"%0 is not defined, evaluates to 0"
-Wunicode"incomplete universal character name treating as '\' followed by identifier"
-Wunicode"universal character name refers to a surrogate character"
-Wunicode"universal character names are only valid in C99 or C++ treating as '\' followed by identifier"
-Wunicode"\%0 used with no following hex digits treating as '\' followed by identifier"
-Wunknown-pragmas"pragma STDC FENV_ACCESS ON is not supported, ignoring pragma"
-Wunknown-pragmas"unknown pragma ignored"
-Wunused-macros"macro is not used", DefaultIgnore

Parse (libparse)

Warning FlagMessage
-Warc-bridge-casts-disallowed-in-nonarc"'%0' casts have no effect when not using ARC"
-Wattributes"unknown __declspec attribute %0 ignored"
-Wavailability"'unavailable' availability overrides all other availability information"
-Wc++11-compat"'auto' storage class specifier is redundant and incompatible with C++11"
-Wc++11-compat"use of right-shift operator ('') in template argument will require parentheses in C++11"
-Wc++98-c++11-compat"'decltype(auto)' type specifier is incompatible with C++ standards before C++1y"
-Wc++98-compat-pedantic"commas at the end of enumerator lists are incompatible with C++98"
-Wc++98-compat-pedantic"extern templates are incompatible with C++98"
-Wc++98-compat-pedantic"extra '' outside of a function is incompatible with C++98"
-Wc++98-compat"'%0' keyword is incompatible with C++98"
-Wc++98-compat"'alignas' is incompatible with C++98"
-Wc++98-compat"'decltype' type specifier is incompatible with C++98"
-Wc++98-compat"'nullptr' is incompatible with C++98"
-Wc++98-compat"alias declarations are incompatible with C++98"
-Wc++98-compat"alignof expressions are incompatible with C++98"
-Wc++98-compat"attributes are incompatible with C++98"
-Wc++98-compat"consecutive right angle brackets are incompatible with C++98 (use ' ')"
-Wc++98-compat"defaulted function definitions are incompatible with C++98"
-Wc++98-compat"deleted function definitions are incompatible with C++98"
-Wc++98-compat"enumeration types with a fixed underlying type are incompatible with C++98"
-Wc++98-compat"generalized initializer lists are incompatible with C++98"
-Wc++98-compat"in-class initialization of non-static data members is incompatible with C++98"
-Wc++98-compat"inline namespaces are incompatible with C++98"
-Wc++98-compat"lambda expressions are incompatible with C++98"
-Wc++98-compat"literal operators are incompatible with C++98"
-Wc++98-compat"noexcept expressions are incompatible with C++98"
-Wc++98-compat"noexcept specifications are incompatible with C++98"
-Wc++98-compat"range-based for loop is incompatible with C++98"
-Wc++98-compat"reference qualifiers on functions are incompatible with C++98"
-Wc++98-compat"rvalue references are incompatible with C++98"
-Wc++98-compat"scoped enumerations are incompatible with C++98"
-Wc++98-compat"static_assert declarations are incompatible with C++98"
-Wc++98-compat"trailing return types are incompatible with C++98"
-Wdeprecated-declarations"use of C-style parameters in Objective-C method declarations is deprecated"
-Wdeprecated-register"'register' storage class specifier is deprecated"
-Wdeprecated"Use of 'long' with '__vector' is deprecated"
-Wduplicate-decl-specifier"duplicate '%0' declaration specifier"
-Wextra-semi"extra '' after member function definition"
-Wextra-tokens"extra tokens at the end of '#pragma omp %0' are ignored"
-Wgcc-compat"GCC does not allow %0 attribute in this position on a function definition"
-WiagGroup<"dangling-else"add explicit braces to avoid dangling else"
-Wignored-attributes"attribute %0 ignored, because it is not attached to a declaration"
-Wmicrosoft-exists"dependent %select{__if_not_exists
-Wmissing-selector-name"%0 used as the name of the previous parameter rather than as part of the selector"
-Wsemicolon-before-method-body, DefaultIgnore"semicolon before method body is ignored"
-Wsource-uses-openmp"unexpected '#pragma omp ...' in program"
-Wstatic-inline-explicit-instantiation"ignoring '%select{static

Semantic (libsema)

Warning FlagMessage
-Wabstract-vbase-init, DefaultIgnore"initializer for virtual base class %0 of abstract class %1 will never be used"
-Waddress-of-array-temporary"pointer is initialized by a temporary array, which will be destroyed at the end of the full-expression"
-Warc-maybe-repeated-use-of-weak"weak %select{variable
-Warc-non-pod-memaccess"%select{destination for
-Warc-performSelector-leaks"performSelector may cause a leak because its selector is unknown"
-Warc-repeated-use-of-weak"weak %select{variable
-Warc-retain-cycles"capturing %0 strongly in this block is likely to lead to a retain cycle"
-Warc-unsafe-retained-assign"assigning %select{array literal
-Warc-unsafe-retained-assign"assigning retained object to %select{weak
-Warc-unsafe-retained-assign"assigning retained object to unsafe property object will be released after assignment"
-Warray-bounds-pointer-arithmetic"the pointer decremented by %0 refers before the beginning of the array"
-Warray-bounds-pointer-arithmetic"the pointer incremented by %0 refers past the end of the array (that contains %1 element%s2)"
-Warray-bounds"'static' has no effect on zero-length arrays"
-Warray-bounds"array argument is too small contains %0 elements, callee requires at least %1"
-Warray-bounds"array index %0 is before the beginning of the array"
-Warray-bounds"array index %0 is past the end of the array (which contains %1 element%s2)"
-Wassign-enum, DefaultIgnore"integer constant not in range of enumerated type %0"
-Watomic-property-with-user-defined-accessor"writable atomic property %0 cannot pair a synthesized %select{getter
-Wattributes"unknown attribute %0 ignored"
-Wauto-var-id"'auto' deduced as 'id' in declaration of %0"
-Wavailability"availability does not match previous declaration"
-Wavailability"feature cannot be %select{introduced
-Wavailability"overriding method %select{introduced after
-Wavailability"overriding method cannot be unavailable on %0 when its overridden method is available"
-Wavailability"unknown platform %0 in availability macro"
-Wbad-function-cast"cast from function call of type %0 to non-matching type %1"
-Wbitfield-constant-conversion"implicit truncation from %2 to bitfield changes value from %0 to %1"
-Wbool-conversion"initialization of pointer of type %0 to null from a constant boolean " "expression"
-Wbridge-cast"%0 bridges to %1, not %2"
-Wbridge-cast"%0 cannot bridge to %1"
-Wbuiltin-requires-header"declaration of built-in function '%0' requires inclusion of the header setjmp.h"
-Wbuiltin-requires-header"declaration of built-in function '%0' requires inclusion of the header stdio.h"
-Wbuiltin-requires-header"declaration of built-in function '%0' requires inclusion of the header ucontext.h"
-Wc++11-compat"explicit instantiation cannot be 'inline'"
-Wc++11-compat"explicit instantiation of %0 must occur at global scope"
-Wc++11-compat"explicit instantiation of %0 not in a namespace enclosing %1"
-Wc++11-compat"explicit instantiation of %q0 must occur in namespace %1"
-Wc++11-narrowing"constant expression evaluates to %0 which cannot be narrowed to type %1 in C++11"
-Wc++11-narrowing"non-constant-expression cannot be narrowed from type %0 to %1 in initializer list in C++11"
-Wc++11-narrowing"type %0 cannot be narrowed to %1 in initializer list in C++11"
-Wc++98-c++11-compat"constexpr function with no return statements is incompatible with C++ standards before C++1y"
-Wc++98-c++11-compat"multiple return statements in constexpr function is incompatible with C++ standards before C++1y"
-Wc++98-c++11-compat"type definition in a constexpr %select{function
-Wc++98-c++11-compat"use of this statement in a constexpr %select{function
-Wc++98-c++11-compat"variable declaration in a constexpr %select{function
-Wc++98-c++11-compat"variable templates are incompatible with C++ standards before C++1y"
-Wc++98-c++11-compatinit-captures.def warn_cxx11_compat_init_capture : Warning "initialized lambda captures are incompatible with C++ standards " "before C++1y"
-Wc++98-compat-pedantic"cast between pointer-to-function and pointer-to-object is incompatible with C++98"
-Wc++98-compat-pedantic"implicit conversion from array size expression of type %0 to %select{integral
-Wc++98-compat"%select{anonymous struct
-Wc++98-compat"%select{class template
-Wc++98-compat"'%0' type specifier is incompatible with C++98"
-Wc++98-compat"'auto' type specifier is incompatible with C++98"
-Wc++98-compat"'constexpr' specifier is incompatible with C++98"
-Wc++98-compat"befriending %1 without '%select{struct
-Wc++98-compat"befriending enumeration type %0 is incompatible with C++98"
-Wc++98-compat"constructor call from initializer list is incompatible with C++98"
-Wc++98-compat"default template arguments for a function template are incompatible with C++98"
-Wc++98-compat"delegating constructors are incompatible with C++98"
-Wc++98-compat"enumeration type in nested name specifier is incompatible with C++98"
-Wc++98-compat"explicit conversion functions are incompatible with C++98"
-Wc++98-compat"friend declaration naming a member of the declaring class is incompatible with C++98"
-Wc++98-compat"friend function %0 would be implicitly redefined in C++98"
-Wc++98-compat"goto would jump into protected scope in C++98"
-Wc++98-compat"indirect goto might cross protected scopes in C++98"
-Wc++98-compat"inheriting constructors are incompatible with C++98"
-Wc++98-compat"initialization of initializer_list object is incompatible with C++98"
-Wc++98-compat"non-class friend type %0 is incompatible with C++98"
-Wc++98-compat"non-type template argument referring to %select{function
-Wc++98-compat"passing object of trivial but non-POD type %0 through variadic %select{function
-Wc++98-compat"redundant parentheses surrounding address non-type template argument are incompatible with C++98"
-Wc++98-compat"reference initialized from initializer list is incompatible with C++98"
-Wc++98-compat"scalar initialized from empty initializer list is incompatible with C++98"
-Wc++98-compat"static data member %0 in union is incompatible with C++98"
-Wc++98-compat"substitution failure due to access control is incompatible with C++98"
-Wc++98-compat"switch case would be in a protected scope in C++98"
-Wc++98-compat"use of 'template' keyword outside of a template is incompatible with C++98"
-Wc++98-compat"use of 'typename' outside of a template is incompatible with C++98"
-Wc++98-compat"use of non-static data member %0 in an unevaluated context is incompatible with C++98"
-Wc++98-compat"use of null pointer as non-type template argument is incompatible with C++98"
-Wcast-align"cast from %0 to %1 increases required alignment from %2 to %3"
-Wcast-of-sel-type"cast of type %0 to %1 is deprecated use sel_getName instead"
-WCFString-literal"input conversion stopped due to an input byte that does not belong to the input codeset UTF-8"
-Wchar-subscripts"array subscript is of type 'char'"
-Wconditional-uninitialized"variable %0 may be uninitialized when %select{used here
-Wconstant-logical-operand"use of logical '%0' with constant operand"
-Wconstexpr-not-const"'constexpr' non-static member function will not be implicitly 'const' in C++1y add 'const' to avoid a change in behavior"
-Wconsumed"argument not in expected state expected '%0', observed '%1'"
-Wconsumed"consumed analysis attribute is attached to member of class '%0' which isn't marked as consumable"
-Wconsumed"invalid invocation of method '%0' on a temporary object while it is in the '%1' state"
-Wconsumed"invalid invocation of method '%0' on object '%1' while it is in the '%2' state"
-Wconsumed"parameter '%0' not in expected state when the function returns: expected '%1', observed '%2'"
-Wconsumed"return state set for an unconsumable type '%0'"
-Wconsumed"return value not in expected state expected '%0', observed '%1'"
-Wconsumed"state of variable '%0' must match at the entry and exit of loop"
-Wconversion"implicit conversion discards imaginary component: %0 to %1"
-Wconversion"implicit conversion loses floating-point precision: %0 to %1"
-Wconversion"implicit conversion loses integer precision: %0 to %1"
-Wconversion"implicit conversion turns floating-point number into integer: %0 to %1"
-Wconversion"implicit conversion turns vector to scalar: %0 to %1"
-Wconversion"non-type template argument value '%0' truncated to '%1' for template parameter of type %2"
-Wconversion"non-type template argument with value '%0' converted to '%1' for unsigned template parameter of type %2"
-Wcovered-switch-default"default label in switch which covers all enumeration values"
-Wcustom-atomic-properties"atomic by default property %0 has a user defined %select{getter
-Wdangling-field"binding reference %select{
-Wdangling-field"binding reference member %0 to stack allocated parameter %1"
-Wdangling-field"initializing pointer member %0 with the stack address of parameter %1"
-Wdangling-initializer-list"array backing the initializer list will be destroyed at the end of %select{the full-expression
-Wdelete-incomplete"deleting pointer to incomplete type %0 may cause undefined behavior"
-Wdelete-non-virtual-dtor"delete called on %0 that has virtual functions but non-virtual destructor"
-Wdelete-non-virtual-dtor"delete called on %0 that is abstract but has non-virtual destructor"
-Wdeprecated-increment-bool"incrementing expression of type bool is deprecated"
-Wdeprecated-objc-isa-usage"assignment to Objective-C's isa is deprecated in favor of object_setClass()"
-Wdeprecated-objc-isa-usage"direct access to Objective-C's isa is deprecated in favor of object_getClass()"
-Wdeprecated-objc-pointer-introspection-performSelectorwarn_objc_pointer_masking.Text
-Wdeprecated-objc-pointer-introspection"bitmasking for introspection of Objective-C object pointers is strongly discouraged"
-Wdeprecated-writable-strings"dummy warning to enable -fconst-strings"
-Wdeprecated"access declarations are deprecated use using declarations instead"
-Wdeprecated"definition of implicit copy %select{constructor
-Wdeprecated"dynamic exception specifications are deprecated"
-Wdirect-ivar-access, DefaultIgnore"instance variable %0 is being directly accessed"
-Wdistributed-object-modifiers"conflicting distributed object modifiers on parameter type in implementation of %0"
-Wdistributed-object-modifiers"conflicting distributed object modifiers on return type in implementation of %0"
-Wdivision-by-zero"division by zero is undefined"
-Wdivision-by-zero"remainder by zero is undefined"
-Wdocumentation"not a Doxygen trailing comment"
-Wduplicate-enum, DefaultIgnore"element %0 has been implicitly assigned %1 which another element has been assigned"
-Wduplicate-method-match"multiple declarations of method %0 found and ignored"
-Wdynamic-class-memaccess"%select{destination for
-Wempty-body"for loop has empty body"
-Wempty-body"if statement has empty body"
-Wempty-body"range-based for loop has empty body"
-Wempty-body"switch statement has empty body"
-Wempty-body"while loop has empty body"
-Wenum-compare"comparison of two values with different enumeration types%diff{ ($ and $)
-Wenum-conversion"implicit conversion from enumeration type %0 to different enumeration type %1"
-Wexit-time-destructors"declaration requires an exit-time destructor"
-Wexplicit-ownership-type, DefaultIgnore"method parameter of type %0 with no explicit ownership"
-Wextern-c-compat"%select{
-Wextern-initializer"'extern' variable has an initializer"
-Wfloat-equal, DefaultIgnore"comparing floating point with == or != is unsafe"
-Wformat-extra-args"data argument not used by format string"
-Wformat-invalid-specifier"invalid conversion specifier '%0'"
-Wformat-nonliteral"format string is not a string literal"
-Wformat-security"format string is not a string literal (potentially insecure)"
-Wformat-zero-length"format string is empty"
-Wformat"%select{field width
-Wformat"'%select{*
-Wformat"cannot mix positional and non-positional arguments in format string"
-Wformat"data argument position '%0' exceeds the number of data arguments (%1)"
-Wformat"field %select{width
-Wformat"flag '%0' is ignored when flag '%1' is present"
-Wformat"flag '%0' results in undefined behavior with '%1' conversion specifier"
-Wformat"format specifies type %0 but the argument has type %1"
-Wformat"format string contains '\0' within the string body"
-Wformat"format string missing"
-Wformat"format string should not be a wide string"
-Wformat"incomplete format specifier"
-Wformat"invalid position specified for %select{field width
-Wformat"length modifier '%0' results in undefined behavior or no effect with '%1' conversion specifier"
-Wformat"more '%%' conversions than data arguments"
-Wformat"no closing ']' for '%%[' in scanf format string"
-Wformat"position arguments in format strings start counting at 1 (not 0)"
-Wformat"values of type '%0' should not be used as format arguments add an explicit cast to %1 instead"
-Wformat"zero field width in scanf format string is unused"
-Wgcc-compat"GCC does not allow the 'cleanup' attribute argument to be anything other than a simple identifier"
-Wglobal-constructors"declaration requires a global constructor"
-Wglobal-constructors"declaration requires a global destructor"
-Wheader-hygiene"using namespace directive in global context in header"
-WiagGroup<"bitwise-op-parentheses"'&' within '
-WiagGroup<"c++-compat"%select{
-WiagGroup<"logical-not-parentheses"logical not is only applied to the left hand side of this comparison"
-WiagGroup<"logical-op-parentheses"'&&' within '
-WiagGroup<"missing-declarations"'%0' ignored on this declaration"
-WiagGroup<"overloaded-shift-op-parentheses"overloaded operator %select{
-WiagGroup<"shift-op-parentheses"operator '%0' has lower precedence than '%1' '%1' will be evaluated first"
-Widiomatic-parentheses, DefaultIgnore"using the result of an assignment as a condition without parentheses"
-Wignored-attributes"#pramga ms_struct can not be used with dynamic classes or structures"
-Wignored-attributes"%0 attribute argument not supported: %1"
-Wignored-attributes"%0 attribute can only be applied to instance variables or properties"
-Wignored-attributes"%0 attribute ignored for field of type %1"
-Wignored-attributes"%0 attribute ignored when parsing type"
-Wignored-attributes"%0 attribute ignored"
-Wignored-attributes"%0 attribute only applies to %select{functions
-Wignored-attributes"%0 attribute only applies to %select{functions
-Wignored-attributes"%0 attribute only applies to %select{Objective-C object
-Wignored-attributes"%0 calling convention ignored on variadic function"
-Wignored-attributes"%0 only applies to variables with static storage duration and functions"
-Wignored-attributes"%select{alignment
-Wignored-attributes"'%0' attribute cannot be specified on a definition"
-Wignored-attributes"'%0' only applies to %select{function
-Wignored-attributes"'gnu_inline' attribute requires function to be marked 'inline', attribute ignored"
-Wignored-attributes"'malloc' attribute only applies to functions returning a pointer type"
-Wignored-attributes"'nonnull' attribute applied to function with no pointer arguments"
-Wignored-attributes"'sentinel' attribute only supported for variadic %select{functions
-Wignored-attributes"'sentinel' attribute requires named arguments"
-Wignored-attributes"attribute %0 after definition is ignored"
-Wignored-attributes"attribute %0 cannot be applied to %select{functions
-Wignored-attributes"attribute %0 ignored, because it cannot be applied to a type"
-Wignored-attributes"attribute %0 is already applied with different parameters"
-Wignored-attributes"attribute %0 is already applied"
-Wignored-attributes"attribute %0 is ignored, place it after \"%select{class
-Wignored-attributes"attribute declaration must precede definition"
-Wignored-attributes"calling convention %0 ignored for this target"
-Wignored-attributes"first field of a transparent union cannot have %select{floating point
-Wignored-attributes"ibaction attribute can only be applied to Objective-C instance methods"
-Wignored-attributes"Objective-C GC does not allow weak variables on the stack"
-Wignored-attributes"transparent union definition must contain at least one field transparent_union attribute ignored"
-Wignored-attributes"transparent_union attribute can only be applied to a union definition attribute ignored"
-Wignored-attributes"unknown visibility %0"
-Wignored-attributes"__declspec attribute %0 is not supported"
-Wignored-attributes"__weak attribute cannot be specified on a field declaration"
-Wignored-attributes"__weak attribute cannot be specified on an automatic variable when ARC is not enabled"
-Wignored-qualifiers"'%0' type qualifier%s1 on return type %plural{1:has
-Wignored-qualifiers"ARC %select{unused
-Wimplicit-atomic-properties"property is assumed atomic by default"
-Wimplicit-atomic-properties"property is assumed atomic when auto-synthesizing the property"
-Wimplicit-fallthrough"fallthrough annotation does not directly precede switch label"
-Wimplicit-fallthrough"fallthrough annotation in unreachable code"
-Wimplicit-fallthrough"unannotated fall-through between switch labels"
-Wimplicit-function-declaration"implicit declaration of function %0"
-Wimplicit-function-declaration"use of unknown builtin %0"
-Wimplicit-retain-self, DefaultIgnore"block implicitly retains 'self' explicitly mention 'self' to indicate this is intended behavior"
-Wincompatible-library-redeclaration"incompatible redeclaration of library function %0"
-Wincomplete-implementation"method definition for %0 not found"
-Winherited-variadic-ctor"inheriting constructor does not inherit ellipsis"
-Winitializer-overrides"initializer overrides prior initialization of this subobject"
-Winitializer-overrides"subobject initialization overrides initialization of other fields within its enclosing subobject"
-Wint-to-pointer-cast"cast to %1 from smaller integer type %0"
-Wint-to-void-pointer-cast"cast to %1 from smaller integer type %0"
-Winvalid-iboutlet"%select{instance variable
-Winvalid-iboutlet"IBOutletCollection properties should be copy/strong and not assign"
-Winvalid-noreturn"function %0 declared 'noreturn' should not return"
-Winvalid-noreturn"function declared 'noreturn' should not return"
-Wlarge-by-value-copy"%0 is a large (%1 bytes) pass-by-value argument pass it by reference instead ?"
-Wlarge-by-value-copy"return value of %0 is a large (%1 bytes) pass-by-value object pass it by reference instead ?"
-Wliteral-conversion"implicit conversion from %0 to %1 changes value from %2 to %3"
-Wliteral-range"magnitude of floating-point constant too large for type %0 maximum is %1"
-Wliteral-range"magnitude of floating-point constant too small for type %0 minimum is %1"
-Wloop-analysis"variable %0 is %select{decremented
-Wloop-analysis"variable%select{s
-Wmethod-signatures"conflicting parameter types in implementation of %0: %1 vs %2"
-Wmethod-signatures"conflicting return type in implementation of %0: %1 vs %2"
-Wmicrosoft"extra qualification on member %0"
-Wmismatched-method-attributes"attributes on method implementation and its declaration must match"
-Wmismatched-parameter-types"conflicting parameter types in implementation of %0%diff{: $ vs $
-Wmismatched-return-types"conflicting return type in implementation of %0%diff{: $ vs $
-Wmissing-braces"suggest braces around initialization of subobject"
-Wmissing-field-initializers"missing field '%0' initializer"
-Wmissing-method-return-type"method has no return type specified defaults to 'id'"
-Wmissing-noreturn"%select{function
-Wmissing-noreturn"block could be declared with attribute 'noreturn'"
-Wmissing-prototypes, DefaultIgnore"no previous prototype for function %0"
-Wmissing-variable-declarations, DefaultIgnore"no previous extern declaration for non-static variable %0"
-Wmultiple-move-vbase"defaulted move assignment operator of %0 will move assign virtual base class %1 multiple times"
-Wnon-literal-null-conversion"expression which evaluates to zero treated as a null pointer constant of " "type %0"
-Wnon-pod-varargs"cannot pass %select{non-POD
-Wnon-pod-varargs"cannot pass object of %select{non-POD
-Wnon-pod-varargs"second argument to 'va_arg' is of ARC ownership-qualified type %0"
-Wnon-pod-varargs"second argument to 'va_arg' is of non-POD type %0"
-Wnon-virtual-dtor"%0 has virtual functions but non-virtual destructor"
-Wnonnull"null passed to a callee which requires a non-null argument"
-WNSObject-attribute"__attribute ((NSObject)) may be put on a typedef only, attribute is ignored"
-Wnull-arithmetic"comparison between NULL and non-pointer %select{(%1 and NULL)
-Wnull-arithmetic"use of NULL in arithmetic operation"
-Wnull-dereference"indirection of non-volatile null pointer will be deleted, not trap"
-Wobjc-autosynthesis-property-ivar-name-match"autosynthesized property %0 will use %select{
-Wobjc-forward-class-redefinition"redefinition of forward class %0 of a typedef name of an object type is ignored"
-Wobjc-interface-ivars, DefaultIgnore"declaration of instance variables in the interface is deprecated"
-Wobjc-literal-compare"direct comparison of %select{an array literal
-Wobjc-literal-missing-atsign"string literal must be prefixed by '@' "
-Wobjc-method-access"class method %objcclass0 not found (return type defaults to 'id') did you mean %objcclass2?"
-Wobjc-method-access"class method %objcclass0 not found (return type defaults to 'id')"
-Wobjc-method-access"instance method %0 found instead of class method %1"
-Wobjc-method-access"instance method %0 is being used on 'Class' which is not in the root class"
-Wobjc-method-access"instance method %objcinstance0 not found (return type defaults to 'id') did you mean %objcinstance2?"
-Wobjc-method-access"instance method %objcinstance0 not found (return type defaults to 'id')"
-Wobjc-missing-property-synthesis, DefaultIgnore"auto property synthesis is synthesizing property not explicitly synthesized"
-Wobjc-missing-super-calls"method possibly missing a [super %0] call"
-Wobjc-noncopy-retain-block-property"retain'ed block property does not copy the block " "- use copy attribute instead"
-Wobjc-nonunified-exceptions"can not catch an exception thrown with @throw in C++ in the non-unified exception model"
-Wobjc-property-implementation"property %0 requires method %1 to be defined - use @dynamic or provide a method implementation in this category"
-Wobjc-property-implementation"property %0 requires method %1 to be defined - use @synthesize, @dynamic or provide a method implementation in this class implementation"
-Wobjc-property-implicit-mismatch"primary property declaration is implicitly strong while redeclaration in class extension is weak"
-Wobjc-property-matches-cocoa-ownership-rule"property's synthesized getter follows Cocoa naming convention for returning 'owned' objects"
-Wobjc-property-no-attribute"default property attribute 'assign' not appropriate for non-GC object"
-Wobjc-property-no-attribute"no 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed"
-Wobjc-property-synthesis"auto property synthesis will not synthesize property '%0' because it cannot share an ivar with another synthesized property"
-Wobjc-property-synthesis"auto property synthesis will not synthesize property '%0' because it is 'readwrite' but it will be synthesized 'readonly' via another property"
-Wobjc-protocol-method-implementation"category is implementing a method which will also be implemented by its primary class"
-Wobjc-protocol-property-synthesis"auto property synthesis will not synthesize property declared in a protocol"
-Wobjc-redundant-literal-use"using %0 with a literal is redundant"
-Wobjc-root-class"class %0 defined without specifying a base class"
-Wobjc-string-compare"direct comparison of a string literal has undefined behavior"
-Wobjc-string-concatenation"concatenated NSString literal for an NSArray expression - possibly missing a comma"
-Wover-aligned"type %0 requires %1 bytes of alignment and the default allocator only guarantees %2 bytes"
-Woverloaded-virtual"%q0 hides overloaded virtual %select{function
-Woverriding-method-mismatch"conflicting distributed object modifiers on parameter type in declaration of %0"
-Woverriding-method-mismatch"conflicting distributed object modifiers on return type in declaration of %0"
-Woverriding-method-mismatch"conflicting parameter types in declaration of %0%diff{: $ vs $
-Woverriding-method-mismatch"conflicting parameter types in declaration of %0: %1 vs %2"
-Woverriding-method-mismatch"conflicting return type in declaration of %0%diff{: $ vs $
-Woverriding-method-mismatch"conflicting return type in declaration of %0: %1 vs %2"
-Woverriding-method-mismatch"conflicting variadic declaration of method and its implementation"
-Wpacked"packed attribute is unnecessary for %0"
-Wpadded"padding %select{struct
-Wpadded"padding %select{struct
-Wpadded"padding size of %0 with %1 %select{byte
-Wparentheses-equality"equality comparison with extraneous parentheses"
-Wparentheses"%0 has lower precedence than %1 %1 will be evaluated first"
-Wparentheses"operator '?:' has lower precedence than '%0' '%0' will be evaluated first"
-Wparentheses"using the result of an assignment as a condition without parentheses"
-Wpointer-arith"subtraction of pointers to type %0 of zero size has undefined behavior"
-Wpredefined-identifier-outside-function"predefined identifier is only valid inside function"
-Wprivate-extern"use of private_extern on a declaration may not produce external symbol private to the linkage unit and is deprecated"
-Wprotocol-property-synthesis-ambiguity"property of type %0 was selected for synthesis"
-Wprotocol"method %0 in protocol not implemented"
-Wreadonly-iboutlet-property"readonly IBOutlet property '%0' when auto-synthesized may not work correctly with 'nib' loader"
-Wreadonly-setter-attrs"property attributes '%0' and '%1' are mutually exclusive"
-Wreceiver-expr"receiver type %0 is not 'id' or interface pointer, consider casting it to 'id'"
-Wreceiver-forward-class"receiver type %0 for instance message is a forward declaration"
-Wreceiver-is-weak, DefaultIgnore"weak %select{receiver
-Wreinterpret-base-class"'reinterpret_cast' %select{from
-Wreorder"%select{field
-Wrequires-super-attribute"%0 attribute cannot be applied to %select{methods in protocols
-Wreturn-stack-address"address of stack memory associated with local variable %0 returned"
-Wreturn-stack-address"reference to stack memory associated with local variable %0 returned"
-Wreturn-stack-address"returning address of label, which is local"
-Wreturn-stack-address"returning address of local temporary object"
-Wreturn-stack-address"returning reference to local temporary object"
-Wreturn-type-c-linkage"%0 has C-linkage specified, but returns incomplete type %1 which could be incompatible with C"
-Wreturn-type-c-linkage"%0 has C-linkage specified, but returns user-defined type %1 which is incompatible with C"
-Wreturn-type"control may reach end of non-void function"
-Wreturn-type"control reaches end of non-void function"
-Wreturn-type"non-void %select{function
-Wsection"section does not match previous declaration"
-Wselector-type-mismatch"multiple selectors named %0 found"
-Wselector"creating selector for nonexistent method %0"
-Wself-assign-field"assigning %select{field
-Wself-assign"explicitly assigning a variable of type %0 to itself"
-Wsentinel"missing sentinel in %select{function call
-Wsentinel"not enough variable arguments in %0 declaration to fit a sentinel"
-Wshadow-ivar"local declaration of %0 hides instance variable"
-Wshadow"declaration shadows a %select{" "local variable
-Wshift-count-negative"shift count is negative"
-Wshift-count-overflow"shift count = width of type"
-Wshift-overflow"signed shift result (%0) requires %1 bits to represent, but %2 only has %3 bits"
-Wshift-sign-overflow, DefaultIgnore"signed shift result (%0) sets the sign bit of the shift expression's type (%1) and becomes negative"
-Wshorten-64-to-32"implicit conversion loses integer precision: %0 to %1"
-Wsign-compare"comparison of integers of different signs: %0 and %1"
-Wsign-conversion"implicit conversion changes signedness: %0 to %1"
-Wsign-conversion"operand of ? changes signedness: %0 to %1"
-Wsizeof-array-argument"sizeof on array function parameter will return size of %0 instead of %1"
-Wsizeof-array-decay"sizeof on pointer operation will return size of %0 instead of %1"
-Wsizeof-pointer-memaccess"'%0' call operates on objects of type %1 while the size is based on a " "different type %2"
-Wsizeof-pointer-memaccess"argument to 'sizeof' in %0 call is the same pointer type %1 as the %select{destination
-Wsometimes-uninitialized"variable %0 is %select{used
-Wstatic-local-in-inline"non-constant static local variable in inline function may be different in different files"
-Wstatic-self-init"static variable %0 is suspiciously used within its own initialization"
-Wstrict-selector-match"multiple methods named %0 found"
-Wstring-compare"result of comparison against %select{a string literal
-Wstring-conversion"implicit conversion turns string literal into bool: %0 to %1"
-Wstring-plus-char"adding %0 to a string pointer does not append to the string"
-Wstring-plus-int"adding %0 to a string does not append to the string"
-Wstrlcpy-strlcat-size"size argument in %0 call appears to be size of the source expected the size of the destination"
-Wstrncat-size"size argument in 'strncat' call appears " "to be size of the source"
-Wstrncat-size"the value of the size argument in 'strncat' is too large, might lead to a " "buffer overflow"
-Wstrncat-size"the value of the size argument to 'strncat' is wrong"
-Wsuper-class-method-mismatch"method parameter type %diff{$ does not match super class method parameter type $
-Wswitch-enum"%0 enumeration values not explicitly handled in switch: %1, %2, %3..."
-Wswitch-enum"enumeration value %0 not explicitly handled in switch"
-Wswitch-enum"enumeration values %0 and %1 not explicitly handled in switch"
-Wswitch-enum"enumeration values %0, %1, and %2 not explicitly handled in switch"
-Wswitch"%0 enumeration values not handled in switch: %1, %2, %3..."
-Wswitch"case value not in enumerated type %0"
-Wswitch"enumeration value %0 not handled in switch"
-Wswitch"enumeration values %0 and %1 not handled in switch"
-Wswitch"enumeration values %0, %1, and %2 not handled in switch"
-Wswitch"overflow converting case value to switch condition type (%0 to %1)"
-Wtautological-compare"%select{self-
-Wtautological-compare"comparison of %0 unsigned%select{
-Wtautological-compare"comparison of unsigned%select{
-Wtautological-constant-out-of-range-compare"comparison of constant %0 with expression of type %1 is always %select{false
-Wthread-safety-analysis"%select{reading
-Wthread-safety-analysis"%select{reading
-Wthread-safety-analysis"%select{reading
-Wthread-safety-analysis"%select{reading
-Wthread-safety-analysis"calling function '%0' requires %select{shared
-Wthread-safety-analysis"cannot call function '%0' while mutex '%1' is locked"
-Wthread-safety-analysis"cannot resolve lock expression"
-Wthread-safety-analysis"expecting mutex '%0' to be locked at start of each loop"
-Wthread-safety-analysis"expecting mutex '%0' to be locked at the end of function"
-Wthread-safety-analysis"locking '%0' that is already locked"
-Wthread-safety-analysis"mutex '%0' is locked exclusively and shared in the same scope"
-Wthread-safety-analysis"mutex '%0' is not locked on every path through here"
-Wthread-safety-analysis"mutex '%0' is still locked at the end of function"
-Wthread-safety-analysis"unlocking '%0' that was not locked"
-Wthread-safety-attributes"%0 attribute can only be applied in a context annotated with 'lockable' attribute"
-Wthread-safety-attributes"%0 attribute only applies to %select{fields and global variables
-Wthread-safety-attributes"%0 attribute requires arguments that are class type or point to class type type here is '%1'"
-Wthread-safety-attributes"%0 attribute requires arguments whose type is annotated with 'lockable' attribute type here is '%1'"
-Wthread-safety-attributes"'%0' only applies to pointer types type here is %1"
-Wthread-safety-attributes"ignoring %0 attribute because its argument is invalid"
-Wthread-safety-beta"Thread safety beta warning."
-Wthread-safety-precise"%select{reading
-Wthread-safety-precise"%select{reading
-Wthread-safety-precise"calling function '%0' requires %select{shared
-Wtype-safety"argument type %0 doesn't match specified '%1' type tag %select{that requires %3
-Wtype-safety"specified %0 type tag requires a null pointer"
-Wtype-safety"this type tag was not designed to be used with this function"
-Wundeclared-selector"undeclared selector %0 did you mean %1?"
-Wundeclared-selector"undeclared selector %0"
-Wundefined-inline"inline function %q0 is not defined"
-Wundefined-internal"%select{function
-Wundefined-reinterpret-cast"dereference of type %1 that was reinterpret_cast from type %0 has undefined behavior"
-Wundefined-reinterpret-cast"reinterpret_cast from %0 to %1 has undefined behavior"
-Wuninitialized"block pointer variable %0 is uninitialized when captured by block"
-Wuninitialized"field %0 is uninitialized when used here"
-Wuninitialized"reference %0 is not yet bound to a value when used here"
-Wuninitialized"reference %0 is not yet bound to a value when used within its own initialization"
-Wuninitialized"variable %0 is uninitialized when %select{used here
-Wuninitialized"variable %0 is uninitialized when used within its own initialization"
-Wunneeded-internal-declaration"%select{function
-Wunneeded-internal-declaration"'static' function %0 declared in header file should be declared 'static inline'"
-Wunneeded-member-function"member function %0 is not needed and will not be emitted"
-Wunreachable-code, DefaultIgnore"will never be executed"
-Wunsequenced"multiple unsequenced modifications to %0"
-Wunsequenced"unsequenced modification and access to %0"
-Wunsupported-friend"dependent nested name specifier '%0' for friend class declaration is not supported turning off access control for %1"
-Wunsupported-friend"dependent nested name specifier '%0' for friend template declaration is not supported ignoring this friend declaration"
-Wunsupported-visibility"target does not support 'protected' visibility using 'default'"
-Wunused-comparison"%select{equality
-Wunused-const-variable"unused variable %0"
-Wunused-exception-parameter"unused exception parameter %0"
-Wunused-function"unused function %0"
-Wunused-label"unused label %0"
-Wunused-member-function"unused member function %0"
-Wunused-parameter"unused parameter %0"
-Wunused-private-field"private field %0 is not used"
-Wunused-property-ivar"ivar %0 which backs the property is not referenced in this property's accessor"
-Wunused-result"ignoring return value of function declared with warn_unused_result attribute"
-Wunused-value"expression result unused should this cast be to 'void'?"
-Wunused-value"expression result unused"
-Wunused-value"ignoring return value of function declared with %0 attribute"
-Wunused-variable"unused variable %0"
-Wunused-volatile-lvalue"expression result unused assign into a variable to force a volatile load"
-Wused-but-marked-unused"%0 was marked unused but was used"
-Wuser-defined-literals"user-defined literal suffixes not starting with '_' are reserved%select{ no literal will invoke this operator
-Wvarargs"'va_start' has undefined behavior with reference types"
-Wvarargs"second argument to 'va_arg' is of promotable type %0 this va_arg has undefined behavior because arguments will be promoted to %1"
-Wvarargs"second parameter of 'va_start' not last named argument"
-Wvector-conversion"incompatible vector types "%select{\%diff{assigning to $ from $
-Wvexing-parse"empty parentheses interpreted as a function declaration"
-Wvexing-parse"parentheses were disambiguated as a function declaration"
-Wvisibility"declaration of %0 will not be visible outside of this function"
-Wvisibility"redefinition of %0 will not be visible outside of this function"
-Wvla"variable length array used"
-Wweak-template-vtables, DefaultIgnore"explicit template instantiation %0 will emit a vtable in every translation unit"
-Wweak-vtables, DefaultIgnore"%0 has no out-of-line virtual method definitions its vtable will be emitted in every translation unit"

Corrections? Additions? Open a Pull Request to submit your change. Any help would be greatly appreciated.

NSCalendarUnitYear

$
0
0

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:

varcakePath=UIBezierPath()cakePath.moveToPoint(CGPointMake(31.5,32.5))cakePath.addCurveToPoint(CGPointMake(6.5,66.1),controlPoint1:CGPointMake(31.5,32.5),controlPoint2:CGPointMake(6.9,46.3))cakePath.addCurveToPoint(CGPointMake(6.5,66.5),controlPoint1:CGPointMake(6.5,66.2),controlPoint2:CGPointMake(6.5,66.3))cakePath.addLineToPoint(CGPointMake(6.5,95))...

Wait, hold up. What is this, Objective-C? Manipulating UIBezierPaths isn't exactly ground-breaking stuff, but with a few dozen more lines to go, this is miserable.

How about we put some syntactic icing on this cake with some custom operators?

operatorinfix--->{associativityleft}func--->(left:UIBezierPath,right:(Float,Float))->UIBezierPath{let(x,y)=rightleft.moveToPoint(CGPointMake(x,y))returnleft}operatorinfix+-{associativityleft}func+-(left:UIBezierPath,right:(Float,Float))->UIBezierPath{let(x,y)=rightleft.addLineToPoint(CGPointMake(x,y))returnleft}operatorinfix+~{associativityleft}func+~(left:UIBezierPath,right:((Float,Float),(Float,Float),(Float,Float)))->UIBezierPath{let((x1,y1),(x2,y2),(x3,y3))=rightleft.addCurveToPoint(CGPointMake(x1,y1),controlPoint1:CGPointMake(x2,y2),controlPoint2:CGPointMake(x3,y3))returnleft}

Get it? ---> replaces moveToPoint, while +- replaces addLineToPoint, and +~ replaces addCurveToPoint. This declaration also does away with all of the redundant calls to CGPointMake, opting instead for simple coordinate tuples.

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🍰=UIBezierPath()🍰--->((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))🍰.closePath()🍰--->((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))🍰.closePath()var📍=UIBezierPath()📍--->((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))📍.closePath()var🔥=UIBezierPath()🔥.miterLimit=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))🔥.closePath()UIColor.blackColor().setFill()🍰.fill()🔥.fill()UIColor.whiteColor().setFill()UIColor.blackColor().setStroke()📍.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.

XCTestCase / XCTestExpectation / measureBlock()

$
0
0

Although iOS 8 and Swift has garnered the lion's share of attention of the WWDC 2014 announcements, the additions and improvements to testing in Xcode 6 may end up making some of the most profound impact in the long-term.

This week, we'll take a look at XCTest, the testing framework built into Xcode, as well as the exciting new additions in Xcode 6: XCTestExpectation and performance tests.


Most Xcode project templates now support testing out-of-the-box. For example, when a new iOS app is created in Xcode with ⇧⌘N, the resulting project file will be configured with two top-level groups (in addition to the "Products" group): "AppName" & "AppNameTests". The project's auto-generated scheme enables the shortcut ⌘R to build and run the executable target, and ⌘U to build and run the test target.

Within the test target is a single file, named "AppNameTests", which contains an example XCTestCase class, complete with boilerplate setUp& tearDown methods, as well as an example functional and performance test cases.

XCTestCase

Xcode unit tests are contained within an XCTestCase subclass. By convention, each XCTestCase subclass encapsulates a particular set of concerns, such as a feature, use case, or flow of an application.

Dividing up tests logically across a manageable number of test cases makes a huge difference as codebases grow and evolve.

setUp & tearDown

setUp is called before each test in an XCTestCase is run, and when that test finishes running, tearDown is called:

classTests:XCTestCase{overridefuncsetUp(){super.setUp()// Put setup code here. This method is called before the invocation of each test method in the class.}overridefunctearDown(){// Put teardown code here. This method is called after the invocation of each test method in the class.super.tearDown()}}

These methods are useful for creating an objects common to all of the tests for a test case:

varcalendar:NSCalendar?varlocale:NSLocale?overridefuncsetUp(){super.setUp()self.calendar=NSCalendar(identifier:NSGregorianCalendar)self.locale=NSLocale(localeIdentifier:"en_US")}

Since XCTestCase is not intended to be initialized directly from within a test case definition, shared properties initialized in setUp are declared as optional vars.

Functional Testing

Each method in a test case with a name that begins with "test" is recognized as a test, and will evaluate any assertions within that function to determine whether it passed or failed.

For example, the function testOnePlusOneEqualsTwo will pass if 1 + 1 is equal to 2:

functestOnePlusOneEqualsTwo(){XCTAssertEqual(1+1,2,"one plus one should equal two")}

All of the XCTest Assertions You Really Need To Know

XCTest comes with a number of built-in assertions, but one could narrow them down to just a few essentials:

Fundamental Test

To be entirely reductionist, all of the XCTest assertions come down to a single, base assertion:

XCTAssert(expression,format...)

If the expression evaluates to true, the test passes. Otherwise, the test fails, printing the formatted message.

Although a developer could get away with only using XCTAssert, the following helper assertions provide some useful semantics to help clarify what exactly is being tested. When possible, use the most specific assertion available, falling back to XCTAssert only in cases where it better expresses the intent.

Boolean Tests

For Bool values, or simple boolean expressions, use XCTAssertTrue& XCTAssertFalse:

XCTAssertTrue(expression,format...)XCTAssertFalse(expression,format...)

XCTAssert is equivalent to XCTAssertTrue.

Equality Tests

When testing whether two values are equal, use XCTAssert[Not]Equal:

XCTAssertEqual(expression1,expression2,format...)XCTAssertNotEqual(expression1,expression2,format...)

XCTAssert[Not]EqualObjects are not necessary in Swift, since there is no distinction between scalars and objects.

When specifically testing whether two Double, Float, or other floating-point values are equal, use XCTAssert[Not]EqualWithAccuracy, to account for any issues with floating point accuracy:

XCTAssertEqualWithAccuracy(expression1,expression2,accuracy,format...)XCTAssertNotEqualWithAccuracy(expression1,expression2,accuracy,format...)

In addition to the aforementioned equality assertions, there are XCTAssertGreaterThan[OrEqual]& XCTAssertLessThan[OrEqual], which supplement == with >, >=, <, & <= equivalents for comparable values.

Nil Tests

Use XCTAssert[Not]Nil to assert the existence (or non-existence) of a given value:

XCTAssertNil(expression,format...)XCTAssertNotNil(expression,format...)

Unconditional Failure

Finally, the XCTFail assertion will always fail:

XCTFail(format...)

XCTFail is most commonly used to denote a placeholder for a test that should be made to pass. It is also useful for handling error cases already accounted by other flow control structures, such as the else clause of an if statement testing for success.

Performance Testing

New in Xcode 6 is the ability to benchmark the performance of code:

functestDateFormatterPerformance(){letdateFormatter=NSDateFormatter()dateFormatter.dateStyle=.LongStyledateFormatter.timeStyle=.ShortStyleletdate=NSDate()self.measureBlock(){letstring=dateFormatter.stringFromDate(date)}}
Test Case '-[_Tests testDateFormatterPerformance]' started.
<unknown>:0: Test Case '-[_Tests testDateFormatterPerformance]' measured [Time, seconds] average: 0.000, relative standard deviation: 242.006%, values: [0.000441, 0.000014, 0.000011, 0.000010, 0.000010, 0.000010, 0.000010, 0.000010, 0.000010, 0.000010], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[_Tests testDateFormatterPerformance]' passed (0.274 seconds).

Performance tests help establish a baseline of performance for hot code paths. Sprinkle them into your test cases to ensure that significant algorithms and procedures remain performant as time goes on.

XCTestExpectation

Perhaps the most exciting feature added in Xcode 6 is built-in support for asynchronous testing, with the XCTestExpectation class. Now, tests can wait for a specified length of time for certain conditions to be satisfied, without resorting to complicated GCD incantations.

To make a test asynchronous, first create an expectation with expectationWithDescription:

letexpectation=expectationWithDescription("...")

Then, at the bottom of the method, add the waitForExpectationsWithTimeout method, specifying a timeout, and handler to execute if the conditions of a test are not satisfied within that timeframe:

waitForExpectationsWithTimeout(10,handler:{errorin// ...})

Now, the only remaining step is to fulfill that expecation in the relevant callback of the asynchronous method being tested:

expectation.fulfill()

If the test has more than one expectation, it will not pass unless each expectation executes fulfill() within the timeout specified in waitForExpectationsWithTimeout().

Here's an example of how the response of an asynchronous networking request can be tested with the new XCTestExpectation APIs:

functestAsynchronousURLConnection(){letURL="http://nshipster.com/"letexpectation=expectationWithDescription("GET \(URL)")letsession=NSURLSession.sharedSession()lettask=session.dataTaskWithURL(NSURL(string:URL),completionHandler:{(data,response,error)inexpectation.fulfill()XCTAssertNotNil(data,"data should not be nil")XCTAssertNil(error,"error should be nil")ifletHTTPResponse=responseasNSHTTPURLResponse!{XCTAssertEqual(HTTPResponse.URL.absoluteString,URL,"HTTP response URL should be equal to original URL")XCTAssertEqual(HTTPResponse.statusCode,200,"HTTP response status code should be 200")XCTAssertEqual(HTTPResponse.MIMETypeasString,"text/html","HTTP response content type should be text/html")}else{XCTFail("Response was not NSHTTPURLResponse")}})task.resume()waitForExpectationsWithTimeout(task.originalRequest.timeoutInterval,handler:{errorintask.cancel()})}

Mocking in Swift

With first-class support for asynchronous testing, Xcode 6 seems to have fulfilled all of the needs of a modern test-driven developer. Well, perhaps save for one: mocking.

Mocking is a useful technique for isolating and controlling behavior in systems that, for reasons of complexity, non-determinism, or performance constraints, do not usually lend themselves to testing. Examples include simulating network requests, intensive database queries, or inducing states that might emerge under a particular race condition.

There are a couple of open source libraries for creating mock objects and stubbing method calls, but these libraries largely rely on Objective-C runtime manipulation, something that is not currently possible with Swift.

However, this may not actually be necessary in Swift, due to its less-constrained syntax.

In Swift, classes can be declared within the definition of a function, allowing for mock objects to be extremely self-contained. Just declare a mock inner-class, override and necessary methods:

functestFetchRequestWithMockedManagedObjectContext(){classMockNSManagedObjectContext:NSManagedObjectContext{overridefuncexecuteFetchRequest(request:NSFetchRequest!,error:AutoreleasingUnsafePointer<NSError?>)->[AnyObject]!{return[["name":"Johnny Appleseed","email":"johnny@apple.com"]]}}letmockContext=MockNSManagedObjectContext()letfetchRequest=NSFetchRequest(entityName:"User")fetchRequest.predicate=NSPredicate(format:"email ENDSWITH[cd] %@","@apple.com")fetchRequest.resultType=.DictionaryResultTypevarerror:NSError?letresults=mockContext.executeFetchRequest(fetchRequest,error:&error)XCTAssertNil(error,"error should be nil")XCTAssertEqual(results.count,1,"fetch request should only return 1 result")letresult=results[0]as[String:String]XCTAssertEqual(result["name"]asString,"Johnny Appleseed","name should be Johnny Appleseed")XCTAssertEqual(result["email"]asString,"johnny@apple.com","email should be johnny@apple.com")}

With Xcode 6, we've finally arrived: the built-in testing tools are now good enough to use on their own. That is to say, there are no particularly compelling reasons to use any additional abstractions in order to provide acceptable test coverage for the vast majority apps and libraries. Except in extreme cases that require extensive stubbing, mocking, or other exotic test constructs, XCTest assertions, expectations, and performance measurements should be sufficient.

But no matter how good the testing tools have become, they're only good as how you actually use them.

If you're new to testing on iOS or OS X, start by adding a few assertions to that automatically-generated test case file and hitting ⌘U. You might be surprised at how easy and—dare I say—enjoyable you'll find the whole experience.

Swift Documentation

$
0
0

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{...}

Unrecognized Headerdoc

What is parsed, however, is something markedly different:

New Recognized Format

/**    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 Styleenum declaration, and the description renders beautifully with a bulleted list:

Swift enum Declaration Documentation

Open Quick Documentation for the method travel, and the parameter is parsed out into a separate field, as expected:

Swift func Declaration Documentation

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 and XXX 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.

Xcode 6 Documentation Source Navigator MARK / TODO / FIXME

// 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.

Alamofire

$
0
0

Swift has hit a reset button on the iOS developer community. It's really been something to behold for seasoned Objective-C developers.

Literally overnight, conversations shifted from namespacing and swizzling to generics and type inference. At a time of the year when Twitter would normally be abuzz with discussion about the latest APIs or speculation about upcoming hardware announcements, the discourse has been dominated by all things Swift.

A new language with new syntax and evolving conventions, Swift has captured the attention and imagination of iOS & OS X developers both old and new.

Although we still have a few months to wait before we can ship apps in Swift, there is already a proliferation of open source projects built with this new language.

One such project is Alamofire. This week, we'll take a look at the design and implementation of Alamofire, and how it's using the language features of Swift to those ends.

Full Disclosure: this article, as with the rest of NSHipster, is written by the creator of AFNetworking and Alamofire. While this makes me qualified to write about the technical details and direction of these projects, it certainly doesn't allow for an objective take on their relative merits. So take all of this with a grain of salt.


Alamofire is an HTTP networking library written in Swift. It leverages NSURLSession and the Foundation URL Loading System to provide first-class networking capabilities in a convenient Swift interface.

Getting started with Alamofire is easy. Here's how to make a GET request:

Alamofire.request(.GET,"http://httpbin.org/get").response{(request,response,data,error)inprintln(request)println(response)println(error)}

Alamofire.request is a convenience method that returns an Alamofire.Request object, which can be chained to add a response handler with response.

By default, response returns the accumulated NSData of the server response. To get a string representation of this instead, use the responseString method instead.

Alamofire.request(.GET,"http://httpbin.org/get").responseString{(request,response,string,error)inprintln(string)}

Similarly, to get a JSON object from the request, use the responseJSON method:

Alamofire.request(.GET,"http://httpbin.org/get").responseJSON{(request,response,JSON,error)inprintln(JSON)}

Chaining

Even minor syntactic differences can have wide-reaching implications for language conventions.

Chief among the complaints lodged against Objective-C was its use of square brackets for denoting message passing. One of the practical implications of [ ] syntax is the difficulty in chaining methods together. Even with Xcode autocomplete, @property dot syntax, and key-value coding key-paths, it is still rare to see deeply nested invocations.

In many ways, the concession of introducing dot syntax for properties in Objective-C 2.0 only served to escalate tensions further, although conventions have started to solidify in recent years.

In Swift, though, all methods are invoked using dot syntax with ordered and parameterized arguments passed inside parentheses, allowing methods returning Self to have successive methods chained together.

Alamofire uses this pattern for succinct, localized networking declarations:

Alamofire.request(.GET,"http://httpbin.org/get").authenticate(HTTPBasic:user,password:password).progress{(bytesRead,totalBytesRead,totalBytesExpectedToRead)inprintln(totalBytesRead)}.responseJSON{(request,response,JSON,error)inprintln(JSON)}.responseString{(request,response,string,error)inprintln(string)}

Requests can have multiple response handlers, which each execute asynchronously once the server has finished sending its response.

Trailing Closures

Closures are deeply integrated into Swift, so much so that if a methods's last argument is a closure type, a trailing closure can be used instead.

The chained methods in the previous example show this in action. For typical usage, it's really convenient to omit the syntactic cruft.

Optional Arguments & Flexible Method Signatures

When communicating with web APIs, it's common to send parameters with URL queries or HTTP body data:

Alamofire.request(.GET,"http://httpbin.org/get",parameters:["foo":"bar"])// GET http://httpbin.org/get?foo=bar

This is actually the same method as before. By default the parameters argument is nil. There's a fourth argument, and it's optional as well: the parameter encoding.

ParameterEncoding & Enums

This brings us to one of the cooler features of Swift: enums. Whereas C / Objective-C enums are merely typedef'd integer declarations, Swift enums support parameterized arguments and can have associated functionality.

Alamofire encapsulates all of the logic for encoding parameters into an HTTP message representation with the ParameterEncoding enum:

enumParameterEncoding{caseURLcaseJSON(options:NSJSONWritingOptions)casePropertyList(format:NSPropertyListFormat,options:NSPropertyListWriteOptions)funcencode(request:NSURLRequest,parameters:[String:AnyObject]?)->(NSURLRequest,NSError?){...}}

The parameterized arguments of ParameterEncoding cases allows for different JSON and Property List serialization options to be specified.

The encode method on each ParameterEncoding case transforms a request and set of parameters into a new request (with optional error return value).

Given a complex, nested set of parameters, encoding and sending as JSON is recommended:

There are no standards defining the encoding of data structures into URL-encoded query parameters, meaning that parsing behavior can vary between web application implementations. Even worse, there are certain structures that cannot be unambiguously represented by a query string. This is why JSON (or XML or plist) encoding is recommended for anything more complex than key-value, if the web API supports it.

letparameters=["foo":"bar","baz":["a",1],"qux":["x":1,"y":2,"z":3]]Alamofire.request(.POST,"http://httpbin.org/post",parameters:parameters,encoding:.JSON(options:nil)).responseJSON{(request,response,JSON,error)inprintln(JSON)}

Another enum is used in Alamofire to represent the HTTP methods defined in RFC 2616 §9:

publicenumMethod:String{caseOPTIONS="OPTIONS"caseGET="GET"caseHEAD="HEAD"casePOST="POST"casePUT="PUT"casePATCH="PATCH"caseDELETE="DELETE"caseTRACE="TRACE"caseCONNECT="CONNECT"}

These values are passed as the first method in request:

Alamofire.request(.POST,"http://httpbin.org/get")
Alamofire.request(.POST,"http://httpbin.org/get",parameters:["foo":"bar"])
Alamofire.request(.POST,"http://httpbin.org/get",parameters:["foo":"bar"],encoding:.JSON(options:nil))

Lazy Properties

Another new feature of Swift is the ability to lazily evaluate properties. A property with the lazy annotation will only evaluate, set, and return the value of an expression after it's called for the first time in code.

An Alamofire.Manager object takes advantage of this to defer construction of the default HTTP headers (Accept-Encoding, Accept-Language, & User-Agent) until the first request is constructed:

lazyvardefaultHeaders:[String:String]={// Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3letacceptEncoding:String="Accept-Encoding: gzip;q=1.0,compress;q=0.5"// Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4letacceptLanguage:String={// ...}()// User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43letuserAgent:String={// ...}()return["Accept-Encoding":acceptEncoding,"Accept-Language":acceptLanguage,"User-Agent":userAgent]}()

Public Immutable Property Backed By Private Mutable Property

In Objective-C, exposing readonly, immutable properties backed by mutable variables in the private implementation required some non-obvious declaration trickery.

With the introduction of access control to Swift in Xcode 6 β4, it's much easier to design a safe, immutable public API:

privatevarmutableData:NSMutableDataoverridevardata:NSData!{returnself.mutableData}

And Much, Much More...

There's a lot baked into the < 1000 LOC comprising Alamofire. Any aspiring Swift developer or API author would be advised to peruse through the source code to get a better sense of what's going on.


For anyone wondering where this leaves AFNetworking, don't worry: AFNetworking is stable and reliable, and isn't going anywhere. In fact, over the coming months, a great deal of work is going to be put into improving test coverage and documentation for AFNetworking 2 and its first-party extensions.

It's also important to note that AFNetworking can be easily used from Swift code, just like any other Objective-C code. Alamofire is a separate project investigating the implications of new language features and conventions on the problem of making HTTP requests.

Alamofire 1.0 is scheduled to coincide with the 1.0 release of Swift... whenever that is. Part of that milestone is complete documentation, and 100% Unit Test Coverage, making use of the new Xcode 6 testing infrastructure& httpbin by Kenneth Reitz.

We're all doing our best to understand how to design, implement, and distribute code in Swift. Alamofire is just one of many exciting new libraries that will guide the development of the language and community in the coming months and years. For anyone interested in being part of this, I welcome your contributions. As the mantra goes: pull requests welcome.

Swift Operators

$
0
0

What would a program be without statements? A mish-mash of classes, namespaces, conditionals, loops, and namespaces signifying nothing.

Statements are what do the work of a program. They are the very execution of an executable.

If we were to take apart a statement—say 1 + 2—decomposing it into its constituent parts, we would find an operator and operands:

1+2
left operandoperatorright operand

Although expressions are flat, the compiler will construct a tree representation, or AST:

1 + 2 AST

Compound statements, like 1 + 2 + 3

(1 + 2)+3
left operandoperatorright operand

1 + 2 + 3 AST

Or, to take an even more complex statement, 1 + 2 * 3 % 4, the compiler would use operator precedence to resolve the expression into a single statement:

1+((2 * 3) % 4)
left operandoperatorright operand

1 + 2 * 3 % 4 AST

Operator precedence rules, similar to the ones you learned in primary school, provide a canonical ordering for any compound statement:

1 + 2 * 3 % 4
1 + ((2 * 3) % 4)
2 + (6 % 4)
2 + 2

However, consider the statement 5 - 2 + 3. Addition and subtraction have the same operator precedence, but evaluating the subtraction first (5 - 2) + 3 yields 6, whereas evaluating subtraction after addition, 5 - (2 + 3), yields 0. In code, arithmetic operators are left-associative, meaning that the left hand side will evaluate first ((5 - 2) + 3).

Operators can be unary and ternary as well. The ! prefix operator negates a logical value of the operand, whereas the ++ postfix operator increments the operand. The ?: ternary operator collapses an if-else expression, by evaluating the statement to the left of the ? in order to either execute the statement left of the : (statement is true) or right of : (statement is false).

Swift Operators

Swift includes a set of operators that should be familiar to C or Objective-C developers, with a few additions (notably, the range and nil coalescing operators):

Prefix

  • ++: Increment
  • --: Decrement
  • +: Unary plus
  • -: Unary minus
  • !: Logical NOT
  • ~: Bitwise NOT

Infix

Exponentiative {precedence 160}
<<Bitwise left shift
>>Bitwise right shift
Multiplicative { associativity left precedence 150 }
*Multiply
/Divide
%Remainder
&*Multiply, ignoring overflow
&/Divide, ignoring overflow
&%Remainder, ignoring overflow
&Bitwise AND
Additive { associativity left precedence 140 }
+Add
-Subtract
&+Add with overflow
&-Subtract with overflow
|Bitwise OR
^Bitwise XOR
Range { precedence 135 }
..<Half-open range
...Closed range
Cast { precedence 132 }
isType check
asType cast
Comparative { precedence 130 }
<Less than
<=Less than or equal
>Greater than
>=Greater than or equal
==Equal
!=Not equal
===Identical
!==Not identical
~=Pattern match
Conjunctive { associativity left precedence 120 }
&&Logical AND
Disjunctive { associativity left precedence 110 }
||Logical OR
Nil Coalescing { associativity right precedence 110 }
??Nil coalescing
Ternary Conditional { associativity right precedence 100 }
?:Ternary conditional
Assignment { associativity right precedence 90 }
=Assign
*=Multiply and assign
/=Divide and assign
%=Remainder and assign
+=Add and assign
-=Subtract and assign
<<=Left bit shift and assign
>>=Right bit shift and assign
&=Bitwise AND and assign
^=Bitwise XOR and assign
|=Bitwise OR and assign
&&=Logical AND and assign
||=Logical OR and assign

Postfix

  • ++: Increment
  • --: Decrement

Member Functions

In addition to the aforementioned standard operators, there are some de facto operators defined by the language:

  • .: Member Access
  • ?: Optional
  • !: Forced-Value
  • []: Subscript
  • []=: Subscript Assignment

Overloading

Swift has the ability to overload operators such that existing operators, like +, can be made to work with additional types.

To overload an operator, simply define a new function for the operator symbol, taking the appropriate number of arguments.

For example, to overload * to repeat a string a specified number of times:

func*(left:String,right:Int)->String{ifright<=0{return""}varresult=leftfor_in1..<right{result+=left}returnresult}
"a"*6// "aaaaaa"

This is, however, a controversial language feature.

Any C++ developer would be all too eager to regale you with horror stories of the non-deterministic havoc this can wreak.

Consider the following statement:

[1,2]+[3,4]// [1, 2, 3, 4]

By default, the + operator acts on two arrays by appending the right hand to the left hand.

However, when overridden thusly:

func+(left:[Double],right:[Double])->[Double]{varsum=[Double](count:left.count,repeatedValue:0.0)for(i,_)inenumerate(left){sum[i]=left[i]+right[i]}returnsum}

The result is now an array with the pairwise sums of each element, expressed as Double:

[1,2]+[3,4]// [4.0, 6.0]

And if the operator were also overloaded to work with Int types, with:

func+(left:[Int],right:[Int])->[Int]{varsum=[Int](count:left.count,repeatedValue:0)for(i,_)inenumerate(left){sum[i]=left[i]+right[i]}returnsum}

The result would then be an array of pairwise sums, expressed as Int.

[1,2]+[3,4]// [4, 6]

Herein lies the original sin of operator overloading: ambiguous semantics.

Having been limited to basic arithmetic operators across many years and programming languages, overloading of operators has become commonplace:

  • Computing Sum of Integers: 1 + 2 // 3
  • Computing Sum of Floats: 1.0 + 2.0 // 3.0
  • Appending to String: "a" + "b" // "ab"
  • Appending to Array: ["foo"] + ["bar"] // ["foo", "bar"]

It makes sense that + would work on numbers—that's just math. But think about it: why should adding two strings together concatenate them? 1 + 2 isn't 12 (except in Javascript). Is this really intuitive, or is it just familiar.

PHP uses . for string concatenation (which is objectively a terrible idea). Objective-C allows consecutive string literals to be appended with whitespace.

In the run-up to it's initial stable release, Swift still has some work to do in resolving ambiguities in operator semantics. Recent changes, such as the addition of the nil coalescing operator (??), and the decision for optionals not to conform to BooleanType (confusing in the case of Bool?) are encouraging, and demonstrate the need for us to collectively ask ourselves "does this really make sense?", and file radars appropriately.

I'm specifically concerned about the semantics of array operators, as demonstrated in the previous example. My 2 cents: arrays should forego the + and - operators in lieu of <<:

func<<<T>(inoutleft:[T],right:[T])->[T]{left.extend(right)returnleft}func<<<T>(inoutleft:[T],right:T)->[T]{left.append(right)returnleft}

Custom Operators

An even more controversial and exciting feature is the ability to define custom operators.

Consider the arithmetic operator found in many programming languages, but missing in swift is **, which raises the left hand number to the power of the right hand number (the ^ symbol, commonly used for superscripts, is already used to perform a bitwise XOR).

To add this operator in Swift, first declare the operator:

infixoperator**{associativityleftprecedence160}
  • infix specifies that it is a binary operator, taking a left and right hand argument
  • operator is a reserved word that must be preceded with either prefix, infix, or postfix
  • ** is the operator itself
  • associativity left means that operations are grouped from the left
  • precedence 160 means that it will evaluate with the same precedence as the exponential operators << and >> (left and right bitshift).
func ** (left: Double, right: Double) -> Double {
    return pow(left, right)
}
2**3// 8

When creating custom operators, make sure to also create the corresponding assignment operator, if appropriate:

infixoperator**={associativityrightprecedence90}func**=(inoutleft:Double,right:Double){left=left**right}

Note that left is inout, which makes sense, since assignment mutates the original value.

Custom Operators with Protocol and Method

Function definitions for the operators themselves should be extremely simple—a single LOC, really. For anything more complex, some additional setup is warranted.

Take, for example, a custom operator, =~, which returns whether the left hand side matches a regular expression on the right hand side:

protocolRegularExpressionMatchable{funcmatch(pattern:String,options:NSRegularExpressionOptions)->Bool}extensionString:RegularExpressionMatchable{funcmatch(pattern:String,options:NSRegularExpressionOptions=nil)->Bool{letregex=NSRegularExpression(pattern:pattern,options:nil,error:nil)returnregex.numberOfMatchesInString(self,options:nil,range:NSMakeRange(0,self.utf16Count))!=0}}infixoperator=~{associativityleftprecedence130}func=~<T:RegularExpressionMatchable>(left:T,right:String)->Bool{returnleft.match(right,options:nil)}
  • First, a RegularExpressionMatchableprotocol is declared, with a single method for matching regular expressions.
  • Next, an extension adding conformance to this protocol to String is declared, with a provided implementation of match, using NSRegularExpression.
  • Finally, the =~ operator is declared and implemented on a generic type conforming to RegularExpressionMatchable.

By doing this, a user has the option to use the match function instead of the operator. It also has the added benefit of greater flexibility in what options are passed into the method.

Actually, there's an even more clever way that this could be done. We'll look into that more next week.

This is all to say: a custom operator should only ever be provided as a convenience for an existing function.

Use of Mathematical Symbols

Custom operators can begin with one of the ASCII characters /, =, -, +, !, *, %, <, >, &, |, , or ~, or any of the Unicode characters in the "Math Symbols" character set.

This makes it possible to take the square root of a number with a single prefix operator (⌥v):

prefixoperator{}prefixfunc(number:Double)->Double{returnsqrt(number)}
4// 2

Or consider the ± operator, which can be used either as an infix or prefix to return a tuple with the sum and difference:

infixoperator±{associativityleftprecedence140}func±(left:Double,right:Double)->(Double,Double){return(left+right,left-right)}prefixoperator±{}prefixfunc±(value:Double)->(Double,Double){return0±value}
2±3// (-1, 5)±4// (-4, 4)

For more examples of functions using mathematical notation in Swift, check out Euler.

Custom operators are hard to type, and therefore hard to use. Exercise restraint when using custom operators with exotic symbols. After all, code should not be copy-pasted.


Operators in Swift are among the most interesting and indeed controversial features of this new language.

When overriding or defining new operators in your own code, make sure to follow these guidelines:

Guidelines for Swift Operators

  1. Don't create an operator unless its meaning is obvious and undisputed. Seek out any potential conflicts to ensure semantic consistency.
  2. Custom operators should only be provided as a convenience. Complex functionality should always be implemented in a function, preferably one specified as a generic using a custom protocol.
  3. Pay attention to the precedence and associativity of custom operators. Find the closest existing class of operators and use the appropriate precedence value.
  4. If it makes sense, be sure to implement assignment shorthand for a custom operator (e.g. += for +).

Swift Literal Convertibles

$
0
0

Last week, we wrote about overloading and creating custom operators in Swift, a language feature that is as powerful as it is controversial.

By all accounts, this week's issue threatens to be equally polarizing, as it covers a feature of Swift that is pervasive, yet invisible: literal convertibles.


In code, a literal is notation representing a fixed value. Most languages define literals for logical values, numbers, strings, and often arrays and dictionaries.

letint=57letfloat=6.02letstring="Hello"

Literals are so ingrained in a developer's mental model of programming that most of us don't actively consider what the compiler is actually doing (thereby remaining blissfully unaware of neat tricks like string interning).

Having a shorthand for these essential building blocks makes code easier to both read and write.

In Swift, developers are provided a hook into how values are constructed from literals, called literal convertible protocols.

The standard library defines 10 such protocols:

  • ArrayLiteralConvertible
  • BooleanLiteralConvertible
  • CharacterLiteralConvertible
  • DictionaryLiteralConvertible
  • ExtendedGraphemeClusterLiteralConvertible
  • FloatLiteralConvertible
  • NilLiteralConvertible
  • IntegerLiteralConvertible
  • StringLiteralConvertible
  • StringInterpolationConvertible

Any class or struct conforming to one of these protocols will be eligible to have an instance of itself statically initialized from the corresponding literal.

It's what allows literal values to "just work" across the language.

Take optionals, for example.

NilLiteralConvertible and Optionals

One of the best parts of optionals in Swift is that the underlying mechanism is actually defined in the language itself:

enumOptional<T>:Reflectable,NilLiteralConvertible{caseNonecaseSome(T)init()init(_some:T)varhasValue:Bool{get}funcmap<U>(f:(T)->U)->U?funcgetMirror()->MirrorTypestaticfuncconvertFromNilLiteral()->T?}

Notice that Optional conforms to the NilLiteralConvertible protocol:

protocolNilLiteralConvertible{classfuncconvertFromNilLiteral()->Self}

Now consider the two statements:

vara:AnyObject=nil// !varb:AnyObject?=nil

The declaration of var a generates the compiler warning Type 'AnyObject' does not conform to the protocol 'NilLiteralConvertible, while the declaration var b works as expected.

Under the hood, when a literal value is assigned, the Swift compiler consults the corresponding protocol (in this case NilLiteralConvertible), and assigns the return value of the conversion function (convertFromNilLiteral()).

Although the implementation of convertFromNilLiteral() is private, the end result is that an Optional set to nil becomes .None.

StringLiteralConvertible and Regular Expressions

Swift literal convertibles can be used to provide convenient shorthand initializers for custom objects.

Recall our Regex example from last week:

structRegex{letpattern:Stringletoptions:NSRegularExpressionOptions!privatevarmatcher:NSRegularExpression{returnNSRegularExpression(pattern:self.pattern,options:self.options,error:nil)}init(pattern:String,options:NSRegularExpressionOptions=nil){self.pattern=patternself.options=options}funcmatch(string:String,options:NSMatchingOptions=nil)->Bool{returnself.matcher.numberOfMatchesInString(string,options:options,range:NSMakeRange(0,string.utf16Count))!=0}}

Developers coming from a Ruby or Perl background may be disappointed by Swift's lack of support for regular expression literals, but this can be retcon'd in using the StringLiteralConvertible protocol:

extensionRegex:StringLiteralConvertible{typealiasExtendedGraphemeClusterLiteralType=StringLiteralTypestaticfuncconvertFromExtendedGraphemeClusterLiteral(value:ExtendedGraphemeClusterLiteralType)->Regex{returnself(pattern:value)}staticfuncconvertFromStringLiteral(value:StringLiteralType)->Regex{returnself(pattern:value)}}

StringLiteralConvertible itself inherits from the ExtendedGraphemeClusterLiteralConvertible protocol. ExtendedGraphemeClusterLiteralType is an internal type representing a String of length 1. In order to implement convertFromExtendedGraphemeClusterLiteral(), ExtendedGraphemeClusterLiteralType can be typealias'd to StringLiteralType.

Now, we can do this:

letstring:String="foo bar baz"letregex:Regex="foo"regex.match(string)// true

...or more simply:

"foo".match(string)// true

Combined with the custom operator =~, this can be made even more idiomatic:

"foo bar baz"=~"foo"// true

Some might bemoan this as the end of comprehensibility, while others will see this merely as filling in one of the missing parts of this new language.

It's all just a matter of what you're used to, and whether you think a developer is entitled to add features to a language in order for it to better suit their purposes.

Either way, I hope we can all agree that this language feature is interesting, and worthy of further investigation. So in that spirit, let's venture forth and illustrate a few more use cases.


ArrayLiteralConvertible and Sets

For a language with such a deep regard for immutability and safety, it's somewhat odd that there is no built-in support for sets in the standard library.

Arrays are nice and all, but the O(1) lookup and idempotence of sets... *whistful sigh*

So here's a simple example of how Set might be implemented in Swift, using the built-in Dictionary type:

structSet<T:Hashable>{typealiasIndex=Tprivatevardictionary:[T:Bool]init(){self.dictionary=[T:Bool]()}varcount:Int{returnself.dictionary.count}varisEmpty:Bool{returnself.dictionary.isEmpty}funccontains(element:T)->Bool{returnself.dictionary[element]??false}mutatingfuncput(element:T){self.dictionary[element]=true}mutatingfuncremove(element:T)->Bool{ifself.contains(element){self.dictionary.removeValueForKey(element)returntrue}else{returnfalse}}}

A real, standard library-calibre implementation of Set would involve a lot more Swift-isms, like generators, sequences, and all manner of miscellaneous protocols. It's enough to write an entirely separate article about.

Of course, a standard collection class is only as useful as it is convenient to use. NSSet wasn't so lucky to receive the first-class treatment when array and dictionary literal syntax was introduced with the Apple LLVM Compiler 4.0, but we can right the wrongs of the past with the ArrayLiteralConvertible protocol:

protocol ArrayLiteralConvertible {
    typealias Element
    class func convertFromArrayLiteral(elements: Element...) -> Self
}

Extending Set to adopt this protocol is relatively straightforward:

extensionSet:ArrayLiteralConvertible{staticfuncconvertFromArrayLiteral(elements:T...)->Set<T>{varset=Set<T>()forelementinelements{set.put(element)}returnset}}

But that's all it takes to achieve our desired results:

letset:Set=[1,2,3]set.contains(1)// trueset.count// 3

This example does, however, highlight a legitimate concern for literal convertibles: type inference ambiguity. Because of the significant API overlap between collection classes like Array and `Set, one could ostensibly write code that would behave differently depending on how the type was resolved (e.g. set addition is idempotent, whereas arrays accumulate, so the count after adding two equivalent elements would differ)

StringLiteralConvertible and URLs

Alright, one last example creative use of literal convertibles: URL literals.

NSURL is the fiat currency of the URL Loading System, with the nice feature of introspection of its component parts according to RFC 2396. Unfortunately, it's so inconvenient to instantiate, that third-party framework authors often decide to ditch them in favor of worse-but-more-convenient strings for method parameters.

With a simple extension on NSURL, one can get the best of both worlds:

extensionNSURL:StringLiteralConvertible{publicclassfuncconvertFromExtendedGraphemeClusterLiteral(value:String)->Self{returnself(string:value)}publicclassfuncconvertFromStringLiteral(value:String)->Self{returnself(string:value)}}

One neat feature of literal convertibles is that the type inference works even without a variable declaration:

"http://nshipster.com/".host// nshipster.com

As a community, it's up to us to decide what capabilities of Swift are features and what are bugs. We'll be the ones to distinguish pattern from anti-pattern; convention from red flag.

So it's unclear, at the present moment, how things like literal convertibles, custom operators, and all of the other capabilities of Swift will be reconciled. This publication has, at times, been more or less prescriptive on how things should be, but in this case, that's not the case here.

All there is to be done is to experiment and learn.


WKWebView

$
0
0

iOS has a complicated relationship with the web. And it goes back to the very inception of the platform nearly a decade ago.

It's difficult to appreciate just how differently the first iPhone could have turned out. The iconic touchscreen device we know and love today was just one option of the table. Early prototypes explored the use of a physical keyboard and a touch screen + stylus combo, with screen dimensions going up to 5" x 7". Even the iPod click wheel was a serious contender for a time.

But perhaps the most significant early decision to be made involved software, not hardware.

How should the iPhone run software? Apps, like on OSX, or as web pages, using Safari? That choice to fork OS X and build iPhoneOS had widespread implications, and remains a contentious decision to this day.

Consider this infamous line from Steve Jobs' WWDC 2007 keynote:

The full Safari engine is inside of iPhone. And so, you can write amazing Web 2.0 and Ajax apps that look exactly and behave exactly like apps on the iPhone. And these apps can integrate perfectly with iPhone services. They can make a call, they can send an email, they can look up a location on Google Maps.

The web has always been a second-class citizen on iOS (which is ironic, since the iPhone is largely responsible for the mobile web existing as it does today). UIWebView is massive and clunky and leaks memory. It lags behind Mobile Safari, which has the benefit of the Nitro JavaScript engine.

However, all of this has changed with the introduction of WKWebView and the rest of the WebKit framework.


WKWebView is the centerpiece of the modern WebKit API introduced in iOS 8 & Mac OS X Yosemite. It replaces UIWebView in UIKit and WebView in AppKit, offering a consistent API across the two platforms.

Boasting responsive 60fps scrolling, built-in gestures, streamlined communication between app and webpage, and the same JavaScript engine as Safari, WKWebView is one of the most significant announcements to come out of WWDC 2014.

What was a single class and protocol with UIWebView& UIWebViewDelegate has been factored out into 14 classes and 3 protocols in WKWebKit. Don't be alarmed by the huge jump in complexity, though—this new architecture allows for a ton of new features:

WKWebKit Framework

Classes

  • WKBackForwardList: A list of webpages previously visited in a web view that can be reached by going back or forward.
    • WKBackForwardListItem: Represents a webpage in the back-forward list of a web view.
  • WKFrameInfo: Contains information about a frame on a webpage.
  • WKNavigation: Contains information for tracking the loading progress of a webpage.
    • WKNavigationAction: Contains information about an action that may cause a navigation, used for making policy decisions.
    • WKNavigationResponse: Contains information about a navigation response, used for making policy decisions.
  • WKPreferences: Encapsulates the preference settings for a web view.
  • WKProcessPool: Represents a pool of Web Content processes.
  • WKUserContentController: Provides a way for JavaScript to post messages and inject user scripts to a web view.
    • WKScriptMessage: Contains information about a message sent from a webpage.
    • WKUserScript: Represents a script that can be injected into a webpage.
  • WKWebViewConfiguration: A collection of properties with which to initialize a web view.
  • WKWindowFeatures: Specifies optional attributes for the containing window when a new web view is requested.

Protocols

  • WKNavigationDelegate: Provides methods for tracking the progress of main frame navigations and for deciding load policy for main frame and subframe navigations.
  • WKScriptMessageHandler: Provides a method for receiving messages from JavaScript running in a webpage.
  • WKUIDelegate: Provides methods for presenting native user interface elements on behalf of a webpage.

API Diff Between UIWebView& WKWebView

WKWebView inherits much of the same programming interface as UIWebView, making it convenient for apps to migrate to WKWebKit (and conditionally compile as necessary while iOS 8 gains widespread adoption).

For anyone interested in the specifics, here's a comparison of the APIs of each class:

UIWebViewWKWebView
var scrollView: UIScrollView! { get }var scrollView: UIScrollView! { get }
var configuration: WKWebViewConfiguration! { get }
var delegate: UIWebViewDelegate!var UIDelegate: WKUIDelegate!
var navigationDelegate: WKNavigationDelegate!
var backForwardList: WKBackForwardList! { get }

Loading

UIWebViewWKWebView
func loadRequest(request: NSURLRequest!)func loadRequest(request: NSURLRequest!) -> WKNavigation!
func loadHTMLString(string: String!, baseURL: NSURL!)func loadHTMLString(string: String!, baseURL: NSURL!) -> WKNavigation!
func loadData(data: NSData!, MIMEType: String!, textEncodingName: String!, baseURL: NSURL!)
var estimatedProgress: Double { get }
var hasOnlySecureContent: Bool { get }
func reload()func reload() -> WKNavigation!
func reloadFromOrigin() -> WKNavigation!
func stopLoading()func stopLoading()
var request: NSURLRequest! { get }
var URL: NSURL! { get }
var title: String! { get }

History

UIWebViewWKWebView
func goToBackForwardListItem(item: WKBackForwardListItem!) -> WKNavigation!
func goBack()func goBack() -> WKNavigation!
func goForward()func goForward() -> WKNavigation!
var canGoBack: Bool { get }var canGoBack: Bool { get }
var canGoForward: Bool { get }var canGoForward: Bool { get }
var loading: Bool { get }var loading: Bool { get }

Javascript Evaluation

UIWebViewWKWebView
func stringByEvaluatingJavaScriptFromString(script: String!) -> String!
func evaluateJavaScript(javaScriptString: String!, completionHandler: ((AnyObject!, NSError!) -> Void)!)

Miscellaneous

UIWebViewWKWebView
var keyboardDisplayRequiresUserAction: Bool
var scalesPageToFit: Bool
var allowsBackForwardNavigationGestures: Bool

Pagination

WKWebView currently lacks equivalent APIs for paginating content.

  • var paginationMode: UIWebPaginationMode
  • var paginationBreakingMode: UIWebPaginationBreakingMode
  • var pageLength: CGFloat
  • var gapBetweenPages: CGFloat
  • var pageCount: Int { get }

Refactored into WKWebViewConfiguration

The following properties on UIWebView have been factored into a separate configuration object, which is passed into the initializer for WKWebView:

  • var dataDetectorTypes: UIDataDetectorTypes
  • var allowsInlineMediaPlayback: Bool
  • var mediaPlaybackRequiresUserAction: Bool
  • var mediaPlaybackAllowsAirPlay: Bool
  • var suppressesIncrementalRendering: Bool

JavaScript ↔︎ Swift Communication

One of the major improvements over UIWebView is how interaction and data can be passed back and forth between an app and its web content.

Injecting Behavior with User Scripts

WKUserScript allows JavaScript behavior to be injected at the start or end of document load. This powerful feature allows for web content to be manipulated in a safe and consistent way across page requests.

As a simple example, here's how a user script can be injected to change the background color of a web page:

letsource="document.body.style.background = \"#777\";"letuserScript=WKUserScript(source:source,injectionTime:.AtDocumentEnd,forMainFrameOnly:true)letuserContentController=WKUserContentController()userContentController.addUserScript(userScript)letconfiguration=WKWebViewConfiguration()configuration.userContentController=userContentControllerself.webView=WKWebView(frame:self.view.bounds,configuration:configuration)

WKUserScript objects are initialized with JavaScript source, as well as whether it should be injected at the start or end of loading the page, and whether this behavior should be used for all frames, or just the main frame. The user script is then added to a WKUserContentController, which is set on the WKWebViewConfiguration object passed into the initializer for WKWebView.

This example could easily be extended to perform more significant modifications to the page, such as removing advertisements, hiding comments, or maybe changing all occurrences of the phrase "the cloud" to "my butt".

Message Handlers

Communication from web to app has improved significantly as well, with message handlers.

Just like console.log prints out information to the Safari Web Inspector debug console, information from a web page can be passed back to the app by invoking:

window.webkit.messageHandlers.{NAME}.postMessage()

What's really great about this API is that JavaScript objects are automatically serialized into native Objective-C or Swift objects.

The name of the handler is configured in addScriptMessageHandler(), which registers a handler conforming to the WKScriptMessageHandler protocol:

classNotificationScriptMessageHandler:WKScriptMessageHandler{funcuserContentController(userContentController:WKUserContentController!,didReceiveScriptMessagemessage:WKScriptMessage!){println(message["body"])}}letuserContentController=WKUserContentController()lethandler=NotificationScriptMessageHandler()userContentController.addScriptMessageHandler(handler,name:"notification")

Now, when a notification comes into the app, such as to notify the creation of a new object on the page, that information can be passed with:

window.webkit.messageHandlers.notification.postMessage({body:"..."});

Add User Scripts to create hooks for webpage events that use Message Handlers to communicate status back to the app.

The same approach can be used to scrape information from the page for display or analysis within the app.

For example, if someone were to build a browser specifically for NSHipster.com, it could have a button that listed related articles in a popover:

// document.location.href == "http://nshipster.com/webkit"functiongetRelatedArticles(){varrelated=[];varelements=document.getElementById("related").getElementsByTagName("a");for(i=0;i<related.length;i++){vara=elements[i];related.push({href:a.href,title:a.title});}window.webkit.messageHandlers.related.postMessage({articles:related});}
letjs="getRelatedArticles();"self.webView?.evaluateJavaScript(js){(_,error)inprintln(error)}// Get results in previously-registered message handler

If your app is little more than a thin container around web content, WKWebView is a game-changer. All of that performance and compatibility that you've longed for is finally available. It's everything you might have hoped for.

If you're more of a native controls purist, you may be surprised at the power and flexibility afforded by the new technologies in iOS 8. It's a dirty secret that some stock apps like Messages use WebKit to render tricky content. The fact that you probably haven't noticed should be an indicator that web views actually have a place in app development best practices.

Swift Default Protocol Implementations

$
0
0

Swift was announced 3 months ago to the day. For many of us, it was among the most shocking and exciting events in our professional lives. In these intervening months, it's safe to say our collective understanding and appreciation of the language has evolved and changed significantly.

First came the infatuation period. We fixated on appearances, on surface-level features like Unicode support (let 🐶🐮!) and its new, streamlined syntax. Hell, even its name was objectively better than its predecessor's.

Within a few weeks, though, after having a chance to go through the Swift manual a few times, we started to understand the full implications of this new multi-paradigm language. All of those folks who had affected the zealotry of functional programmers in order to sound smarter (generics!) learned enough to start backing it up. We finally got the distinction between class and struct down, and picked up a few tricks like custom operators and literal convertibles along the way. All of that initial excitement could now be channeled productively into apps and libraries and tutorials.

Next week's announcement effectively marks the end of the summer for iOS & OS X developers. It's time to reign in our experimentation and start shipping again.

But hey, we have another few days before things get real again. Let's learn a few more things:


Generics are the defining feature of Swift. Working in coordination with the language's powerful type system, a developer can write safer and more performant code than was ever possible with Objective-C.

The underlying mechanism for generics are protocols. A Swift protocol, like an Objective-C @protocol declares methods and properties to be implemented in order to conform to it.

Within the Object-Oriented paradigm, types are often conflated with class identity. When programming in Swift, though, think about polymorphism through protocols first, before resorting to inheritance.

The one major shortcoming of protocols, both in Swift and Objective-C, is the lack of a built-in way to provide default implementations for methods, as one might accomplish in other languages with mixins or traits.

...but that's not the end of the story. Swift is a fair bit more Aspect-Oriented than it initially lets on.

Consider the Equatable protocol, used throughout the standard library:

protocolEquatable{func==(lhs:Self,rhs:Self)->Bool}

Given an Articlestruct with a title and body field, implementing Equatable is straightforward:

structArticle{lettitle:Stringletbody:String}extensionArticle:Equatable{}func==(lhs:Article,rhs:Article)->Bool{returnlhs.title==rhs.title&&lhs.body==rhs.body}

With everything in place, let's show Equatable in action:

lettitle="Swift Custom Operators: Syntactic Sugar or Menace to Society?"letbody="..."leta=Article(title:title,body:body)letb=Article(title:title,body:body)a==b// truea!=b// false

Wait... where did != come from?

!= isn't defined by the Equatable protocol, and it's certainly not implemented for Article. So what's going on?

!= is actually drawing its implementation from this function in the standard library:

func!=<T:Equatable>(lhs:T,rhs:T)->Bool

Because != is implemented as a generic function for Equatable, any type that conforms to Equatable, including Article, automatically gets the != operator as well.

If we really wanted to, we could override the implementation of !=:

func!=(lhs:Article,rhs:Article)->Bool{return!(lhs==rhs)}

For equality, it's unlikely that we could offer something more efficient than the negation of the provided == check, but this might make sense in other cases. Swift's type inference system allows more specific declarations to trump any generic or implicit candidates.

The standard library uses generic operators all over the place, like for bitwise operations:

protocolBitwiseOperationsType{func&(_:Self,_:Self)->Selffunc|(_:Self,_:Self)->Selffunc^(_:Self,_:Self)->Selfprefixfunc~(_:Self)->SelfclassvarallZeros:Self{get}}

Implementing functionality in this way significantly reduces the amount of boilerplate code needed to build on top of existing infrastructure.

Default Implementation of Methods

However, the aforementioned technique only really works for operators. Providing a default implementation of a protocol method is less convenient.

Consider a protocol P with a method m() that takes a single Int argument:

protocolP{funcm(arg:Int)}

The closest one can get to a default implementation is to provide a top-level generic function that takes explicit self as the first argument:

protocolP{funcm()/* {        f(self)    }*/}funcf<T:P>(_arg:T){// ...}

The commented-out code in the protocol helps communicate the provided functional implementation to the consumer.


All of this highlights a significant tension between methods and functions in Swift.

The Object-Oriented paradigm is based around the concept of objects that encapsulate both state and behavior. However, in Swift, it's simply impossible to implement certain generic functions as methods on the struct or class itself.

Take, for instance, the contains method:

funccontains<S:SequenceTypewhereS.Generator.Element:Equatable>(seq:S,x:S.Generator.Element)->Bool

Because of the constraint on the element of the sequence generator being Equatable, this cannot be declared on a generic container, without thereby requiring elements in that collection to conform to Equatable.

Relegating behavior like contains, advance, or partition to top-level functions does a disservice to the standard library. Not only does it hide functionality from method autocompletion, but it fragments the API across a Object-Oriented and Functional paradigms.

Although it's unlikely that this will be resolved in time for 1.0 (and there are certainly more pressing matters), there are a number of ways this could be resolved:

  • Provide mixin or trait functionality that extend protocols to allow them to provide default implementations.
  • Allow extensions with generic arguments, such that something like extension Array<T: Equatable> could define additional methods, like func contains(x: T), that are only available to associated types that match a particular criteria.
  • Automatically bridge function calls with Self as the first argument to be available as methods using implicit self.

RawOptionSetType

$
0
0

In Objective-C, NS_ENUM& NS_OPTIONS are used to annotate C enums in such a way that sets clear expectations for both the compiler and developer. Since being introduced to Objective-C with Xcode 4.5, these macros have become a standard convention in system frameworks, and a best practice within the community.

In Swift, enumerations are codified as a first-class language construct as fundamental as a struct or class, and include a number of features that make them even more expressive, like raw types and associated values. They're so perfectly-suited to encapsulating closed sets of fixed values, that developers would do well to actively seek out opportunities to use them.

When interacting with frameworks like Foundation in Swift, all of those NS_ENUM declarations are automatically converted into an enum—often improving on the original Objective-C declaration by eliminating naming redundancies:

enumUITableViewCellStyle:Int{caseDefaultcaseValue1caseValue2caseSubtitle}
typedefNS_ENUM(NSInteger,UITableViewCellStyle){UITableViewCellStyleDefault,UITableViewCellStyleValue1,UITableViewCellStyleValue2,UITableViewCellStyleSubtitle};

Unfortunately, for NS_OPTIONS, the Swift equivalent is arguably worse:

structUIViewAutoresizing:RawOptionSetType{init(_value:UInt)varvalue:UIntstaticvarNone:UIViewAutoresizing{get}staticvarFlexibleLeftMargin:UIViewAutoresizing{get}staticvarFlexibleWidth:UIViewAutoresizing{get}staticvarFlexibleRightMargin:UIViewAutoresizing{get}staticvarFlexibleTopMargin:UIViewAutoresizing{get}staticvarFlexibleHeight:UIViewAutoresizing{get}staticvarFlexibleBottomMargin:UIViewAutoresizing{get}}
typedefNS_OPTIONS(NSUInteger,UIViewAutoresizing){UIViewAutoresizingNone=0,UIViewAutoresizingFlexibleLeftMargin=1<<0,UIViewAutoresizingFlexibleWidth=1<<1,UIViewAutoresizingFlexibleRightMargin=1<<2,UIViewAutoresizingFlexibleTopMargin=1<<3,UIViewAutoresizingFlexibleHeight=1<<4,UIViewAutoresizingFlexibleBottomMargin=1<<5};

RawOptionsSetType is the Swift equivalent of NS_OPTIONS (or at least as close as it gets). It is a protocol that adopts the RawRepresentable, Equatable, BitwiseOperationsType, and NilLiteralConvertible protocols. An option type can be represented by a struct conforming to RawOptionsSetType.

Why does this suck so much? Well, the same integer bitmasking tricks in C don't work for enumerated types in Swift. An enum represents a type with a closed set of valid options, without a built-in mechanism for representing a conjunction of options for that type. An enum could, ostensibly, define a case for all possible combinations of values, but for n > 3, the combinatorics make this approach untenable. There are a few different ways NS_OPTIONS could be implemented in Swift, but RawOptionSetType is probably the least bad.

Compared to the syntactically concise enum declaration, RawOptionsSetType is awkward and cumbersome, requiring over a dozen lines of boilerplate for computed properties:

structToppings:RawOptionSetType,BooleanType{privatevarvalue:UInt=0init(_value:UInt){self.value=value}// MARK: RawOptionSetTypestaticfuncfromMask(raw:UInt)->Toppings{returnself(raw)}// MARK: RawRepresentablestaticfuncfromRaw(raw:UInt)->Toppings?{returnself(raw)}functoRaw()->UInt{returnvalue}// MARK: BooleanTypevarboolValue:Bool{returnvalue!=0}// MARK: BitwiseOperationsTypestaticvarallZeros:Toppings{returnself(0)}// MARK: NilLiteralConvertiblestaticfuncconvertFromNilLiteral()->Toppings{returnself(0)}// MARK: -staticvarNone:Toppings{returnself(0b0000)}staticvarExtraCheese:Toppings{returnself(0b0001)}staticvarPepperoni:Toppings{returnself(0b0010)}staticvarGreenPepper:Toppings{returnself(0b0100)}staticvarPineapple:Toppings{returnself(0b1000)}}

As of Xcode 6 Beta 6, RawOptionSetType no longer conforms to BooleanType, which is required for performing bitwise checks.

One nice thing about doing this in Swift is its built-in binary integer literal notation, which allows the bitmask to be computed visually. And once the options type is declared, the usage syntax is not too bad.

Taken into a larger example for context:

structPizza{enumStyle{caseNeopolitan,Sicilian,NewHaven,DeepDish}structToppings:RawOptionSetType{...}letdiameter:Intletstyle:Stylelettoppings:Toppingsinit(inchesInDiameterdiameter:Int,style:Style,toppings:Toppings=.None){self.diameter=diameterself.style=styleself.toppings=toppings}}letdinner=Pizza(inchesInDiameter:12,style:.Neopolitan,toppings:.Pepperoni|.GreenPepper)

A value membership check can be performed with the & operator, just like with unsigned integers in C:

extensionPizza{varisVegetarian:Bool{returntoppings&Toppings.Pepperoni?false:true}}dinner.isVegetarian// false

In all fairness, it may be too early to really appreciate what role option types will have in the new language. It could very well be that Swift's other constructs, like tuples or pattern matching—or indeed, even enums—make options little more than a vestige of the past.

Either way, if you're looking to implement an NS_OPTIONS equivalent in your code base, here's an Xcode snippet-friendly example of how to go about it:

struct<#Options#>:RawOptionSetType,BooleanType{privatevarvalue:UInt=0init(_value:UInt){self.value=value}varboolValue:Bool{returnvalue!=0}staticfuncfromMask(raw:UInt)-><#Options#>{returnself(raw)}staticfuncfromRaw(raw:UInt)-><#Options#>?{returnself(raw)}functoRaw()->UInt{returnvalue}staticvarallZeros:<#Options#>{returnself(0)}staticfuncconvertFromNilLiteral()-><#Options#>{returnself(0)}staticvarNone:<#Options#>{returnself(0b0000)}staticvar<#Option#>:<#Options#>{returnself(0b0001)}// ...}

Image Resizing Techniques

$
0
0

Since time immemorial, iOS developers have been perplexed by a singular question: "How do you resize an image?". It is a question of beguiling clarity, spurred on by a mutual mistrust of developer and platform. A thousand code samples litter web search results, each claiming to be the One True Solution, and all the others false prophets.

It's embarrassing, really.

This week's article endeavors to provide a clear explanation of the various approaches to image resizing on iOS (and OS X, making the appropriate UIImageNSImage conversions), using empirical evidence to offer insights into the performance characteristics of each approach, rather than simply prescribing any one way for all situations.

Before reading any further, please note the following:

When setting a UIImage on a UIImageView, manual resizing is unnecessary for the vast majority of use cases. Instead, one can simply set the contentMode property to either .ScaleAspectFit to ensure that the entire image is visible within the frame of the image view, or .ScaleAspectFill to have the entire image view filled by the image, cropping as necessary from the center.

imageView.contentMode=.ScaleAspectFitimageView.image=image

Determining Scaled Size

Before doing any image resizing, one must first determine the target size to scale to.

Scaling by Factor

The simplest way to scale an image is by a constant factor. Generally, this involves dividing by a whole number to reduce the original size (rather than multiplying by a whole number to magnify).

A new CGSize can be computed by scaling the width and height components individually:

letsize=CGSizeMake(image.size.width/2.0,image.size.height/2.0)

...or by applying a CGAffineTransform:

letsize=CGSizeApplyAffineTransform(image.size,CGAffineTransformMakeScale(0.5,0.5))

Scaling by Aspect Ratio

It's often useful to scale the original size in such a way that fits within a rectangle without changing the original aspect ratio. AVMakeRectWithAspectRatioInsideRect is a useful function found in the AVFoundation framework that takes care of that calculation for you:

importAVFoundationletsize=AVMakeRectWithAspectRatioInsideRect(image.size,imageView.bounds)

Resizing Images

There are a number of different approaches to resizing an image, each with different capabilities and performance characteristics.

UIGraphicsBeginImageContextWithOptions& UIImage -drawInRect:

The highest-level APIs for image resizing can be found in the UIKit framework. Given a UIImage, a temporary graphics context can be used to render a scaled version, using UIGraphicsBeginImageContextWithOptions() and UIGraphicsGetImageFromCurrentImageContext():

letimage=UIImage(contentsOfFile:self.URL.absoluteString!)letsize=CGSizeApplyAffineTransform(image.size,CGAffineTransformMakeScale(0.5,0.5))lethasAlpha=falseletscale:CGFloat=0.0// Automatically use scale factor of main screenUIGraphicsBeginImageContextWithOptions(size,!hasAlpha,scale)image.drawInRect(CGRect(origin:CGPointZero,size:size))letscaledImage=UIGraphicsGetImageFromCurrentImageContext()

UIGraphicsBeginImageContextWithOptions() creates a temporary rendering context into which the original is drawn. The first argument, size, is the target size of the scaled image. The second argument, isOpaque is used to determine whether an alpha channel is rendered. Setting this to false for images without transparency (i.e. an alpha channel) may result in an image with a pink hue. The third argument scale is the display scale factor. When set to 0.0, the scale factor of the main screen is used, which for Retina displays is 2.0.

CGBitmapContextCreate& CGContextDrawImage

Core Graphics / Quartz 2D offers a lower-level set of APIs that allow for more advanced configuration. Given a CGImage, a temporary bitmap context is used to render the scaled image, using CGBitmapContextCreate() and CGBitmapContextCreateImage():

letimage=UIImage(contentsOfFile:self.URL.absoluteString!).CGImageletwidth=CGImageGetWidth(image)/2.0letheight=CGImageGetHeight(image)/2.0letbitsPerComponent=CGImageGetBitsPerComponent(image)letbytesPerRow=CGImageGetBytesPerRow(image)letcolorSpace=CGImageGetColorSpace(image)letbitmapInfo=CGImageGetBitmapInfo(image)letcontext=CGBitmapContextCreate(nil,width,height,bitsPerComponent,bytesPerRow,colorSpace,bitmapInfo)CGContextSetInterpolationQuality(context,kCGInterpolationHigh)CGContextDrawImage(context,CGRect(origin:CGPointZero,size:CGSize(width:CGFloat(width),height:CGFloat(height))),image)letscaledImage=UIImage(CGImage:CGBitmapContextCreateImage(context))

CGBitmapContextCreate takes several arguments to construct a context with desired dimensions and amount of memory for each channel within a given colorspace. In the example, these values are fetched from the CGImage. Next, CGContextSetInterpolationQuality allows for the context to interpolate pixels at various levels of fidelity. In this case, kCGInterpolationHigh is passed for best results. CGContextDrawImage allows for the image to be drawn at a given size and position, allowing for the image to be cropped on a particular edge or to fit a set of image features, such as faces. Finally, CGBitmapContextCreateImage creates a CGImage from the context.

CGImageSourceCreateThumbnailAtIndex

Image I/O is a powerful, yet lesser-known framework for working with images. Independent of Core Graphics, it can read and write between between many different formats, access photo metadata, and perform common image processing operations. The framework offers the fastest image encoders and decoders on the platform, with advanced caching mechanisms and even the ability to load images incrementally.

CGImageSourceCreateThumbnailAtIndex offers a concise API with different options than found in equivalent Core Graphics calls:

importImageIOifletimageSource=CGImageSourceCreateWithURL(self.URL,nil){letoptions=[kCGImageSourceThumbnailMaxPixelSize:max(size.width,size.height)/2.0,kCGImageSourceCreateThumbnailFromImageIfAbsent:true]letscaledImage=UIImage(CGImage:CGImageSourceCreateThumbnailAtIndex(imageSource,0,options))}

Given a CGImageSource and set of options, CGImageSourceCreateThumbnailAtIndex creates a thumbnail image. Resizing is accomplished by the kCGImageSourceThumbnailMaxPixelSize. Specifying the maximum dimension divided by a constant factor scales the image while maintaining the original aspect ratio. By specifying either kCGImageSourceCreateThumbnailFromImageIfAbsent or kCGImageSourceCreateThumbnailFromImageAlways, Image I/O will automatically cache the scaled result for subsequent calls.

Lanczos Resampling with Core Image

Core Image provides a built-in Lanczos resampling functionality with the CILanczosScaleTransform filter. Although arguably a higher-level API than UIKit, the pervasive use of key-value coding in Core Image makes it unwieldy.

That said, at least the pattern is consistent. The process of creating a transform filter, configuring it, and rendering an output image is just like any other Core Image workflow:

letimage=CIImage(contentsOfURL:self.URL)letfilter=CIFilter(name:"CILanczosScaleTransform")filter.setValue(image,forKey:"inputImage")filter.setValue(0.5,forKey:"inputScale")filter.setValue(1.0,forKey:"inputAspectRatio")letoutputImage=filter.valueForKey("outputImage")asCIImageletcontext=CIContext(options:nil)letscaledImage=UIImage(CGImage:self.context.createCGImage(outputImage,fromRect:outputImage.extent()))

CILanczosScaleTransform accepts an inputImage, inputScale, and inputAspectRatio, all of which are pretty self-explanatory. A CIContext is used to create a UIImage by way of a CGImageRef intermediary representation, since UIImage(CIImage:) doesn't often work as expected.


Performance Benchmarks

So how do these various approaches stack up to one another?

Here are the results of a set of performance benchmarks done on an iPod Touch (5th Generation) running iOS 8.0 GM, using XCTestCase.measureBlock():

JPEG

Scaling a large, high-resolution (12000 ⨉ 12000 px 20 MB JPEG) source image from NASA Visible Earth to 1/10th the size:

OperationTime (sec)σ
UIKit0.00222%
Core Graphics10.0069%
Image I/O20.001121%
Core Image3, 40.0117%

PNG

Scaling a reasonably large (1024 ⨉ 1024 px 1MB PNG) rendering of the Postgres.app Icon to 1/10th the size:

OperationTime (sec)σ
UIKit0.00125%
Core Graphics50.00512%
Image I/O60.00182%
Core Image70.23443%

1, 5 Results were consistent across different values of CGInterpolationQuality, with negligible differences in performance benchmarks.

2 The high standard deviation reflects the cost of creating the cached thumbnail, which was comparable to the performance of the equivalent Core Graphics function.

3 Creating a CIContext is an extremely expensive operation, and accounts for most of the time spent in the benchmark. Using a cached instance reduced average runtime down to metrics comparable with UIGraphicsBeginImageContextWithOptions.

4, 7 Setting kCIContextUseSoftwareRenderer to true on the options passed on CIContext creation yielded results an order of magnitude slower than base results.

Conclusions

  • UIKit, Core Graphics, and Image I/O all perform well for scaling operations on most images.
  • Core Image is outperformed for image scaling operations. In fact, it is specifically recommended in the Performance Best Practices section of the Core Image Programming Guide to use Core Graphics or Image I/O functions to crop or downsample images beforehand.
  • For general image scaling without any additional functionality, UIGraphicsBeginImageContextWithOptions is probably the best option.
  • If image quality is a consideration, consider using CGBitmapContextCreate in combination with CGContextSetInterpolationQuality.
  • When scaling images with the intent purpose of displaying thumbnails, CGImageSourceCreateThumbnailAtIndex offers a compelling solution for both rendering and caching.

NSMutableHipster

This is a good opportunity to remind readers that NSHipster articles are published on GitHub. If you have any corrections or additional insights to offer, please open an issue or submit a pull request.

PHImageManager

$
0
0

Yesterday's article described various techniques for resizing images using APIs from the UIKit, Core Graphics, Core Image, and Image I/O frameworks. However, that article failed to mention some rather extraordinary functionality baked into the new Photos framework which takes care of all of this for you.

For anyone developing apps that manage photos or videos, meet your new best friend: PHImageManager.


New in iOS 8, the Photos framework is something of a triumph for the platform. Photography is one of the key verticals for the iPhone: in addition to being the most popular cameras in the world, photo & video apps are regularly featured on the App Store. This framework goes a long way to empower apps to do even more, with a shared set of tools and primitives.

A great example of this is PHImageManager, which acts as a centralized coordinator for image assets. Previously, each app was responsible for creating and caching their own image thumbnails. In addition to requiring extra work on the part of developers, redundant image caches could potentially add up to gigabytes of data across the system. But with PHImageManager, apps don't have to worry about resizing or caching logistics, and can instead focus on building out features.

Requesting Asset Images

PHImageManager provides several APIs for asynchronously fetching image and video data for assets. For a given asset, a PHImageManager can request an image at a particular size and content mode, with a high degree of configurability in terms of quality and delivery options.

But first, here's a simple example of how a table view might asynchronously load cell images with asset thumbnails:

varassets:[PHAsset]varimageRequests:[NSIndexPath:PHImageRequestID]functableView(tableView:UITableView,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{letcell=tableView.dequeueReusableCellWithIdentifier("Cell",forIndexPath:indexPath)asUITableViewCellletmanager=PHImageManager.defaultManager()ifletrequest=imageRequests[indexPath]{manager.cancelImageRequest(request)}letasset=assets[indexPath.row]cell.textLabel?.text=NSDateFormatter.localizedStringFromDate(asset.creationDate,dateStyle:.MediumStyle,timeStyle:.MediumStyle)imageRequests[indexPath]=manager.requestImageForAsset(asset,targetSize:CGSize(width:100.0,height:100.0),contentMode:.AspectFill,options:nil){(result,_)incell.imageView?.image=result}returncell}

API usage is pretty straightforward: the defaultManager asynchronously requests an image for the asset corresponding to the cell at a particular index path, and the cell image view is set whenever the result comes back. The one tricky part is using imageRequests to keep track of image requests, in order to cancel any pending requests when a cell is reused.

Batch Pre-Caching Asset Images

If there's reasonable assurance that most of a set of assets will be viewed at some point, it may make sense to pre-cache those images. PHCachingImageManager is a subclass of PHImageManager designed to do just that.

For example, here's how the results of a PHAsset fetch operation can be pre-cached in order to optimize image availability:

letcachingImageManager=PHCachingImageManager()letoptions=PHFetchOptions()options.predicate=NSPredicate(format:"favorite == YES")options.sortDescriptors=[NSSortDescriptor(key:"creationDate",ascending:true)]ifletresults=PHAsset.fetchAssetsWithMediaType(.Image,options:options){varassets:[PHAsset]=[]results.enumerateObjectsUsingBlock{(object,idx,_)inifletasset=objectas?PHAsset{assets.append(asset)}}cachingImageManager.startCachingImagesForAssets(assets,targetSize:PHImageManagerMaximumSize,contentMode:.AspectFit,options:nil)}

Alternatively, Swift willSet / didSet hooks offer a convenient way to automatically start pre-caching assets as they are loaded:

letcachingImageManager=PHCachingImageManager()varassets:[PHAsset]=[]{willSet{cachingImageManager.stopCachingImagesForAllAssets()}didSet{cachingImageManager.startCachingImagesForAssets(self.assets,targetSize:PHImageManagerMaximumSize,contentMode:.AspectFit,options:nil)}}

PHImageRequestOptions

In the previous examples, the options parameter of requestImageForAsset()& startCachingImagesForAssets() have been set to nil. Passing an instance of PHImageRequestOptions allows for fine-grained control over what gets loaded and how.

PHImageRequestOptions has the following properties:

  • deliveryModePHImageRequestOptionsDeliveryMode: (Described Below)
  • networkAccessAllowedBool: Will download the image from iCloud, if necessary.
  • normalizedCropRectCGRect: Specify a crop rectangle in unit coordinates of the original image.
  • progressHandler: Provide caller a way to be told how much progress has been made prior to delivering the data when it comes from iCloud. Defaults to nil, shall be set by caller
  • resizeModePHImageRequestOptionsResizeMode: .None, .Fast, or .Exact. Does not apply when size is PHImageManagerMaximumSize.
  • synchronousBool: Return only a single result, blocking until available (or failure). Defaults to NO
  • versionPHImageRequestOptionsVersion: .Current, .Unadjusted, or .Original

Several of these properties take a specific enum type, which are all pretty self explanatory, save for PHImageRequestOptionsDeliveryMode, which encapsulates some pretty complex behavior:

PHImageRequestOptionsDeliveryMode

  • .Opportunistic: Photos automatically provides one or more results in order to balance image quality and responsiveness. Photos may call the resultHandler block method more than once, such as to provide a low-quality image suitable for displaying temporarily while it prepares a high-quality image. If the image manager has already cached the requested image, Photos calls your result handler only once. This option is not available if the synchronous property is false.
  • .HighQualityFormat: Photos provides only the highest-quality image available, regardless of how much time it takes to load. If the synchronous property is true or if using the requestImageDataForAsset:options:resultHandler: method, this behavior is the default and only option.
  • .FastFormat: Photos provides only a fast-loading image, possibly sacrificing image quality. If a high-quality image cannot be loaded quickly, the result handler provides a low-quality image. Check the PHImageResultIsDegradedKey key in the info dictionary to determine the quality of image provided to the result handler.

Cropping Asset To Detected Faces Using 2-Phase Image Request

Using PHImageManager and PHImageRequestOptions to their full capacity allows for rather sophisticated functionality. One could, for example, use successive image requests to crop full-quality assets to any faces detected in the image.

letasset:PHAsset@IBOutletweakvarimageView:UIImageView!@IBOutletweakvarprogressView:UIProgressView!overridefuncviewDidLoad(){super.viewDidLoad()letmanager=PHImageManager.defaultManager()letinitialRequestOptions=PHImageRequestOptions()initialRequestOptions.synchronous=trueinitialRequestOptions.resizeMode=.FastinitialRequestOptions.deliveryMode=.FastFormatmanager.requestImageForAsset(asset,targetSize:CGSize(width:250.0,height:250.0),contentMode:.AspectFit,options:initialRequestOptions){(initialResult,_)inletfinalRequestOptions=PHImageRequestOptions()finalRequestOptions.progressHandler={(progress,_,_,_)inself.progressView.progress=Float(progress)}letdetector=CIDetector(ofType:CIDetectorTypeFace,context:nil,options:[CIDetectorAccuracy:CIDetectorAccuracyLow])letfeatures=detector.featuresInImage(CIImage(CGImage:initialResult.CGImage))as[CIFeature]iffeatures.count>0{varrect=CGRectZeroforfeatureinfeatures{rect=CGRectUnion(rect,feature.bounds)}finalRequestOptions.normalizedCropRect=CGRectApplyAffineTransform(rect,CGAffineTransformMakeScale(1.0/initialResult.size.width,1.0/initialResult.size.height))}manager.requestImageForAsset(asset,targetSize:PHImageManagerMaximumSize,contentMode:.AspectFit,options:finalRequestOptions){(finalResult,_)inself.imageView.image=finalResult}}}

The initial request attempts to get the most readily available representation of an asset to pass into a CIDetector for facial recognition. If any features were detected, the final request would be cropped to them, by specifying the normalizedCropRect on the final PHImageRequestOptions.

normalizedCropRect is normalized for origin and size components within the inclusive range 0.0 to 1.0. An affine transformation scaling on the inverse of the original frame makes for an easy calculation.


From its very inception, iOS has been a balancing act between functionality and integrity. And with every release, a combination of thoughtful affordances and powerful APIs have managed to expand the role third-party applications without compromising security or performance.

By unifying functionality for fetching, managing, and manipulating photos, the Photos framework will dramatically raise the standards for existing apps, while simultaneously lowering the bar for developing apps, and is a stunning example of why developers tend to prefer iOS as a platform.

Viewing all 379 articles
Browse latest View live