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

NSScanner

$
0
0

Strings are a ubiquitous and diverse part of our computing lives. They comprise emails and essays, poems and novels—and indeed, every article on nshipster.com, the configuration files that shape the site, and the code that builds it.

Being able to pull apart strings and extract particular bits of data is therefore a powerful skill, one that we use over and over building apps and shaping our tools. Cocoa provides a powerful set of tools to handle string processing. In particular:

  • string.componentsSeparatedByCharactersInSet / string.componentsSeparatedByString: Great for splitting a string into constituent pieces. Not so great at anything else.

  • NSRegularExpression: Powerful for validating and extracting string data from an expected format. Cumbersome when dealing with complex serial input and finicky for parsing numeric values.

  • NSDataDetector: Perfect for detecting and extracting dates, addresses, links, and more. Limited to its predefined types.

  • NSScanner: Highly configurable and designed for scanning string and numeric values from loosely demarcated strings.

This week's article focuses on the last of these, NSScanner. Read on to learn about its flexibility and power.


Among Cocoa's tools, NSScanner serves as a wrapper around a string, scanning through its contents to efficiently retrieve substrings and numeric values. It offers several properties that modify an NSScanner instance's behavior:

  • caseSensitiveBool: Whether to pay attention to the upper- or lower-case while scanning. Note that this property only applies to the string-matching methods scanString:intoString: and scanUpToString:intoString:—character sets scanning is always case-sensitive.
  • charactersToBeSkippedNSCharacterSet: The characters to skip over on the way to finding a match for the requested value type.
  • scanLocationInt: The current position of the scanner in its string. Scanning can be rewound or restarted by setting this property.
  • localeNSLocale: The locale that the scanner should use when parsing numeric values (see below).

An NSScanner instance has two additional read-only properties: string, which gives you back the string the scanner is scanning; and atEnd, which is true if scanLocation is at the end of the string.

Note:NSScanner is actually the abstract superclass of a private cluster of scanner implementation classes. Even though you're calling alloc and init on NSScanner, you'll actually receive one of these subclasses, such as NSConcreteScanner. No need to fret over this.

Extracting Substring and Numeric Values

The raison d'être of NSScanner is to pull substring and numeric values from a larger string. It has fifteen methods to do this, all of which follow the same basic pattern. Each method takes a reference to an output variable as a paramter and returns a boolean value indicating success or failure of the scan:

letstringScanner=NSScanner(string:"John & Paul & Ringo & George.")stringScanner.charactersToBeSkipped=whitespaceAndPunctuationSetvarname:NSString?whilestringScanner.scanUpToCharactersFromSet(whitespaceAndPunctuationSet,intoString:&name),letname=name{println(name)}// John// Paul// Ringo// George
NSScanner*stringScanner=[[NSScanneralloc]initWithString:@"John & Paul & Ringo & George."];stringScanner.charactersToBeSkipped=whitespaceAndPunctuationSet;NSString*name;while([stringScannerscanUpToCharactersFromSet:whitespaceAndPunctuationSetintoString:&name]){NSLog(@"%@",name);}// John// Paul// Ringo// George

There are two

1) String Scanners

scanString:intoString: / scanCharactersFromSet:intoString:
Scans to match the string parameter or characters in the NSCharacterSet parameter, respectively. The intoString parameter will return containing the scanned string, if found. These methods are often used to advance the scanner's location—pass nil for the intoString parameter to ignore the output.
scanUpToString:intoString: / scanUpToCharactersFromSet:intoString:
Scans characters into a string until finding the string parameter or characters in the NSCharacterSet parameter, respectively. The intoString parameter will return containing the scanned string, if any was found. If the given string or character set are not found, the result will be the entire rest of the scanner's string.

2) Numeric Scanners

scanDouble: / scanFloat: / scanDecimal:
Scans a floating-point value from the scanner's string and returns the value in the referenced Double, Float, or NSDecimal instance, if found.
scanInteger: / scanInt: / scanLongLong: / scanUnsignedLongLong:
Scans an integer value from the scanner's string and returns the value in the referenced Int, Int32, Int64, or UInt64 instance, if found.
scanHexDouble: / scanHexFloat:
Scans a hexadecimal floating-point value from the scanner's string and returns the value in the referenced Double or Float instance, if found. To scan properly, the floating-point value must have a 0x or 0X prefix.
scanHexInt: / scanHexLongLong:
Scans a hexadecimal integer value from the scanner's string and returns the value in the referenced UInt32 or UInt64 instance, if found. The value may have a 0x or 0X prefix, but it is not required.

localizedScannerWithString / locale

Because it is a part of Cocoa, NSScanner has built-in localization support (of course). An NSScanner instance can work with either the user's locale when created via + localizedScannerWithString:, or a specific locale after setting its locale property. In particular, the separator for floating-point values will be correctly interpreted based on the given locale:

varprice=0.0letgasPriceScanner=NSScanner(string:"2.09 per gallon")gasPriceScanner.scanDouble(&price)// 2.09// use a german locale instead of the defaultletbenzinPriceScanner=NSScanner(string:"1,38 pro Liter")benzinPriceScanner.locale=NSLocale(localeIdentifier:"de-DE")benzinPriceScanner.scanDouble(&price)// 1.38
doubleprice;NSScanner*gasPriceScanner=[[NSScanneralloc]initWithString:@"2.09 per gallon"];[gasPriceScannerscanDouble:&price];// 2.09// use a german locale instead of the defaultNSScanner*benzinPriceScanner=[[NSScanneralloc]initWithString:@"1,38 pro Liter"];[benzinPriceScannersetLocale:[NSLocalelocaleWithLocaleIdentifier:@"de-DE"]];[benzinPriceScannerscanDouble:&price];// 1.38

Example: Parsing SVG Path Data

To take NSScanner out for a spin, we'll look at parsing the path data from an SVG path. SVG path data are stored as a string of instructions for drawing the path, where "M" indicates a "move-to" step, "L" stands for "line-to", and "C" stands for a curve. Uppercase instructions are followed by points in absolute coordinates; lowercase instructions are followed by coordinates relative to the last point in the path.

Here's an SVG path I happen to have lying around:

varsvgPathData="M28.2,971.4c-10,0.5-19.1,13.3-28.2,2.1c0,15.1,23.7,30.5,39.8,16.3c16,14.1,39.8-1.3,39.8-16.3c-12.5,15.4-25-14.4-39.8,4.5C35.8,972.7,31.9,971.2,28.2,971.4z"

Note that the point data are fairly irregular. Sometimes the x and y values of a point are separated by a comma, sometimes not, and likewise with points themselves. Parsing these data with regular expressions could turn into a mess pretty quickly, but with NSScanner the code is clear and straightforward.

We'll define a bezierPathFromData function that will convert a string of path data into an UIBezierPath. Our scanner is set up to skip commas and whitespace while scanning for values:

funcbezierPathFromData(str:String)->UIBezierPath{letscanner=NSScanner(string:str)// skip commas and whitespaceletskipChars=NSMutableCharacterSet(charactersInString:",")skipChars.formUnionWithCharacterSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())scanner.charactersToBeSkipped=skipChars// the resulting bezier pathvarpath=UIBezierPath()

With the setup out of the way, it's time to start scanning. We start by scanning for a string made up characters in the allowed set of instructions:

// instructions code can be upper- or lower-caseletinstructionSet=NSCharacterSet(charactersInString:"MCSQTAmcsqta")varinstruction:NSString?// scan for an instruction codewhilescanner.scanCharactersFromSet(instructionSet,intoString:&instruction){

The next section scans for two Double values in a row, converts them to a CGPoint, and then ultimately adds the correct step to the bezier path:

varx=0.0,y=0.0varpoints:[CGPoint]=[]// scan for pairs of Double, adding them as CGPoints to the points arraywhilescanner.scanDouble(&x)&&scanner.scanDouble(&y){points.append(CGPoint(x:x,y:y))}// new point for bezier pathswitchinstruction??""{case"M":path.moveToPoint(points[0])case"C":path.addCurveToPoint(points[2],controlPoint1:points[0],controlPoint2:points[1])case"c":path.addCurveToPoint(path.currentPoint.offset(points[2]),controlPoint1:path.currentPoint.offset(points[0]),controlPoint2:path.currentPoint.offset(points[1]))default:break}}returnpath}

Lo and behold, the result:

NSMustacheScanner

The required flipping, resizing, waxing, and twirling are left as an exercise for the reader.


Swift-Friendly Scanning

As a last note, working with NSScanner in Swift can feel almost silly. Really, NSScanner, I need to pass in a pointer just so you can return a Bool? I can't use optionals, which are pretty much designed for this exact purpose? Really?

With a simple extension converting the built-in methods to ones returning optional values, scanning becomes far more in sync with Swift's idiom. Our path data scanning example can now use optional binding instead of inout variables for a cleaner, easier-to-read implementation:

// look for an instruction codewhileletinstruction=scanner.scanCharactersFromSet(instructionSet){varpoints:[CGPoint]=[]// scan for pairs of Double, adding them as CGPoints to the points arraywhileletx=scanner.scanDouble(),y=scanner.scanDouble(){points.append(CGPoint(x:x,y:y))}// new point for bezier pathswitchinstruction{// ...}}

You've gotta have the right tools for every job. NSScanner can be the shiny tool to reach for when it's time to parse a user's input or a web service's data. Being able to distinguish which tools are right for which tasks helps us on our way to creating clear and accurate code.


NSCalendar Additions

$
0
0

Dates. More than any other data type, the gulf between the initial banality of dates and their true, multifaceted complexity looms terrifyingly large. Combining sub-second precision, overlapping units, geopolitical time zone boundaries, localization differences in both language and grammar, and Daylight Saving shifts and leap year adjustments that literally add and remove whole chunks of time from measured existence, there's a lot to process.

To embark on any date-heavy task, then, requires a solid understanding of the tools already at your fingertips. Better to use a Foundation method than to write the n-thousandth version of dateIsTomorrow. Are you using NSDateComponents? Did you specify all the right calendar units? Will your code still work correctly on February 28, 2100?

But here's the thing: the APIs you're already using have been holding out on you. Unless you're digging through release notes and API diffs, you wouldn't know that over the last few releases of OS X, NSCalendar has quietly built a powerful set of methods for accessing and manipulating dates, and that the latest release brought them all to iOS.

letcalendar=NSCalendar.currentCalendar()
NSCalendar*calendar=[NSCalendarcurrentCalendar];

From new ways of accessing individual date components and flexibly comparing dates to powerful date interpolation and enumeration methods, there's far too much to ignore. Make some room in your calendar and read on for more.

Convenient Component Access

Oh, NSDateComponents. So practical and flexible, yet so cumbersome when I just. Want to know. What the hour is. NSCalendar to the rescue!

lethour=calendar.component(.CalendarUnitHour,fromDate:NSDate())
NSIntegerhour=[calendarcomponent:NSCalendarUnitHourfromDate:[NSDatedate]];

That's much better. NSCalendar, what else can you do?

  • getEra(_:year:month:day:fromDate:): Returns the era, year, month, and day of the given date by reference. Pass nil/NULL for any parameters you don't need.
  • getEra(_:yearForWeekOfYear:weekOfYear:weekday:fromDate:): Returns the era, year (for week of year), week of year, and weekday of the given date by reference. Pass nil/NULL for any parameters you don't need.
  • getHour(_:minute:second:nanosecond:fromDate:): Returns time information for the given date by reference. nil/NULL, you get the idea.

And just kidding, NSDateComponents, I take it all back. There are a couple methods for you, too:

  • componentsInTimeZone(_:fromDate:): Returns an NSDateComponents instance with components of the given date shifted to the given time zone.
  • components(_:fromDateComponents:toDateComponents:options:): Returns the difference between two NSDateComponents instances. The method will use base values for any components that are not set, so provide at the least the year for each parameter. The options parameter is unused; pass nil/0.

Date Comparison

While direct NSDate comparison has always been a simple matter, more meaningful comparisons can get surprisingly complex. Do two NSDate instances fall on the same day? In the same hour? In the same week?

Fret no more, NSCalendar has you covered with an extensive set of comparison methods:

  • isDateInToday(_:): Returns true if the given date is today.
  • isDateInTomorrow(_:): Returns true if the given date is tomorrow.
  • isDateInYesterday(_:): Returns true if the given date is a part of yesterday.
  • isDateInWeekend(_:): Returns true if the given date is part of a weekend, as defined by the calendar.
  • isDate(_:inSameDayAsDate:): Returns true if the two NSDate instances are on the same day—delving into date components is unnecessary.
  • isDate(_:equalToDate:toUnitGranularity:): Returns true if the dates are identical down to the given unit of granularity. That is, two date instances in the same week would return true if used with calendar.isDate(tuesday, equalToDate: thursday, toUnitGranularity: .CalendarUnitWeekOfYear), even if they fall in different months.
  • compareDate(_:toDate:toUnitGranularity:): Returns an NSComparisonResult, treating as equal any dates that are identical down to the given unit of granularity.
  • date(_:matchesComponents:): Returns true if a date matches the specific components given.

Date Interpolation

Next up is a set of methods that allows you to find the next date(s) based on a starting point. You can find the next (or previous) date based on an NSDateComponents instance, an individual date component, or a specific hour, minute, and second. Each of these methods takes an NSCalendarOptions bitmask parameter that provides fine-grained control over how the next date is selected, particularly in cases where an exact match isn't found at first.

NSCalendarOptions

The easiest option of NSCalendarOptions is .SearchBackwards, which reverses the direction of each search, for all methods. Backward searches are constructed to return dates similar to forward searches. For example, searching backwards for the previous date with an hour of 11 would give you 11:00, not 11:59, even though 11:59 would technically come "before" 11:00 in a backwards search. Indeed, backward searching is intuitive until you think about it and then unintuitive until you think about it a lot more. That .SearchBackwards is the easy part should give you some idea of what's ahead.

The remainder of the options in NSCalendarOptions help deal with "missing" time instances. Time can be missing most obviously if one searches in the short window when time leaps an hour forward during a Daylight Saving adjustment, but this behavior can also come into play when searching for dates that don't quite add up, such as the 31st of February or April.

When encountering missing time, if NSCalendarOptions.MatchStrictly is provided, the methods will continue searching to find an exact match for all components given, even if that means skipping past higher order components. Without strict matching invoked, one of .MatchNextTime, .MatchNextTimePreservingSmallerUnits, and .MatchPreviousTimePreservingSmallerUnits must be provided. These options determine how a missing instance of time will be adjusted to compensate for the components in your request.

In this case, an example will be worth a thousand words:

// begin with Valentine's Day, 2015 at 9:00amletvalentines=cal.dateWithEra(1,year:2015,month:2,day:14,hour:9,minute:0,second:0,nanosecond:0)!// to find the last day of the month, we'll set up a date components instance with // `day` set to 31:letcomponents=NSDateComponents()components.day=31
NSDate*valentines=[calendardateWithEra:1year:2015month:2day:14hour:9minute:0second:0nanosecond:0];NSDateComponents*components=[[NSDateComponentsalloc]init];components.day=31;

Using strict matching will find the next day that matches 31, skipping into March to do so:

calendar.nextDateAfterDate(valentines,matchingComponents:components,options:.MatchStrictly)// Mar 31, 2015, 12:00 AM
NSDate*date=[calendarnextDateAfterDate:valentinesmatchingComponents:componentsoptions:NSCalendarMatchStrictly];// Mar 31, 2015, 12:00 AM

Without strict matching, nextDateAfterDate will stop when it hits the end of February before finding a match—recall that the highest unit specified was the day, so the search will only continue within the next highest unit, the month. At that point, the option you've provided will determine the returned date. For example, using .MatchNextTime will pick the next possible day:

calendar.nextDateAfterDate(valentines,matchingComponents:components,options:.MatchNextTime)// Mar 1, 2015, 12:00 AM
date=[calendarnextDateAfterDate:valentinesmatchingComponents:componentsoptions:NSCalendarMatchNextTime];// Mar 1, 2015, 12:00 AM

Similarly, using .MatchNextTimePreservingSmallerUnits will pick the next day, but will also preserve all the units smaller than the given NSCalendarUnitDay:

calendar.nextDateAfterDate(valentines,matchingComponents:components,options:.MatchNextTimePreservingSmallerUnits)// Mar 1, 2015, 9:00 AM
date=[calendarnextDateAfterDate:valentinesmatchingComponents:componentsoptions:NSCalendarMatchNextTimePreservingSmallerUnits];// Mar 1, 2015, 9:00 AM

And finally, using .MatchPreviousTimePreservingSmallerUnits will resolve the missing date by going the other direction, choosing the first possible previous day, again preserving the smaller units:

calendar.nextDateAfterDate(valentines,matchingComponents:components,options:.MatchPreviousTimePreservingSmallerUnits)// Feb 28, 2015, 9:00 AM
date=[calendarnextDateAfterDate:valentinesmatchingComponents:componentsoptions:NSCalendarMatchPreviousTimePreservingSmallerUnits];// Feb 28, 2015, 9:00 AM

Besides the NDateComponents version shown here, it's worth noting that nextDateAfterDate has two other variations:

// matching a particular calendar unitcal.nextDateAfterDate(valentines,matchingUnit:.CalendarUnitDay,value:31,options:.MatchStrictly)// March 31, 2015, 12:00 AM// matching an hour, minute, and secondcal.nextDateAfterDate(valentines,matchingHour:15,minute:30,second:0,options:.MatchNextTime)// Feb 14, 2015, 3:30 PM
// matching a particular calendar unitdate=[calendarnextDateAfterDate:valentinesmatchingUnit:NSCalendarUnitDayvalue:31options:NSCalendarMatchStrictly];// March 31, 2015, 12:00 AM// matching an hour, minute, and seconddate=[calendarnextDateAfterDate:valentinesmatchingHour:15minute:30second:0options:NSCalendarMatchNextTime];// Feb 14, 2015, 3:30 PM

Enumerating Interpolated Dates

Rather than using nextDateAfterDate iteratively, NSCalendar provides an API for enumerating dates with the same semantics. enumerateDatesStartingAfterDate(_:matchingComponents:options:usingBlock:) computes the dates that match the given set of components and options, calling the provided closure with each date in turn. The closure can set the stop parameter to true, thereby stopping the enumeration.

Putting this new NSCalendarOptions knowledge to use, here's one way to list the last fifty leap days:

letleapYearComponents=NSDateComponents()leapYearComponents.month=2leapYearComponents.day=29vardateCount=0cal.enumerateDatesStartingAfterDate(NSDate(),matchingComponents:leapYearComponents,options:.MatchStrictly|.SearchBackwards){(date:NSDate!,exactMatch:Bool,stop:UnsafeMutablePointer<ObjCBool>)inprintln(date)if++dateCount==50{// .memory gets at the value of an UnsafeMutablePointerstop.memory=true}}// 2012-02-29 05:00:00 +0000// 2008-02-29 05:00:00 +0000// 2004-02-29 05:00:00 +0000// 2000-02-29 05:00:00 +0000// ...
NSDateComponents*leapYearComponents=[[NSDateComponentsalloc]init];leapYearComponents.month=2;leapYearComponents.day=29;__blockintdateCount=0;[calendarenumerateDatesStartingAfterDate:[NSDatedate]matchingComponents:leapYearComponentsoptions:NSCalendarMatchStrictly|NSCalendarSearchBackwardsusingBlock:^(NSDate*date,BOOLexactMatch,BOOL*stop){NSLog(@"%@",date);if(++dateCount==50){*stop=YES;}}];// 2012-02-29 05:00:00 +0000// 2008-02-29 05:00:00 +0000// 2004-02-29 05:00:00 +0000// 2000-02-29 05:00:00 +0000// ...

Working for the Weekend

If you're always looking forward to the weekend, look no further than our final two NSCalendar methods:

  • nextWeekendStartDate(_:interval:options:afterDate): Returns the starting date and length of the next weekend by reference via the first two parameters. This method will return false if the current calendar or locale doesn't support weekends. The only relevant option here is .SearchBackwards. (See below for an example.)
  • rangeOfWeekendStartDate(_:interval:containingDate): Returns the starting date and length of the weekend containing the given date by reference via the first two parameters. This method returns false if the given date is not in fact on a weekend or if the current calendar or locale doesn't support weekends.

Localized Calendar Symbols

As if all that new functionality wasn't enough, NSCalendar also provides access to a full set of properly localized calendar symbols, making possible quick access to the names of months, days of the week, and more. Each group of symbols is further enumerated along two axes: (1) the length of the symbol and (2) its use as a standalone noun or as part of a date.

Understanding this second attribute is extremely important for localization, since some languages, Slavic languages in particular, use different noun cases for different contexts. For example, a calendar would need to use one of the standaloneMonthSymbols variants for its headers, not the monthSymbols that are used for formatting specific dates.

For your perusal, here's a table of all the symbols that are available in NSCalendar—note the different values for standalone symbols in the Russian column:

 en_USru_RU
monthSymbolsJanuary, February, March…января, февраля, марта…
shortMonthSymbolsJan, Feb, Mar…янв., февр., марта…
veryShortMonthSymbolsJ, F, M, A…Я, Ф, М, А…
standaloneMonthSymbolsJanuary, February, March…Январь, Февраль, Март…
shortStandaloneMonthSymbolsJan, Feb, Mar…Янв., Февр., Март…
veryShortStandaloneMonthSymbolsJ, F, M, A…Я, Ф, М, А…
weekdaySymbolsSunday, Monday, Tuesday, Wednesday…воскресенье, понедельник, вторник, среда…
shortWeekdaySymbolsSun, Mon, Tue, Wed…вс, пн, вт, ср…
veryShortWeekdaySymbolsS, M, T, W…вс, пн, вт, ср…
standaloneWeekdaySymbolsSunday, Monday, Tuesday, Wednesday…Воскресенье, Понедельник, Вторник, Среда…
shortStandaloneWeekdaySymbolsSun, Mon, Tue, Wed…Вс, Пн, Вт, Ср…
veryShortStandaloneWeekdaySymbolsS, M, T, W…В, П, В, С…
AMSymbolAMAM
PMSymbolPMPM
quarterSymbols1st quarter, 2nd quarter, 3rd quarter, 4th quarter1-й квартал, 2-й квартал, 3-й квартал, 4-й квартал
shortQuarterSymbolsQ1, Q2, Q3, Q41-й кв., 2-й кв., 3-й кв., 4-й кв.
standaloneQuarterSymbols1st quarter, 2nd quarter, 3rd quarter, 4th quarter1-й квартал, 2-й квартал, 3-й квартал, 4-й квартал
shortStandaloneQuarterSymbolsQ1, Q2, Q3, Q41-й кв., 2-й кв., 3-й кв., 4-й кв.
eraSymbolsBC, ADдо н. э., н. э.
longEraSymbolsBefore Christ, Anno Dominiдо н.э., н.э.

Note: These same collections are also available via NSDateFormatter.

Your Weekly Swiftification

It's becoming something of a feature here at NSHipster to close with a slightly Swift-ified version of the discussed API. Even in this brand-new set of NSCalendar APIs, there are some sharp edges to be rounded off, replacing UnsafeMutablePointer parameters with more idiomatic tuple return values.

With a useful set of NSCalendar extensions (gist here), the component accessing and weekend finding methods can be used without in-out variables. For example, getting individual date components from a date is much simpler:

// built-invarhour=0varminute=0calendar.getHour(&hour,minute:&minute,second:nil,nanosecond:nil,fromDate:NSDate())// swiftifiedlet(hour,minute,_,_)=calendar.getTimeFromDate(NSDate())

As is fetching the range of the next weekend:

// built-invarstartDate:NSDate?varinterval:NSTimeInterval=0letsuccess=cal.nextWeekendStartDate(&startDate,interval:&interval,options:nil,afterDate:NSDate())ifsuccess,letstartDate=startDate{println("start: \(startDate), interval: \(interval)")}// swiftifiedifletnextWeekend=cal.nextWeekendAfterDate(NSDate()){println("start: \(nextWeekend.startDate), interval: \(nextWeekend.interval)")}

So take that, complicated calendrical math. With these new additions to NSCalendar, you'll have your problems sorted out in no time.

Quick Look Debugging

$
0
0

Debugging can be an exercise in irony. We create programs that tell our pint-sized supercomputers to complete infinitely varied and incalculable tasks on our behalf, yet when trying to understand those same programs, we tell the computers to wait for us.

For example, suppose I'm trying to figure out why the UINavigationBar in my app doesn't appear as I expected. To investigate, I might use the debugger to look at the UIColor instance I'm setting on the navigation bar—what color is this, exactly?

UIColor in Debug

Hold on! No more trying to figure out how those components add together. There's a better way.

Since version 5, Xcode has shipped with Quick Look display in the debugger. Just as you can inspect the contents of a file on the Desktop with a quick tap of the space bar, in Xcode you can use Quick Look to see a visual representation of a variety of datatypes. Tapping the space bar on our color variable gives an instant answer—no off-the-top-of-your-head RGB calculations required:

UIColor Quick Look


You can also invoke Quick Look while debugging directly from your code. Consider the following method, buildPathWithRadius(_:steps:loopCount:). It creates a UIBezierPath of some kind, but you've forgotten which, and does this code even work?

funcbuildPathWithRadius(radius:CGFloat,steps:CGFloat,loopCount:CGFloat)->UIBezierPath{letaway=radius/stepsletaround=loopCount/steps*2*CGFloat(M_PI)letpoints=map(stride(from:1,through:steps,by:1)){step->CGPointinletx=cos(step*around)*step*awaylety=sin(step*around)*step*awayreturnCGPoint(x:x,y:y)}letpath=UIBezierPath()path.moveToPoint(CGPoint.zeroPoint)forpointinpoints{path.addLineToPoint(point)}returnpath}
-(UIBezierPath*)buildPathWithRadius:(CGFloat)radiussteps:(CGFloat)stepsloopCount:(CGFloat)loopCount{CGFloatx,y;CGFloataway=radius/steps;CGFloataround=loopCount/steps*2*M_PI;UIBezierPath*path=[UIBezierPathbezierPath];[pathmoveToPoint:CGPointZero];for(inti=1;i<=steps;i++){x=cos(i*around)*i*away;y=sin(i*around)*i*away;[pathaddLineToPoint:CGPointMake(x,y)];}returnpath;}

To see the result, you could surely create a custom view for the bezier path or draw it into a UIImage. But better yet, you could insert a breakpoint at the end of the method and mouse over path:

Spiral UIBezierPath Quick Look

Spiraltastic!


Built-In Types

Quick Look can be used with most of the datatypes you'll want to visualize right out of the box. Xcode already has you covered for the following types:

  • Images:UIImage, NSImage, UIImageView, NSImageView, CIImage, and NSBitmapImageRep are all visible via Quick Look.
  • Colors:UIColor and NSColor. (Sorry, CGColor.)
  • Strings:NSString and NSAttributedString.
  • Geometry:UIBezierPath and NSBezierPath along with CGPoint, CGRect, and CGSize.
  • Locations:CLLocation gives a large, interactive view of the mapped location, with details about altitude and accuracy in an overlay.
  • URLs:NSURL is represented by a view showing the local or remote content addressed by the URL.
  • Cursors:NSCursor, for the cursored among us.
  • SpriteKit:SKSpriteNode, SKShapeNode, SKTexture, and SKTextureAtlas are all represented.
  • Data:NSData has a great view showing hex and ASCII values with their offset.
  • Views: Last but not least, any UIView subclass will display its contents in a Quick Look popup—so convenient.

What's more, these Quick Look popups often include a button that will open the content in a related application. Image data (as well as views, cursors, and SpriteKit types) offer an option to open in Preview. Remote URLs can be opened in Safari; local ones can be opened in the related application. Finally, plain-text and attributed string data can likewise be opened in TextEdit.

Custom Types

For anything beyond these built-in types, Xcode 6 has added Quick Look for custom objects. The implementation couldn't be simpler—add a single debugQuickLookObject() method to any NSObject-derived class, and you're set. debugQuickLookObject() can then return any of the built-in types described above, configured for your custom type's needs:

funcdebugQuickLookObject()->AnyObject{letpath=buildPathWithRadius(radius,steps:steps,loopCount:loopCount)returnpath}
-(id)debugQuickLookObject{UIBezierPath*path=[selfbuildPathWithRadius:self.radiussteps:self.stepsloopCount:self.loopCount];returnpath;}

In sum, Quick Look enables a more direct relationship with the data we manipulate in our code by allowing us to iterate over smaller pieces of functionality. This direct view into previously obfuscated datatypes brings some of the immediacy of a Swift Playground right into our main codebase. Displaying images? Visualizing data? Rendering text? Computers are so good at all that! Let's let them do it from now on.

Unmanaged

$
0
0

APIs do more than just exposing functionality to developers. They also communicate values about how the API should be used and why. This communication is what makes naming things one of the Hard Parts of computer science; it's what separates the good APIs from the great.

A reading of Swift's standard library shows a clear demarcation between the safety and reliability that Swift advertises on one side and the tools necessary for Objective-C interoperability on the other. Types with names like Int, String, and Array let you expect straightforward usage and unsurprising behavior, while it's impossible to create an UnsafeMutablePointer or Unmanaged instance without thinking "here be dragons."

Here we take a look at Unmanaged, a wrapper for unclearly-memory-managed objects and the hot-potatoesque way of properly handling them. But to start, let's rewind the clock a bit.

Automatic Reference Counting

Back in the Stone Age (aka 2011), reference counting in Objective-C was still a manual affair. Each reference-retaining operation needed to be balanced with a corresponding release, lest an application's memory take on an air of phantasmagoria, with zombies dangling and memory leaking... Gross. Carefully tending the reference count of one's objects was a constant chore for developers and a major hurdle for newcomers to the platform.

The advent of automatic reference counting (ARC) made all of that manual memory management unnecessary. Under ARC, the compiler inserts the retain/release/autorelease calls for you, reducing the cognitive load of applying the rules at every turn. If this graphic doesn't convince you of the boon of dropping manual memory management, nothing will:

Memory management before and after ARC

Now, in this post-ARC world, all Objective-C and Core Foundation types returned from Objective-C methods are automatically memory managed, leaving Core Foundation types returned from C functions as the lone holdout. For this last category, management of an object's ownership is still done with calls to CFRetain() and CFRelease() or by bridging to Objective-C objects with one of the __bridge functions.

To help understand whether or not a C function returns objects that are owned by the caller, Apple uses naming conventions defined by the Create Rule and the Get Rule:

  • The Create Rule states that a function with Create or Copy in its name returns ownerships to the call of the function. That is to say, the caller of a Create or Copy function will eventually need to call CFRelease on the returned object.

  • The Get Rule isn't so much a rule of its own as it is a catch-all for everything that doesn't follow the Create Rule. A function doesn't have Create or Copy in its name? It follows the Get rule instead, returning without transferring ownership. If you want the returned object to persist, in most cases it's up to you to retain it.

If you're a belt-and-suspenders-and-elastic-waistband programmer like I am, check the documentation as well. Even when they follow the proper name convention, most APIs also explicitly state which rule they follow, and any exceptions to the common cases.

Wait—this article is about Swift. Let's get back on track.

Swift uses ARC exclusively, so there's no room for a call to CFRelease or __bridge_retained. How does Swift reconcile this "management-by-convention" philosophy with its guarantees of memory safety?

This occurs in two different ways. For annotated APIs, Swift is able to make the conventions explicit—annotated CoreFoundation APIs are completely memory-managed and provide the same promise of memory safety as do bridged Objective-C or native Swift types. For unannotated APIs, however, Swift passes the job to you, the programmer, through the Unmanaged type.

Managing Unmanaged

While most CoreFoundation APIs have been annotated, some significant chunks have yet to receive attention. As of this writing, the Address Book framework seems the highest profile of the unannotated APIs, with several functions taking or returning Unmanaged-wrapped types.

An Unmanaged<T> instance wraps a CoreFoundation type T, preserving a reference to the underlying object as long as the Unmanaged instance itself is in scope. There are two ways to get a Swift-managed value out of an Unmanaged instance:

  • takeRetainedValue(): returns a Swift-managed reference to the wrapped instance, decrementing the reference count while doing so—use with the return value of a Create Rule function.

  • takeUnretainedValue(): returns a Swift-managed reference to the wrapped instance without decrementing the reference count—use with the return value of a Get Rule function.

In practice, you're better off not even working with Unmanaged instances directly. Instead, take... the underlying instance immediately from the function's return value and bind that.

Let's take a look at this in practice. Suppose we wish to create an ABAddressBook and fetch the name of the user's best friend:

letbestFriendID=ABRecordID(...)// Create Rule - retainedletaddressBook:ABAddressBook=ABAddressBookCreateWithOptions(nil,nil).takeRetainedValue()iflet// Get Rule - unretainedbestFriendRecord:ABRecord=ABAddressBookGetPersonWithRecordID(addressBook,bestFriendID)?.takeUnretainedValue(),// Create Rule (Copy) - retainedname=ABRecordCopyCompositeName(bestFriendRecord)?.takeRetainedValue()as?String{println("\(name): BFF!")// Rhonda Shorsheimer: BFF!}

With Swift 1.2's improved optional bindings, it's a piece of cake to unwrap, take the underlying value, and cast to a Swift type.

Better Off Without

Now that we've looked at how to work with Unmanaged, let's look at how to get rid of it altogether. If Unmanaged references are returned from calls to your own C functions, you're better off using annotations. Annotations let the compiler know how to automatically memory-manage your return value: instead of an Unmanaged<CFString>, you receive a CFString, which is type-safe and fully memory-managed by Swift.

For example, let's take a function that combines two CFString instances and annotate that function to tell Swift how to memory-manage the resulting string. Following the naming conventions described above, our function will be called CreateJoinedString—that name communicates that the caller will own the returned string.

CFStringRefCreateJoinedString(CFStringRefstring1,CFStringRefstring2);

Sure enough, in the implementation you can see that this creates resultString with CFStringCreateMutableCopy and returns it without a balancing CFRelease:

CFStringRefCreateJoinedString(CFStringRefstring1,CFStringRefstring2){CFMutableStringRefresultString=CFStringCreateMutableCopy(NULL,0,string1);CFStringAppend(resultString,string2);returnresultString;}

In our Swift code, just as above, we still need to manage the memory manually. Our function is imported as returning an Unmanaged<CFString>!:

// imported declaration:funcCreateJoinedString(string1:CFString!,string2:CFString!)->Unmanaged<CFString>!// to call:letjoinedString=CreateJoinedString("First","Second").takeRetainedValue()asString

Since our function follows the naming conventions described in the Create Rule, we can turn on the compiler's implicit bridging to eliminate the Unmanaged wrapper. Core Foundation provides two macros—namely, CF_IMPLICIT_BRIDGING_ENABLED and CF_IMPLICIT_BRIDGING_DISABLED—that turn on and off the Clang arc_cf_code_audited attribute:

CF_IMPLICIT_BRIDGING_ENABLED// get rid of Unmanaged#pragma clang assume_nonnull begin      // also get rid of !sCFStringRefCreateJoinedString(CFStringRefstring1,CFStringRefstring2);#pragma clang assume_nonnull beginCF_IMPLICIT_BRIDGING_DISABLED

Because Swift now handles the memory management for this return value, our code is simpler and skips use of Unmanaged altogether:

// imported declaration:funcCreateJoinedString(string1:CFString,string2:CFString)->CFString// to call:letjoinedString=CreateJoinedString("First","Second")asString

Finally, when your function naming doesn't comply with the Create/Get Rules, there's an obvious fix: rename your function to comply with the Create/Get Rules. Of course, in practice, that's not always an easy fix, but having an API that communicates clearly and consistently pays dividends beyond just avoiding Unmanaged. If renaming isn't an option, there are two other annotations to use: functions that pass ownership to the caller should use CF_RETURNS_RETAINED, while those that don't should use CF_RETURNS_NOT_RETAINED. For instance, the poorly-named MakeJoinedString is shown here with manual annotations:

CF_RETURNS_RETAINED__nonnullCFStringRefMakeJoinedString(__nonnullCFStringRefstring1,__nonnullCFStringRefstring2);

One gets that feeling that Unmanaged is a stopgap measure—that is, a way to use CoreFoundation while the work of annotating the mammoth API is still in progress. As functions are revised to interoperate more cleanly, each successive Xcode release may allow you to strip takeRetainedValue() calls from your codebase. Yet until the sun sets on the last CFUnannotatedFunctionRef, Unmanaged will be there to help you bridge the gap.

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.


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 midst of Swift code, 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{...}

So what is this strange new documentation format? It turns out that SourceKit (the private framework powering Xcode, previously known for its high FPS crashes) includes a basic parser for reStructuredText. Only a subset of the specification is implemented, but there's enough in there to cover basic formatting.

Basic Markup

Documentation comments are distinguished by using /** ... */ for multi-line comments or /// ... for single-line comments. Inside comment blocks, paragraphs are separated by blank lines. Unordered lists can be made with several bullet characters: -, +, *, , etc, while ordered lists use Arabic numerals (1, 2, 3, ...) followed by a period 1. or right parenthesis 1) or surrounded by parentheses on both sides (1):

/**    You can apply *italic*, **bold**, or `code` inline styles.    - Lists are great,    - but perhaps don't nest    - Sub-list formatting      - isn't the best.    1. Ordered lists, too    2. for things that are sorted;    3. Arabic numerals    4. are the only kind supported.*/

Definition & Field Lists

Defininition and field lists are displayed similarly in Xcode's Quick Documentation popup, with definition lists a little more compact:

/**    Definition list        A list of terms and their definitions.    Format        Terms left-justified, definitions indented underneath.    :Field header:        Field lists are spaced out a little more.    :Another field: Field lists can start the body immediately, without a line break and indentation.        Subsequent indented lines are treated as part of the body, too.*/

Two special fields are used to document parameters and return values: :param: and :returns:, respectively. :param: is followed by the name of the paramenter, then the description. Return values don't have a name, so the description begins immediately after :returns::

/**    Repeats a string `times` times.    :param: str     The string to repeat.    :param: times   The number of times to repeat `str`.    :returns: A new string with `str` repeated `times` times.*/funcrepeatString(str:String,times:Int)->String{returnjoin("",Array(count:times,repeatedValue:str))}

Code blocks

Code blocks can be embedded in documentation comments as well, which can be useful for demonstrating proper usage or implementation details. Inset the code block by at least two spaces:

/**    The area of the `Shape` instance.    Computation depends on the shape of the instance. For a triangle, `area` will be equivalent to:      let height = triangle.calculateHeight()      let area = triangle.base * height / 2*/vararea:CGFloat{get}

Documentation Is My New Bicycle

How does this look when applied to an entire class? Quite nice, actually:

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.    */enumStyle{caseRoad,Touring,Cruiser,Hybrid}/**        Mechanism for converting pedal power into motion.        - Fixed: A single, fixed gear.        - Freewheel: A variable-speed, disengageable gear.    */enumGearing{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}/**        Take a bike out for a spin.        :param: meters The distance to travel in meters.    */functravel(distancemeters:Double){ifmeters>0{distanceTravelled+=meters++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

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.append("A road bike for streets or trails")case.Touring:descriptors.append("A touring bike for long journeys")case.Cruiser:descriptors.append("A cruiser bike for casual trips around town")case.Hybrid:descriptors.append("A hybrid bike for general-purpose transportation")}switchself.gearing{case.Fixed:descriptors.append("with a single, fixed gear")case.Freewheel(letn):descriptors.append("with a \(n)-speed freewheel gear")}switchself.handlebar{case.Riser:descriptors.append("and casual, riser handlebars")case.Café:descriptors.append("and upright, café handlebars")case.Drop:descriptors.append("and classic, drop handlebars")case.Bullhorn:descriptors.append("and powerful bullhorn handlebars")}descriptors.append("on a \(frameSize)\" frame")// FIXME: Use a distance formatterdescriptors.append("with a total of \(distanceTravelled) meters traveled over \(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."

Jazzy

Jazzy is an open-source command-line utility that transforms your documentation comments into a set of Apple-like HTML documentation.


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.

XCPlayground

$
0
0

Stop right there! Given the topic, wouldn't you rather read this article as a Playground? Download Now →

Play.

Given the association of play with childish exuberance, one could define play as the opposite of work. And yet, the elements of play are what we do in our line of work every day: experimentation, exploration, discovery.

Playgrounds aren't a feature of the Swift language per se—instead, they are a terrific showcase for all that Swift has to offer, from its efficiency and power to its opacity and depth. Playgrounds make it truly simple to create a working program—indeed, every new Playground starts with the soon-familiar "Hello, playground". At the same time, Playgrounds hide some of their most powerful features, leaving exploration as the only means to discovering their rapidly advancing capabilities.

This week, we'll take a look past the surface of Playgrounds, giving you tools to make them a powerful part of your development process. Read on for more about Playground sources and resources, captured values and extended execution, and integrated rich formatting that can transform a Playground into an interactive teaching tool.

Note: The digital version of the recently released NSHipster: Obscure Topics in Cocoa & Swift includes a package of Playgrounds—one for every chapter in the book. Each Playground provides a chance to explore and experiment with the concepts presented therein, including extended examples.

Sources

The revamped Playground package structure includes a "Sources" folder for code that is more stable than experimental. All files in the "Sources" directory are compiled (just once, not every time you press a key) into a single module and automatically imported into the Playground. In addition to simplifying the code in your Playground, this compilation drastically speeds up execution. This means that if I have defined a type, function, or global constant in those files with public accessibility, I can use it in a Playground without further ado:

Compiled Sources

To get started in your own Playground, open the Project Navigator (⌘1) and expand the Playground file to see the "Sources" directory.

Importing Frameworks

To import an external framework, create a new Xcode Workspace that contains both the framework project and your Playground. After building the framework, it can be imported with a standard import statement.

Resources

No playground is complete without a sandbox to play in, and Swift Playgrounds don't disappoint. Playgrounds have two places to keep associated resources: one local to each individual playground and a second shared amongst all of them. Being able to load and work with XML, JSON data, XIBs, and images extends the usefulness of your Playground experimentation.

Local

The Resources folder, embedded in the Playground package alongside Sources, is visible in the Project Navigator—simply drag and drop images or data files to use them in your Playground. The contents are then available via the main bundle. For example, we can easily load a JSON file filled with weather data:

letjsonPath=NSBundle.mainBundle().bundlePath.stringByAppendingPathComponent("weather.json")ifletjsonData=NSData(contentsOfFile:jsonPath),json=NSJSONSerialization.JSONObjectWithData(jsonData,options:nil,error:nil)as?[String:AnyObject]{// ...}

Shared

The contents of a "Shared Playground Data" directory inside your "Documents" folder are available to any Playground you create. Access the shared folder via the XCPSharedDataDirectoryPath constant.

To try this out yourself, you'll need to create the directory at "~/Documents/Shared Playground Data". Here we're attempting to load an image named "image.png":

letsharedImagePath=XCPSharedDataDirectoryPath.stringByAppendingPathComponent("image.png")ifletimage=UIImage(contentsOfFile:sharedImagePath){// ...}

Captured Values

A Playground normally shows the results of simple expressions immediately. Arrays, strings, numbers, and more have their values shown in the results pane as they are calculated. But what about values that change over time?

By using the XCPCaptureValue() function, we can build a graph of a changing value over a series of iterations. Returning to our weather sample, let's take a look at the hourly temperatures in the data, using XCPCaptureValue to display the value of temperature in the Assistant Editor's timeline view:

importXCPlaygroundforforecastinforecasts{iflettempString=forecast["temp"]?["english"]as?String,temperature=tempString.toInt(){XCPCaptureValue("Temperature",temperature)}}

Alternatively, choosing Editor → Show Result For Current Line will capture the current line's values and display the chart directly in the flow of the Playground:

Result for Current Line

Asynchronous Execution

Unlike most Swift code that is written as part of an app or framework, Playgrounds are treated as top-level code. Top-level code in a Playground is executed instruction-by-instruction in order, from top to bottom. This container-less style of execution provides immediate feedback, but there is one problem: execution halts as soon as it reaches the end of the Playground. Network requests, timers, and long-running background queues are abandoned before they can return to report on success or failure.

To keep execution going long enough to see the results of these kinds of asynchronous operations, the XCPlayground module includes a function that extends the length of the process:

importXCPlaygroundXCPSetExecutionShouldContinueIndefinitely(continueIndefinitely:true)leturl=NSURL(string:"http://httpbin.org/image/png")!lettask=NSURLSession.sharedSession().dataTaskWithURL(url){data,_,_inletimage=UIImage(data:data)// ...}task.resume()

In addition to the code in a Playground, Swift code run from the command line in a scripting file, in the Swift REPL, or in a project's optional "main.swift" file is also considered top-level code. Along with order-dependent execution, top-level code is the only kind that can contain statements outside of a function or encapsulated within a type.

Documentation

Beyond experimentation, Playgrounds are also powerful for demonstrating tools and frameworks in the Swift language. Special documentation sections can be rendered as rich text, giving clear narration to code that demonstrates a technique or the correct way to use a library.

Unlike Swift's other documentation syntax, Swift Playgrounds use Markdown for richly formatted text. (If you downloaded the Playground for this post, you're reading Markdown right now.) A colon (:) added to a single- or multi-line comment marker signifies a rich-text comment:

//: This line will have **bold** and *italic* text./*:## Headers of All Sizes### Lists of Links- [NSHipster](http://nshipster.com)- [ASCIIwwdc](http://asciiwwdc.org)- [SwiftDoc](http://swiftdoc.org)### Images, Too![Remote Image](http://nshipster.s3.amazonaws.com/alert.gif)![Local Image](bomb.gif) *Images in the Resources directory can be referenced locally**/

Rendered Documentation

It's possible to toggle rich documentation rendering either by selecting the Editor → Show Rendered Markup menu item or by checking the Render Documentation checkbox in the File Inspector (⌘⌥1).


Playgrounds represent a major shift in the way we share and learn about tools for OS X and iOS. A Playground can demonstrate each feature and provide a space for potential users to discover and explore the library you've created. Trade out your static README.md for an interactive README.playground and let the play begin anew.

MirrorType

$
0
0

Reflection in Swift is a limited affair, providing read-only access to a subset of type metadata. While far from the rich array of run-time hackery familiar to seasoned Objective-C developers, Swift's tools enable the immediate feedback and sense of exploration offered by Xcode Playgrounds.

Perhaps Swift's strict type checking obviates the need for reflection. With variable types typically known at compile time, there might not be cause for further examination or branching. Then again, a hefty number of Cocoa APIs dole out AnyObject instances at the drop of a hat, leaving us to cast about for the matching type.

This week, we'll reflect on reflection in Swift, its mirror types, and MirrorType, the protocol that binds them together.


MirrorType

The entry point for reflection is the reflect function, which can take an instance of any type as its single parameter and returns a MirrorType. Now, MirrorType is something of an oddity for the Swift standard libary: a protocol used as a type. Other than the ubiquitous AnyObject, to date no other protocol is used this way. The particular MirrorType-conforming instance that you receive depends on the type passed to reflect—Swift's internals define mirrors for types such as Array, Dictionary, Optional, and Range, along with more generic mirrors for structs, classes, tuples, and metatypes.

MirrorType provides the nascent reflection API that Swift offers, wrapping a value along with its type information, information about its children, and different representations of the instance. Mirrors have the following properties:

  • value: access to the original reflected value, but with type Any.
  • valueType: the Type of the original reflected value—equivalent to value.dynamicType.
  • count: the number of logical children. For a collection, like Array or Set, this is the number of elements; for a struct, this is the number of stored properties.
  • disposition: a value from the MirrorDisposition enumeration, intended to help the IDE choose how to display the value. MirrorDisposition has eleven cases:
    • IndexContainer, KeyContainer, MembershipContainer, Container: used for collections.
    • Optional: used for optional values. Implicitly unwrapped optionals are skipped over by reflect() to fetch the reflection of the unwrapped value.
    • Aggregate: used for Swift types that bridge to Objective-C and for Objective-C types that have been augmented for use with Swift. For example, Float has an Aggregate disposition while the non-bridged Float80 returns Struct, and UIView (extended for Reflectable conformance), has an Aggregate disposition while the unadorned UIBarButtonItem returns ObjCObject.
    • ObjCObject: by contrast with Aggregate, used for unextended Objective-C classes.
    • Tuple: used for tuple values.
    • Struct, Class, Enum: used as fallback cases for types that don't fall into any of the above categories.
  • objectIdentifier: the unique object identifier for a class or metatype instance.
  • summary: a string description of the value.
  • quickLookObject: a QuickLookObject instance holding a visual or text representation of the value. Its behavior is similar to the debugQuickLookObjectwe covered a few weeks back.

Additionally, a mirror has an Int-based subscript that returns a (String, MirrorType) tuple for each child. That's the name of the property/key/index and a mirror of the value.

So how can we put MirrorType to use? Let's suppose we have a group of numbers in a tuple that we want to use for a lottery ticket, but we need to convert them to an [Int] array first:

letlotteryTuple=(4,8,15,16,23,42)

Rather than extracting the pieces of the tuple one by one (i.e., lotteryType.0, lotteryTuple.1, etc.), we can use reflect() to iterate over the elements:

// create a mirror of the tupleletlotteryMirror=reflect(lotteryTuple)// loop over the elements of the mirror to build an arrayvarlotteryArray:[Int]=[]foriin0..<lotteryMirror.count{let(index,mirror)=lotteryMirror[i]ifletnumber=mirror.valueas?Int{lotteryArray.append(number)}}println(lotteryArray)// [4, 8, 15, 16, 23, 42]

Not bad.

Mapping a Mirror

If we could map over the elements in a mirror, reflecting over an instance's properties or elements would be a bit easier. Let's write a mapReflection function that takes an instance of any type and a transforming closure:

funcmapReflection<T,U>(x:T,@noescapetransform:(String,MirrorType)->U)->[U]{varresult:[U]=[]letmirror=reflect(x)foriin0..<mirror.count{result.append(transform(mirror[i]))}returnresult}

Now we can quite simply print all the logical children of any instance:

letprintChild:(String,MirrorType)->()={println("\($0): \($1.value)")}mapReflection(lotteryTuple,printChild)// .0: 4// .1: 8// ...mapReflection(lotteryArray,printChild)// [0]: 4// [1]: 8// ...mapReflection(CGRect.zeroRect,printChild)// origin: (0.0, 0.0)// size: (0.0, 0.0)

That output might look familiar to those who have used Swift's dump function before. dump uses reflection recursively to print out an instance's children, their children, and so on:

dump(CGRect.zeroRect)// ▿ (0.0, 0.0, 0.0, 0.0)//   ▿ origin: (0.0, 0.0)//     - x: 0.0//     - y: 0.0//   ▿ size: (0.0, 0.0)//     - width: 0.0//     - height: 0.0

Custom-Cut Mirrors

Beyond dump, Xcode also uses mirrors extensively for the display of values in a Playground, both in the results pane on the right side of a Playground window and in captured value displays. Custom types don't start out with a custom mirror, so their display can leave something to be desired. Let's look at the default behavior of a custom type in a Playground and then see how a custom MirrorType can improve that display.

For our custom type, we'll use a simple struct to hold information about a WWDC session:

/// Information for a single WWDC session.structWWDCSession{/// An enumeration of the different WWDC tracks.enumTrack:String{caseFeatured="Featured"caseAppFrameworks="App Frameworks"caseDistribution="Distribution"caseDeveloperTools="Developer Tools"caseMedia="Media"caseGraphicsAndGames="Graphics & Games"caseSystemFrameworks="System Frameworks"caseDesign="Design"}letnumber:Intlettitle:Stringlettrack:Trackletsummary:String?}letsession801=WWDCSession(number:801,title:"Designing for Future Hardware",track:.Design,summary:"Design for tomorrow's products today. See examples...")

By default, reflection on a WWDCSession instance uses the built-in _StructMirror type. This provides a property-based summary on the right (useful) but only the class name in a captured value pane (not so useful):

Default WWDCSession Representation

To provide a richer representation of a WWDCSession, we'll implement a new type, WWDCSessionMirror. This type must conform to MirrorType, including all the properties listed above:

structWWDCSessionMirror:MirrorType{privatelet_value:WWDCSessioninit(_value:WWDCSession){_value=value}varvalue:Any{return_value}varvalueType:Any.Type{returnWWDCSession.self}varobjectIdentifier:ObjectIdentifier?{returnnil}vardisposition:MirrorDisposition{return.Struct}// MARK: Child propertiesvarcount:Int{return4}subscript(index:Int)->(String,MirrorType){switchindex{case0:return("number",reflect(_value.number))case1:return("title",reflect(_value.title))case2:return("track",reflect(_value.track))case3:return("summary",reflect(_value.summary))default:fatalError("Index out of range")}}// MARK: Custom representationvarsummary:String{return"WWDCSession \(_value.number) [\(_value.track.rawValue)]: \(_value.title)"}varquickLookObject:QuickLookObject?{return.Text(summary)}}

In the summary and quickLookObject properties, we provide our custom representation of a WWDCSession—a nicely formatted string. Note, in particular, that the implementation of count and the subscript are completely manual. The default mirror types ignore private and internal access modifiers, so a custom mirror could be used to hide implementation details, even from reflection.

Lastly, we must link WWDCSession to its custom mirror by adding conformance to the Reflectable protocol. Conformance only requires a single new method, getMirror(), which returns a MirrorType—in this case, our shiny new WWDCSessionMirror:

extensionWWDCSession:Reflectable{funcgetMirror()->MirrorType{returnWWDCSessionMirror(self)}}

That's it! The Playground now uses our custom representation instead of the default:

Custom WWDCSession Representation

In the absence of Printable conformance, println() and toString() will also pull the string representation from an instance's mirror.


In its current form, Swift reflection is more novelty than powerful feature. With new Swift functionality surely right around the corner at WWDC, this article may prove to have a very short shelf life indeed. But in the mean time, should you find the need for introspection, you'll know just where to look.

NSHipster Quiz #7

$
0
0

On June 9th, we organized the third annual WWDC edition of the NSHipster Pub Quiz, with topics ranging from pop cultural trivia to technical pedantry. A huge thanks to Realm, who opened their offices and hosted with aplomb the scores of developers who turned out for the quiz.

With dozens of teams competing for the prizes on offer, competition was fierce. After the final tally, Team "U+1F4A9" (aka "💩 PILE OF POO") took the top prize with a score of 35 points. Congratulations to Benjamin Encz, Dave Verwer, Emilio Peláez, Jeffrey Bergier, Michael Helmbrecht, and Warren Moore on the win!

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

  • Four rounds of ten questions each
  • Record your answers on a separate sheet of paper
  • Each correct answer to a question gets you 1 point (unless otherwise specified)
  • Play with up to five friends for maximum enjoyment
  • Don't be lame and look things up on the Internet or in Xcode

Round 1: General Knowledge

  1. What 1989 movie introduces its protagonist free climbing El Capitan, to disastrous effect?
  2. El Capitan looks down on a river that winds through Yosemite. What is the river’s name?
  3. In the WWDC keynote, Tim Cook showed off a list of demands some Cleveland Indians players were using to ransom their teammate’s 100th home run ball. What item did Apple carefully remove from the list?
  4. With WebKit already using WK, WatchKit had to find a different class prefix. What is the unusual prefix used for the most of the WatchKit framework?
  5. The name "Swift" can be written using a ligature for its final two letters. What are the five Unicode-standard ligatures beginning with the letter "f"?
  6. The typeface used for Apple Watch (and the new versions of iOS and OS X) is "San Francisco," but it isn’t the first custom font Apple has used by that name. What were the letters of the original "San Francisco" meant to resemble? For a bonus point, name the original font's designer.
  7. What is reportedly the lock screen image on Jony Ive’s iPhone?
  8. Apple made a splash when they announced that ResearchKit would be open source and hosted on GitHub. What was the first pull request accepted for ResearchKit?
  9. ResearchKit also uses an unexpected class prefix. What is the prefix and what does it stand for?
  10. Scott Forstall is back in the news, producing a Broadway musical that just won five Tonys. What is the name of the musical? For a bonus point, upon what author's memoir is the musical based?

Round 2: Name That Framework

How well do you know Cocoa? For each question in this round, you'll be given three classes with their identifying prefix removed. Name the framework which contains all three.

  1. MultiPoint, Placemark, Polyline
  2. Locale, NotificationCenter, NumberFormatter
  3. Correlation, Statistics, Unit
  4. Navigation, Preferences, UserScript
  5. Switch, Timer, Map
  6. URL, URLCache, URLCredential
  7. Activity, Lexicon, DictationPhrase
  8. ContentItem, MoviePlayerController, RemoteCommand
  9. PaymentToken, PaymentMethod, ShippingMethod
  10. Beacon, Floor, Visit

Round 3: Picture Round

  1. Who is shown here sporting this unannounced gold link bracelet Apple Watch shortly before launch? Picture 1

  2. What TV show featured these watches? For a bonus point, what was the related catchphrase? Picture 2

  3. Hailing from the same era, what development tool is this? Picture 3

  4. What Apple Design Award-winning app is this? Picture 4

  5. What Apple Design Award-winning app is this? Picture 5

  6. What app is this? Picture 6

  7. What app is this? Picture 7

  8. Who is this? Picture 8

  9. Who is this? Picture 9

  10. What are the Unicode names for these Emoji? Picture 10

Round 4: Anagrammable

NSAnagram has been ported to Swift as Anagrammable, still a vexing test of knowledge and concentration. Each question is an anagram, whose letters can be rearranged to form the name of a type or protocol in the Swift standard library. Good luck!

For timing, listen to these three songs play back to back:




  1. Be Loud!
  2. Forget No Ear
  3. Too Plain
  4. Oboe Penalty
  5. Scalable Lube Item
  6. Poets Win At Poetry
  7. Ol’ Potty Licence
  8. Unprofitable Tea Menus
  9. Ram Placebo
  10. Tin?

Answers

Round 1: General Knowledge

  1. Star Trek V: The Final Frontier
  2. Merced River
  3. 50 gallon drum of lube
  4. WKInterface
  5. ff, fi, fl, ffi, ffl
  6. A ransom note, by Susan Kare
  7. A Playmobile figure of himself
  8. A typo in the README.md
  9. ORK: Open Research Kit
  10. Fun Home, based on the memoir by Alison Bechdel

Round 2: Name That Framework

  1. MapKit
  2. Foundation or CoreFoundation (2 points for both)
  3. HealthKit
  4. WebKit
  5. WatchKit
  6. Foundation
  7. UIKit
  8. MediaPlayer
  9. PassKit
  10. CoreLocation

Round 3: Picture Round

  1. Beyonce
  2. Parker Lewis Can't Lose, "Synchronize Swatches"
  3. ResEdit
  4. Metamorphabet
  5. Workflow
  6. Meerkat
  7. GIFs
  8. Chris Lattner
  9. Christian Laettner
  10. Aubergine, Dancer, Clinking Beer Mugs

Round 4: Anagrammable

  1. Double! (2 points if marked as an implicitly unwrapped optional)
  2. GeneratorOf
  3. Optional
  4. BooleanType
  5. MutableSliceable
  6. RawOptionSetType
  7. CollectionType
  8. UnsafeMutablePointer
  9. Comparable
  10. Int? (2 points if marked as an Optional)

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


iOS 9

$
0
0

WWDC 2015 may not have packed quite as many fireworks as its predecessor, but neither was it short on the new and shiny. The introduction of watchOS 2, including a revamped WatchKit, access to the Apple Watch hardware, and support for complications. Major changes to the Swift language and the announcement of open source plans. Brand new frameworks, such as Contacts, ContactsUI, and CoreSpotlight, and new UIKit types like UIStackView and SFSafariViewController.

We'll surely delve into these major new components in the weeks and months to come. For now, however, let's take a look at some of the smaller changes that iOS 9 brings to the APIs we already know and love.


String Transformations

Up first is the news that string transformations, formerly housed in Core Foundation, have made their way to NSString and the Swift String type. This is a huge advance in discoverability and ease-of-use for this powerful Cocoa feature, because there is no longer any need to deal with the hassle of bridging to and from CFStringRef. Here's how some of our favorite transformations can be done along with the new NSStringTransform* constants that enable them:

Transliteration

"privet".stringByApplyingTransform(NSStringTransformLatinToCyrillic,reverse:false)// "привет""안녕하세요".stringByApplyingTransform(NSStringTransformLatinToHangul,reverse:true)// "annyeonghaseyo""annyeonghaseyo".stringByApplyingTransform(NSStringTransformLatinToHangul,reverse:false)// "안녕하세요"
NSLog(@"%@",[@"privet"stringByApplyingTransform:NSStringTransformLatinToCyrillicreverse:NO]);// "привет"NSLog(@"%@",[@"annyeonghaseyo"stringByApplyingTransform:NSStringTransformLatinToHangulreverse:NO]);// "안녕하세요"NSLog(@"%@",[@"안녕하세요"stringByApplyingTransform:NSStringTransformLatinToHangulreverse:YES]);// "annyeonghaseyo"

Unicode Names

"🐷".stringByApplyingTransform(NSStringTransformToUnicodeName,reverse:false)// "{PIG FACE}"
NSLog(@"%@",[@"🐷"stringByApplyingTransform:NSStringTransformToUnicodeNamereverse:NO]);// "{PIG FACE}"

Normalizing User Input

"Hello! こんにちは! สวัสดี! مرحبا! 您好!".stringByApplyingTransform(NSStringTransformToLatin,reverse:false)?.stringByApplyingTransform(NSStringTransformStripDiacritics,reverse:false)?.localizedLowercaseString.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet())// ["hello!", "kon'nichiha!", "swasdi!", "mrhba!", "nin", "hao!"]
NSString*input=@"Hello! こんにちは! สวัสดี! مرحبا! 您好!";NSString*processing=[inputstringByApplyingTransform:NSStringTransformToLatinreverse:NO];processing=[processingstringByApplyingTransform:NSStringTransformStripDiacriticsreverse:NO];NSArray<NSString*>*output=[processing.localizedLowercaseStringcomponentsSeparatedByCharactersInSet:[NSCharacterSetwhitespaceCharacterSet]];NSLog(@"%@",output);// ["hello!", "kon'nichiha!", "swasdi!", "mrhba!", "nin", "hao!"]

For more on string transformations, be sure to check out Mattt's terrific article on CFStringTransform.

CLLocationManager.requestLocation

Core Location includes a nice new API for apps that simply need to fetch the user's location without continuous location updates. requestLocation() uses the same delegate methods as continuous updates, turning itself off after delivering the location with the desired accuracy:

classViewController:UIViewController,CLLocationManagerDelegate{letlocationManager=CLLocationManager()// ...overridefuncviewDidLoad(){super.viewDidLoad()locationManager.delegate=selflocationManager.desiredAccuracy=kCLLocationAccuracyHundredMeterslocationManager.requestLocation()}// MARK: - CLLocationManagerDelegatefunclocationManager(manager:CLLocationManager,didUpdateLocationslocations:[CLLocation]){ifletlocation=locations.first{print("Current location: \(location)")}else{// ...}}funclocationManager(manager:CLLocationManager,didFailWithErrorerror:NSError){print("Error finding location: \(error.localizedDescription)")}}

Swiftification

The Cocoa APIs in iOS 9 (and OS X 10.11) include thousands of minor changes designed to make Swift interoperability safer and cleaner. These changes start with the familiar nullability annotations, converting implicitly unwrapped parameters and return values to either true Optionals or simple, non-optional data types. But they also go much further.

For example, instead of marking NSArray return values as nullable, many APIs have been modified to return an empty array—semantically these have the same value (i.e., nothing), but a non-optional array is far simpler to work with. Moreover, many Cocoa APIs are gradually taking advantage of the new Objective-C generic syntax to provide typed arrays. The CLLocationManagerDelegate method locationManager(_:didUpdateLocations:) shown above is one such example: where the locations parameter used to be imported as an AnyObject array, it is now an array of CLLocation, eliminating the need for any casting in the method body.

Finally, some APIs that were completely inaccessible from Swift have been revised, such as methods for retrieving UIAppearance proxies that limit appearance to a particular containment hierarchy—UIKit's version of CSS-lite. The old appearanceWhenContainedIn: method is implemented with C variadic parameters, which Swift doesn't import; the new appearanceWhenContainedInInstancesOfClasses(_:) method simply takes an array of type objects:

UIBarButtonItem.appearanceWhenContainedInInstancesOfClasses([UINavigationController.self]).tintColor=UIColor.redColor()
[UIBarButtonItemappearanceWhenContainedInInstancesOfClasses:@[[UINavigationControllerclass]]].tintColor=[UIColorredColor];

Ironically enough, this method crashes at runtime when used from Swift. Betas gonna beta.

NSFormatter Additions

The new Contacts framework includes NSFormatter subclasses for localized formatting of contacts and addresses alongside a new Foundation NSPersonNameComponentsFormatter class. We'll cover those more in the weeks to come, but here let's highlight a few additions to two old favorites: NSNumberFormatter and NSDateFormatter.

NSNumberFormatter

First, NSNumberFormatter sees four additional styles in iOS 9, starting with .OrdinalStyle, used for converting numbers to their ordinal equivalent:

letformatter=NSNumberFormatter()formatter.numberStyle=.OrdinalStyleletnumbers=[1,2,3,4,5]numbers.map{formatter.stringFromNumber($0)!}// ["1st", "2nd", "3rd", "4th", "5th"]formatter.locale=NSLocale(localeIdentifier:"es")numbers.map{formatter.stringFromNumber($0)!}// ["1º", "2º", "3º", "4º", "5º"]
NSNumberFormatter*formatter=[[NSNumberFormatteralloc]init];formatter.numberStyle=NSNumberFormatterOrdinalStyle;NSArray<NSNumber*>*numbers=@[@1,@2,@3,@4,@5];for(NSNumber*numberinnumbers){NSLog(@"%@",[formatterstringFromNumber:number]);}// "1st", "2nd", "3rd", "4th", "5th"formatter.locale=[NSLocalelocaleWithLocaleIdentifier:@"es"];for(NSNumber*numberinnumbers){NSLog(@"%@",[formatterstringFromNumber:number]);}// "1º", "2º", "3º", "4º", "5º"

Next, the existing .CurrencyStyle gets some company with .CurrencyPluralStyle, .CurrencyISOCodeStyle, and .CurrencyAccountingStyle. When using these new styles, be sure your locale fully specifies both a language and a country, which it makes it possible for the formatter to choose the right currency and presentation:

letstyles:[NSNumberFormatterStyle]=[.CurrencyStyle,.CurrencyPluralStyle,.CurrencyISOCodeStyle,.CurrencyAccountingStyle]formatter.locale=NSLocale(localeIdentifier:"en_US")styles.map{formatter.numberStyle=$0returnformatter.stringFromNumber(-125)!}// ["-$125.00", "-125.00 US dollars", "-USD125.00", "($125.00)"]formatter.locale=NSLocale(localeIdentifier:"es_ES")styles.map{formatter.numberStyle=$0returnformatter.stringFromNumber(-125)!}// ["-125,00 €", "-125,00 euros", "-125,00 EUR", "-125,00 €"]

NSDateFormatter

Second—and I'll fess up—this new NSDateFormatter method is a bit of a cheat. setLocalizedDateFormatFromTemplate(_:) was introduced in iOS 8 but largely slid under the radar until this year's sessions on internationalization. This new-ish method makes it supremely easy to define a template for the date and time elements you want; however, it leaves the formatting up to the excellent localization built into NSDateFormatter:

letnow=NSDate()// full date and timeletfullFormatter=NSDateFormatter()fullFormatter.setLocalizedDateFormatFromTemplate("yyyyMMMMddhhmm")// month name and year onlyletshortFormatter=NSDateFormatter()shortFormatter.setLocalizedDateFormatFromTemplate("yyMMMM")fullFormatter.stringFromDate(now)// "June 23, 2015, 4:56 PM"shortFormatter.stringFromDate(now)// "June 15"// switch locales to "de_DE"fullFormatter.locale=NSLocale(localeIdentifier:"de_DE")shortFormatter.locale=NSLocale(localeIdentifier:"de_DE")fullFormatter.stringFromDate(now)// "Juni 23, 2015, 4:56 nachm."shortFormatter.stringFromDate(now)// "Juni 15"

Well, that's that for our quick tour through some of iOS 9's new APIs, though there are many more where those came from. What new features are you most excited about in iOS 9 or OS X 10.11? Check out Apple's diffs and let us know!

CloudKit

$
0
0

As an iOS developer, if you want to make an application on your own, you sometimes need to write back-end code. Even for the developer who can take that on, there is more than just the code, there's also maintenance. Your worst fear becomes not that people might not like your application, but that your server might fail under heavy traffic.

Fortunately, we now have CloudKit. Apple takes care of all these details, so you can focus on how to make your application great.

What is CloudKit?

Perhaps you've heard of iCloud Drive before—iCloud Drive is where we can store our user's data and files for easy access from other devices. CloudKit is the framework that helps us do this easily in the apps we create.

CloudKit offers tons of APIs to access iCloud. You can create a user model inside your application linked to a user's iCloud account. Meanwhile, you can have a public global database to store application-level data. You can also save large files and bulk data into iCloud Drive, so your users can use their data from their other devices. This works just like working on local files, but with all the operations sent to the cloud.

Overall, CloudKit is a framework that replaces back-end web services like old-school databases, file storage, and user authentication systems. With CloudKit's help you don't need to worry about any of these, so you can focus your energy on your application.

Get into CloudKit

Imagine that you're working on a check-in application where users can add "places" with their location and check in at these places. We'll talk about how to build some basic functions of the check-in application with CloudKit.

Enable CloudKit

We have already talked about how powerful CloudKit is, now it is the time to show you how to use it. It's simple. All you need is to turn on iCloud and check CloudKit in the project panel of Xcode:

Enabling CloudKit in Xcode

Fundamental CloudKit Objects

There are 7 different fundamental objects in CloudKit. You may have seen this elsewhere in your programming career, but there are some slight differences.

  • CKContainer: A container is like a sandbox. An application can only use the resources inside its container. The container is located at the very outer border and each application has one and only one separate container. (You can allow other applications to access your container by configuring CloudKit Dashboard.)

  • CKDatabase: A database is the place that you put all your data. There are two different kinds of databases: private and public. The private database is where you store sensitive data, like user's information. The public database is where you store shared data. For example, in our check-in application, you would store a user's birthday and check-ins in the private database but store "places" information in the public database.

  • CKRecord: A record is a piece of data inside your database. It is stored as a key-value pair. For now, you can save NSString, NSNumber, NSData, NSDate, CLLocation, CKReference, and CKAsset, as well as arrays of all the types listed above.

  • CKRecordZone: Records are not stored scattered in a database, they are located in record zones. Every application has a default record zone, and you can also have your own custom record zones.

  • CKRecordIdentifier: the unique label of a record, used for locating a particular record.

  • CKReference: Reference is like the relationship in an RDBMS. In our check-in example, there may be many people checked in at the same place, so we'll need to establish a reference between places and check-ins.

  • CKAsset: Assets are resources, like binary files or bulk data. For example, a user's picture should be stored as an asset.

Convenience API

CloudKit's convenience API is there for do basic operations such as reading, writing, and editing records.

Let's work on our check-in application. To get started, import the CloudKit framework and get a reference to the public database:

importCloudKit// ...letpublicDB=CKContainer.defaultContainer().publicCloudDatabase
#import <CloudKit/CloudKit.h>// ...CKDatabase*publicDB=[[CKContainerdefaultContainer]publicCloudDatabase];

Next, create a new place and save it:

letgreatID=CKRecordID(recordName:"GreatPlace")letplace=CKRecord(recordType:"Place",recordID:greatID)publicDB.saveRecord(place){savedRecord,errorin// handle errors here}
CKRecordID*greatID=[[CKRecordIDalloc]initWithRecordName:@"GreatPlace"];CKRecord*place=[[CKRecordalloc]initWithRecordType:@"Place"recordID:greatID];[publicDBsaveRecord:placecompletionHandler:^(CKRecord*savedPlace,NSError*error){// handle errors here}];

CloudKit will connect to the Internet asynchronously when saveRecord:completionHandler: method is invoked. Remember to handle the error in the block, since the user's connection may be unstable. A good application deserves perfect error handling logic.

You should check the error code of the NSError object to detect which kind of error you are dealing with. A CKErrorNetworkUnavailable error may occur if you were on a bad internet connection, and what you need to do is to retry the operation after failure. But when to retry? Immediately or 10 seconds later? Don't worry, CloudKit offers a suggestion in the error's userInfo dictionary with the key CKErrorRetryAfterKey:

ifletretryAfterValue=error.userInfo[CKErrorRetryAfterKey]as?NSTimeInterval{letretryAfterDate=NSDate(timeIntervalSinceNow:retryAfterValue)// ...}
doubleretryAfterValue=error.userInfo[CKErrorRetryAfterKey];NSDate*retryAfterDate=[NSDatedateWithTimeIntervalSinceNow:retryAfterValue];

Here I'll read the place's information back:

letgreatID=CKRecordID(recordName:"GreatPlace")publicDB.fetchRecordWithID(greatID){fetchedPlace,errorin// handle errors here}
CKRecordID*greatID=[[CKRecordIDalloc]initWithRecordName:@"GreatPlace"];[publicDBfetchRecordWithID:greatIDcompletionHandler:^(CKRecord*fetchedPlace,NSError*error){// handle errors here}];

And here I'll edit an existing place's information:

letgreatID=CKRecordID(recordName:"GreatPlace")publicDB.fetchRecordWithID(greatID){fetchedPlace,erroringuardletfetchedPlace=fetchedPlaceelse{// handle errors herereturn}letname=fetchedPlace["name"]as?String??"Unnamed Place"fetchedPlace["name"]=name+" Door A"publicDB.saveRecord(fetchedPlace){savedPlace,savedErrorin//...}}
CKRecordID*greatID=[[CKRecordIDalloc]initWithRecordName:@"GreatPlace"];[publicDBfetchRecordWithID:greatIDcompletionHandler:^(CKRecord*fetchedPlace,NSError*error){if(fetchedPlace!=nil){NSString*name=fetchedPlace[@"name"];fetchedPlace[@"name"]=[namestringByAppendingString:@" Door A"];[publicDBsaveRecord:fetchedPlacecompletionHandler:^(CKRecord*savedPlace,NSError*savedError){//...}];}else{// handle errors here}}];

The progress of editing a record is pretty simple: read, edit, then save. What you should really pay attention to is how to do the three-step updating process, especially when updating one record depends on fetching others.

A bad practice:

database.fetchRecordWithID(recordID,completionHandler:{record,errorin//...database.fetchRecordWithID(otherRecordID,completionHandler:{otherRecord,otherErrorin//...database.saveRecord(record!,completionHandler:{anotherRecord,anotherErrorin//...})})})
[databasefetchRecordWithID:recordIDcompletionHandler:^(CKRecord*record,NSError*error){//...[databasefetchRecordWithID:otherRecordIDcompletionHandler:^(CKRecord*otherRecord,NSError*otherError){//...[databasesaveRecord:recordcompletionHandler:^(CKRecord*anotherRecord,NSError*anotherError){//...}];}];}];

With very complex nested operations you may run into a dilemma: There are three (or more) blocks and three (or more) errors to handle, so where should you handle the errors? where should you retry the operation if an error occurs? All together it starts looking like kind of a disaster.

A better approach is to use NSOperation dependencies to manage the dependent tasks:

letfirstFetch=CKFetchRecordsOperation()letsecondFetch=CKFetchRecordsOperation()secondFetch.addDependency(firstFetch)letqueue=NSOperationQueue()queue.addOperations([firstFetch,secondFetch],waitUntilFinished:false)
CKFetchRecordsOperation*firstFetch=...;CKFetchRecordsOperation*secondFetch=...;[secondFetchaddDependency:firstFetch];NSOperationQueue*queue=[[NSOperationQueuealloc]init];[queueaddOperations:[firstFetch,secondFetch]waitUntilFinished:NO];

You can finish almost all the work you need to do with the convenience API. What do you think so far? It's much easier than writing backend code, maintaining a server, and writing the code to communicate with it.

Advanced Features

Queries

While powerful, the convenience APIs aren't quite enough to finish our check-in application—now it's time to add search functionality. To add a search function, you will need a query. A CKQuery object is made up of RecordType, NSPredicate and NSSortDescriptors.

NSPredicate plays an important role here, handling string matching, location and date ranging, and combinations of simple queries. Refer to the CKQuery documentation for more.

Let's say I want all places containing the name 'Apple Store':

letpredicate=NSPredicate(format:"name CONTAINS 'Apple Store'")letquery=CKQuery(recordType:"Place",predicate:predicate)publicDB.performQuery(query,inZoneWithID:nil){results,errorin// ...}
NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"name CONTAINS 'Apple Store'"];CKQuery*query=[[CKQueryalloc]initWithRecordType:@"Place"predicate:predicate];[publicDBperformQuery:queryinZoneWithID:nilcompletionHandler:^(NSArray*results,NSError*error){// ...}];

Alternately, you could modify the query to retrieve all the places within one mile around the user.

Subscriptions

After adding queries, our application is almost complete. Or wait, did we forget something?

Yes: notifications. They're a critical part of any check-in application.

For example, a social person may want to be notified if someone mentions "party" around him or her. This is possible with CloudKit—the framework already provides something to achieve this using the CKSubscription class:

letpredicate=NSPredicate(format:"description CONTAINS 'party'")letsubscription=CKSubscription(recordType:"Checkin",predicate:predicate,options:.FiresOnRecordCreation)letinfo=CKNotificationInfo()info.alertLocalizationKey="NEW_PARTY_ALERT_KEY"info.soundName="NewAlert.aiff"info.shouldBadge=truesubscription.notificationInfo=infopublicDB.saveSubscription(subscription){subscription,errorin//...}
CKDatabase*publicDB=[[CKContainerdefaultContainer]publicCloudDatabase];NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"description CONTAINS 'party'"];CKSubscription*subscription=[[CKSubscriptionalloc]initWithRecordType:@"Checkin"predicate:predicateoptions:CKSubscriptionOptionsFiresOnRecordCreation];CKNotificationInfo*info=[CKNotificationInfonew];info.alertLocalizationKey=@"NEW_PARTY_ALERT_KEY";info.soundName=@"NewAlert.aiff";info.shouldBadge=YES;subscription.notificationInfo=info;[publicDBsaveSubscription:subscriptioncompletionHandler:^(CKSubscription*subscription,NSError*error){//...}];

Receiving the notification is handled by the application delegate:

funcapplication(application:UIApplication,didReceiveRemoteNotificationuserInfo:[NSObject:AnyObject]){letckNotification=CKNotification(fromRemoteNotificationDictionary:userInfoas![String:NSObject])ifckNotification.notificationType==.Query,letqueryNotification=ckNotificationas?CKQueryNotification{letrecordID=queryNotification.recordID//...}}
-(void)application:(UIApplication*)applicationdidReceiveRemoteNotification:(NSDictionary*)userInfo{CKNotification*ckNotification=[CKNotificationnotificationFromRemoteNotificationDictionary:userInfo];if(ckNotification.notificationType==CKNotificationTypeQuery){CKQueryNotification*queryNotification=ckNotification;CKRecordID*recordID=[queryNotificationrecordID];// ...}}

More

As I said in the beginning, CloudKit can do much more than described in this article. You can allow your users to add pictures to their check-ins. References in CloudKit allow you to get all the related check-ins for certain places. Moreover, CloudKit has an API that allows you to find your users' friends who are also using your application via their address book.

Can't wait to try out CloudKit? It could free you from writing backend code, caring about server pressure, maintaining a large CDN network, renting a server, and more. But wait—what about the price? How much does it cost? The answer is: free. Apple allows using CloudKit for 10 GB of resource storage, 100 MB of data storage, and 2 GB of daily transfer, scaling with your user base up to to 1 petabyte of resources, 10 TB database, and 200 TB transfer.

Check out the CloudKit cost calculator at the bottom of the page for detailed free limits and pricing.


As of WWDC 2015, CloudKit is not only available on iOS or OS X. You can now integrate CloudKit JS with your website to make it possible for iCloud users to enjoy your service in a web browser or use the CloudKit web service to communicate with CloudKit servers directly via HTTP request. All this means it's now possible to use CloudKit from any other mobile or desktop platform!

CloudKit is an amazing thing. I can't wait to see the awesome applications you NSHipsters make with it.

UIKeyCommand

$
0
0

Adding a new feature to a product is always a tradeoff. Will the added utility of a new feature be enough to offset the added complexity? Shortcuts would seem to side-step this issue—after all, they're simply a quicker alternative for features already in your app. But that creates another dilemma: what if a new feature is added and no one knows it's there?

When key commands for external keyboards debuted in iOS 7, there was no intrinsic way to learn of their existence. Unlike in OS X, where a user can gradually discover shortcuts for the menu items they use most often, an iOS app had few ways to communicate what key commands are available. Initial tours flash by and fade from memory; help screens are hidden out of sight. Without a way to make shortcuts visible in a timely and relevant manner, users were sure to miss out on useful features that developers had taken the time to implement.

No longer. As part of the push for greater productivity on the iPad, iOS 9 adds Discoverability, an overlay showing the currently available key commands inside an app. This small change suddenly makes key commands far more viable on the iPad and, with it, makes UIKeyCommand a necessary addition to your app.


UIKeyCommand

The UIKeyCommand class is in fact quite simple, with only four properties to configure:

  • input: The character of the key you'd like to recognize, or the correct constant for the arrow and escape keys, which do not have characters themselves. The available constants are:

    • UIKeyInputUpArrow
    • UIKeyInputDownArrow
    • UIKeyInputLeftArrow
    • UIKeyInputRightArrow
    • UIKeyInputEscape
  • modifierFlags: One or more UIKeyModifierFlags, describing the modifier keys that should be pressed in combination with input:

    • .Command, .Alternate, .Shift, .Control: The Command, Option, Shift, and Control keys, respectively.
    • .NumericPad: Indicates that input should come from the numeric keypad rather than the top row of the standard keyboard.
    • .AlphaShift: Indicates that the CapsLock key should be pressed as part of the combination, rather than just engaged.
  • action: The selector to call when the key command is invoked, called with a UIKeyCommand as its only argument. The key event will travel up the responder chain until a matching selector is found.

  • discoverabilityTitle(iOS 9 only): An optional label to display for the key command in the Discoverability layover. Only key commands with a title set will be listed.

Responding to Key Commands

Enabling key commands is as simple as providing an array of UIKeyCommand instances somewhere in the responder chain. Text inputs are automatically first responders, but perhaps more usefully, a view controller can respond to key commands by implementing canBecomeFirstResponder():

overridefunccanBecomeFirstResponder()->Bool{returntrue}
-(BOOL)canBecomeFirstResponder{returnYES;}

Next, provide a list of available key commands via the keyCommands property:

overridevarkeyCommands:[UIKeyCommand]?{return[UIKeyCommand(input:"1",modifierFlags:.Command,action:"selectTab:",discoverabilityTitle:"Types"),UIKeyCommand(input:"2",modifierFlags:.Command,action:"selectTab:",discoverabilityTitle:"Protocols"),UIKeyCommand(input:"3",modifierFlags:.Command,action:"selectTab:",discoverabilityTitle:"Functions"),UIKeyCommand(input:"4",modifierFlags:.Command,action:"selectTab:",discoverabilityTitle:"Operators"),UIKeyCommand(input:"f",modifierFlags:[.Command,.Alternate],action:"search:",discoverabilityTitle:"Find…"),]}// ...funcselectTab(sender:UIKeyCommand){letselectedTab=sender.input// ...}
-(NSArray<UIKeyCommand*>*)keyCommands{return@[[UIKeyCommandkeyCommandWithInput:@"1"modifierFlags:UIKeyModifierCommandaction:@selector(selectTab:)discoverabilityTitle:@"Types"],[UIKeyCommandkeyCommandWithInput:@"2"modifierFlags:UIKeyModifierCommandaction:@selector(selectTab:)discoverabilityTitle:@"Protocols"],[UIKeyCommandkeyCommandWithInput:@"3"modifierFlags:UIKeyModifierCommandaction:@selector(selectTab:)discoverabilityTitle:@"Functions"],[UIKeyCommandkeyCommandWithInput:@"4"modifierFlags:UIKeyModifierCommandaction:@selector(selectTab:)discoverabilityTitle:@"Operators"],[UIKeyCommandkeyCommandWithInput:@"f"modifierFlags:UIKeyModifierCommand|UIKeyModifierAlternateaction:@selector(search:)discoverabilityTitle:@"Find…"]];}// ...-(void)selectTab:(UIKeyCommand*)sender{NSString*selectedTab=sender.input;// ...}

In the Discoverability layover, accessed by holding down the Command key, key commands are listed in the order you specified:

Discoverability Layover

Voila! Secrets, revealed!

Context Sensitivity

The keyCommands property is accessed whenever a key pressed, making it possible to provide context-sensitive responses depending on the state of your application. While this is similar to the way a menu item and its active/inactive state are configured in OS X, the recommendation for iOS is to omit inactive commands completely—that is, there are no grayed out commands in the Discoverability layover.

Here, a set of commands that are available to logged in users of an app are included only when appropriate:

letglobalKeyCommands=[UIKeyCommand(input:...),...]letloggedInUserKeyCommands=[UIKeyCommand(input:...),...]overridevarkeyCommands:[UIKeyCommand]?{ifisLoggedInUser(){returnglobalKeyCommands+loggedInUserKeyCommands}else{returnglobalKeyCommands}}

Although we don't take shortcuts when creating our apps, that doesn't mean our users won't find shortcuts useful. Adding key commands lets control of your app shift from the screen to the keyboard—your users will love the option.

guard & defer

$
0
0

“We should do (as wise programmers aware of our limitations) our utmost best to … make the correspondence between the program (spread out in text space) and the process (spread out in time) as trivial as possible.”

Edsger W. Dijkstra, “Go To Considered Harmful”

Recently, Swift 2.0 introduced two new control statements that aim to simplify and streamline the programs we write: guard and defer. While the first by its nature makes our code more linear, the other defers execution of its contents. How should we approach these new control statements? How can guard and defer help us clarify the correspondence between the program and the process?

Let’s defer defer and first take on guard.


guard

If the multiple optional bindings syntax introduced in Swift 1.2 heralded a renovation of the pyramid of doom, guard statements tear it down altogether.

guard is a new conditional statement that requires execution to exit the current block if the condition isn’t met. Any new optional bindings created in a guard statement’s condition are available for the rest of the function or block, and the mandatory else must exit the current scope, by using return to leave a function, continue or break within a loop, or a @noreturn function like fatalError():

forimageNameinimageNamesList{guardletimage=UIImage(named:imageName)else{continue}// do something with image}

Let’s take a before-and-after look at how guard can improve our code and help prevent errors. As an example, we’ll build a new string-to-UInt8 initializer. UInt8 already declares a failable initializer that takes a String, but if the conversion fails we don’t learn the reason—was the format invalid or was the value out of bounds for the numeric type? Our new initializer throws a ConversionError that provides more information.

enumConversionError:ErrorType{caseInvalidFormat,OutOfBounds,Unknown}extensionUInt8{init(fromStringstring:String)throws{// check the string's formatiflet_=string.rangeOfString("^\\d+$",options:[.RegularExpressionSearch]){// make sure the value is in boundsifstring.compare("\(UInt8.max)",options:[.NumericSearch])!=NSComparisonResult.OrderedAscending{throwConversionError.OutOfBounds}// do the built-in conversionifletvalue=UInt8(string){self.init(value)}else{throwConversionError.Unknown}}throwConversionError.InvalidFormat}}

Note how far apart the format check and the invalid format throw are in this example. Not ideal. Moreover, the actual initialization happens two levels deep, inside a nested if statement. And if that isn’t enough, there’s a bug in the logic of this initializer that isn’t immediately apparent. Can you spot the flaw? What’s really going to bake your noodle later on is, would you still have noticed it if I hadn’t said anything?

Next, let’s take a look at how using guard transforms this initializer:

extensionUInt8{init(fromStringstring:String)throws{// check the string's formatguardlet_=string.rangeOfString("^\\d+$",options:[.RegularExpressionSearch])else{throwConversionError.InvalidFormat}// make sure the value is in boundsguardstring.compare("\(UInt8.max)",options:[.NumericSearch])!=NSComparisonResult.OrderedDescendingelse{throwConversionError.OutOfBounds}// do the built-in conversionguardletvalue=UInt(string)else{throwConversionError.Unknown}self.init(value)}}

Much better. Each error case is handled as soon as it has been checked, so we can follow the flow of execution straight down the left-hand side.

Even more importantly, using guard prevents the logic flaw in our first attempt: that final throw is called every time because it isn’t enclosed in an else statement. With guard, the compiler forces us to break scope inside the else-block, guaranteeing the execution of that particular throw only at the right times.

Also note that the middle guard statement isn’t strictly necessary. Since it doesn’t unwrap an optional value, an if statement would work perfectly well. Using guard in this case simply provides an extra layer of safety—the compiler ensures that you leave the initializer if the test fails, leaving no way to accidentally comment out the throw or introduce another error that would lose part of the initializer’s logic.

defer

Between guard and the new throw statement for error handling, Swift 2.0 certainly seems to be encouraging a style of early return (an NSHipster favorite) rather than nested if statements. Returning early poses a distinct challenge, however, when resources that have been initialized (and may still be in use) must be cleaned up before returning.

The new defer keyword provides a safe and easy way to handle this challenge by declaring a block that will be executed only when execution leaves the current scope. Consider this snippet of a function working with vImage from the Accelerate framework, taken from the newly-updated article on image resizing:

funcresizeImage(url:NSURL)->UIImage?{// ...letdataSize:Int=...letdestData=UnsafeMutablePointer<UInt8>.alloc(dataSize)vardestBuffer=vImage_Buffer(data:destData,...)// scale the image from sourceBuffer to destBuffervarerror=vImageScale_ARGB8888(&sourceBuffer,&destBuffer,...)guarderror==kvImageNoErrorelse{destData.dealloc(dataSize)// 1returnnil}// create a CGImage from the destBufferguardletdestCGImage=vImageCreateCGImageFromBuffer(&destBuffer,&format,...)else{destData.dealloc(dataSize)// 2returnnil}destData.dealloc(dataSize)// 3// ...}

Here an UnsafeMutablePointer<UInt8> is allocated for the destination data early on, but we need to remember to deallocate at both failure points and once we no longer need the pointer.

Error prone? Yes. Frustratingly repetitive? Check.

A defer statement removes any chance of forgetting to clean up after ourselves while also simplifying our code. Even though the defer block comes immediately after the call to alloc(), its execution is delayed until the end of the current scope:

funcresizeImage(url:NSURL)->UIImage?{// ...letdataSize:Int=...letdestData=UnsafeMutablePointer<UInt8>.alloc(dataSize)defer{destData.dealloc(dataSize)}vardestBuffer=vImage_Buffer(data:destData,...)// scale the image from sourceBuffer to destBuffervarerror=vImageScale_ARGB8888(&sourceBuffer,&destBuffer,...)guarderror==kvImageNoErrorelse{returnnil}// create a CGImage from the destBufferguardletdestCGImage=vImageCreateCGImageFromBuffer(&destBuffer,&format,...)else{returnnil}// ...}

Thanks to defer, destData will be properly deallocated no matter which exit point is used to return from the function.

Safe and clean. Swift at its best.

defer blocks are executed in the reverse order of their appearance. This reverse order is a vital detail, ensuring everything that was in scope when a deferred block was created will still be in scope when the block is executed.

(Any Other) Defer Considered Harmful

As handy as the defer statement is, be aware of how its capabilities can lead to confusing, untraceable code. It may be tempting to use defer in cases where a function needs to return a value that should also be modified, as in this typical implementation of the postfix ++ operator:

postfixfunc++(inoutx:Int)->Int{letcurrent=xx+=1returncurrent}

In this case, defer offers a clever alternative. Why create a temporary variable when we can just defer the increment?

postfixfunc++(inoutx:Int)->Int{defer{x+=1}returnx}

Clever indeed, yet this inversion of the function’s flow harms readability. Using defer to explicitly alter a program’s flow, rather than to clean up allocated resources, will lead to a twisted and tangled execution process.


“As wise programmers aware of our limitations,” we must weigh the benefits of each language feature against its costs. A new statement like guard leads to a more linear, more readable program; apply it as widely as possible. Likewise, defer solves a significant challenge but forces us to keep track of its declaration as it scrolls out of sight; reserve it for its minimum intended purpose to guard against confusion and obscurity.

Reader Submissions - New Year's 2016

$
0
0

With 2015 behind us and the new year begun, it’s time again for an NSHipster tradition: reader submissions! As inyear’spast, this installment is chock full of tips and tricks that can help ease your days working with Xcode, Swift, and Objective-C.

Many thanks to Cédric Luthi, Josip Ćavar, Juraj Hilje, Kyle Van Essen, Luo Jie, Mathew Huusko V, Nicolás Jakubowski, Nolan O'Brien, Orta Therox, Ray Fix, Stephen Celis, Taylor Franklin, Ursu Dan, Matthew Flint, @biggercoffee, and @vlat456 for their contributions!


Swift’s defer in Objective-C

From Nolan O'Brien:

With the excellent addition of defer to Swift we Objective-C holdouts can’t help but feel envious of the improvements happening so rapidly to the Swift language. Until Apple officially adds @defer devs can actually implement defer support simply enough with a macro in Objective-C. Below I’ve outlined how one can go about doing a defer today in Objective-C. Personally, having Apple add @defer seem like an easy win for Objective-C, but we’ll see what happens. :)

Nolan’s macro uses the GCC (cleanup()) attribute to execute a block when scope exits:

// some helper declarations#define _nob_macro_concat(a, b) a##b#define nob_macro_concat(a, b) _nob_macro_concat(a, b)typedefvoid(^nob_defer_block_t)();NS_INLINEvoidnob_deferFunc(__strongnob_defer_block_t*blockRef){nob_defer_block_tactualBlock=*blockRef;actualBlock();}// the core macro#define nob_defer(deferBlock) \__strong nob_defer_block_t nob_macro_concat(__nob_stack_defer_block_, __LINE__) __attribute__((cleanup(nob_deferFunc), unused)) = deferBlock

Blocks used with nob_defer are executed in reverse order, just like Swift defer statements:

#include <nob_defer.h>-(void)dealWithFile{FILE*file=fopen();nob_defer(^{if(file){fclose(file);}});// continue code where any scope exit will // lead to the defer being executed}-(void)dealWithError{__blockNSError*scopeError=nil;nob_defer(^{if(scopeError){[selfperformCustomErrorHandling:scopeError];}});// assign any errors to "scopeError" to handle the error// on exit, no matter how you exit}#define NOBDeferRelease(cfTypeRef) nob_defer(^{ if (cfTypeRef) { CFRelease(cfTypeRef); } })-(void)cleanUpCFTypeRef{CFStringRefstringRef=...somecodetocreateaCFStringRef...;NOBDeferRelease(stringRef);// continue working without having to worry// about the CFTypeRef needing to be released}

I’ve been using my custom defer macro in production code since June and it is really the Bee’s Knees!


Swift Protocol Extensions

From Juraj Hilje:

Keep inheritance trees shallow and use protocol composition:

protocolHello{funcsayHello()->String}extensionHello{funcsayHello()->String{return"Hello, stranger"}}classMyClass:Hello{}letc=MyClass()c.sayHello()// "Hello, stranger"

Public Read-only Variables

From Stephen Celis:

Classes commonly have public, read-only properties but need the ability privately modify them. I’ve come across the following pattern a few times:

publicclassPerson{publicvarname:String{return_name}privatevar_name:String// ...}

Luckily, there’s a better, oft-overlooked way that avoids the extra variable:

publicclassPerson{publicprivate(set)varname:String// ...}

Swift where Everywhere

From Taylor Franklin:

The addition of the where clause has made my code simple and compact while remaining readable. In addition, it has a broad application in Swift, so that it can be applied in nearly any kind of control-flow statement, such as for loop, while loop, if, guard, switch, and even in extension declarations. One simple way I like to use it is in my prepareForSegue method:

ifletsegueID=segue.identifierwheresegueID=="mySegue"{...}

The combo of unwrapping and performing a condition check is most commonly where I use the where clause. The where clause is not going to change your life, but it should be an easy and useful addition to your Swift skills.


Improved Optional Binding

From Ursu Dan:

The improved optional binding in Swift is amazing and I use it virtually everywhere now and avoid the pyramid of doom:

ifleturl=NSBundle.mainBundle().URLForResource("users",withExtension:"json"),data=NSData(contentsOfURL:url),deserialized=try?NSJSONSerialization.JSONObjectWithData(data,options:[]),userList=deserializedas?[[String:AnyObject]]{foruserDictinuserList{ifletid=userDict["id"]as?Int,name=userDict["name"]as?String,username=userDict["username"]as?String,email=userDict["email"]as?String,phone=userDict["phone"]as?String,address=userDict["address"]as?[String:AnyObject]{users.append(User(id:id,name:name,...))}}}

Unbuffered xcodebuild Output

From Cédric Luthi:

Using xcpretty because the output of xcodebuild test is unreadable? Unfortunately, the output of the test results becomes buffered when piped. Solution: set the NSUnbufferedIO environment variable for a smooth experience. 😎

env NSUnbufferedIO=YES xcodebuild [flags]| xcpretty

Multiline Labels in a Table View

From Ray Fix:

Using autolayout to toggle a label in a table view from one line to many:

tableView.beginUpdates()label.numberOfLines=label.numberOfLines==0?1:0tableView.endUpdates()

You can see Ray’s technique in action in an example project:

Multiline demo


AmIRunningAsAnExtension

Another from Nolan O'Brien:

With extensions in iOS, it is critical that frameworks that can be linked to both extensions and apps be cognizant of their uses so they don’t call any APIs that might not be available to an extension (like UIApplication). Here’s a function to help determine if you are running in an extension at runtime:

(Per Apple, an extension will have a top level “NSExtension” dictionary in the info.plist.)

letNOBAmIRunningAsAnExtension:Bool={letextensionDictionary:AnyObject?=NSBundle.mainBundle().infoDictionary?["NSExtension"]returnextensionDictionary?.isKindOfClass(NSDictionary.self)??false}()
FOUNDATION_EXTERNBOOLAmIRunningAsAnExtension()__attribute__((const));BOOLNOBAmIRunningAsAnExtension(){staticBOOLsIsExtension;staticdispatch_once_tsOnceToken;dispatch_once(&sOnceToken,^{NSDictionary*extensionDictionary=[[NSBundlemainBundle]infoDictionary][@"NSExtension"];sIsExtension=[extensionDictionaryisKindOfClass:[NSDictionaryclass]];});returnsIsExtension;}

That frees you to do things like this:

-(void)startBackgroundTask{#if TARGET_OS_IPHONEif(!NOBAmIRunningAsAnExtension()){ClassUIApplicationClass=NSClassFromString(@"UIApplication");idsharedApplication=[UIApplicationClasssharedApplication];self.backgroundTaskIdentifier=[sharedApplicationbeginBackgroundTaskWithExpirationHandler:^{if(self.backgroundTaskIdentifier!=UIBackgroundTaskInvalid){[sharedApplicationendBackgroundTask:self.backgroundTaskIdentifier];self.backgroundTaskIdentifier=UIBackgroundTaskInvalid;}}];}#endif}-(void)endBackgroundTask{#if TARGET_OS_IPHONEif(self.backgroundTaskIdentifier!=UIBackgroundTaskInvalid){NSAssert(!NOBAmIRunningAsAnExtension());ClassUIApplicationClass=NSClassFromString(@"UIApplication");idsharedApplication=[UIApplicationClasssharedApplication];[sharedApplicationendBackgroundTask:self.backgroundTaskIdentifier];self.backgroundTaskIdentifier=UIBackgroundTaskInvalid;}#endif}

Beyond Breakpoints

From Matthew Flint:

This has been my year of truly appreciating Xcode breakpoints, beyond the standard “break at this line” type. It breaks my heart to see other developers not using them to their potential.

Right click on a breakpoint and choose Edit Breakpoint… for access to advanced features:

Breakpoint Options Popup

Particularly the ones with actions (such as logging to the console) that continue automatically without pausing any threads, because you can add/edit them without recompiling. I’ll never accidentally commit NSLogs again. :)


Fix Console po frame Printing

GitHub user @biggercoffee reminds us that po frame printing fails in the LLDB console by default:

Broken po frame

Fix it mid-debugging session with expr @import UIKit, or fix it once and for all by adding a couple lines to your “.lldbinit”. From the command line:

touch ~/.lldbinit
echo display @import UIKit >> ~/.lldbinit
echo target stop-hook add -o \"target stop-hook disable\">> ~/.lldbinit

Fixed po frame


Avoiding -DDEBUG in Swift

From GitHub user @vlat456:

For those, who like me, are trying to avoid the mess with -DDEBUG in Swift, but have to know which version of executable is running, Debug or Release.

// PreProcessorMacros.m:#include "PreProcessorMacros.h"#ifdef DEBUGBOOLconstDEBUG_BUILD=YES;#elseBOOLconstDEBUG_BUILD=NO;#endif// PreProcessorMacros.h:#ifndef PreProcessorMacros_h#define PreProcessorMacros_h#includeexternBOOLconstDEBUG_BUILD;#endif /* PreProcessorMacros_h */// in Bridged header:#import "PreProcessorMacros.h"

And then from Swift:

ifDEBUG_BUILD{debugPrint("It's Debug build")}else{debugPrint("It's Release build")}

Checking For Null Blocks

From Nicolás Jakubowski:

This macro for checking block nullability before executing them:

#define BLOCK_EXEC(block, ...) if (block) { block(__VA_ARGS__); };

Old and busted:

if(completionBlock){completionBlock(arg1,arg2);}

New and shiny:

BLOCK_EXEC(completionBlock,arg1,arg2);

Swiftier GCD

From Luo Jie:

You can use enums and protocol extensions to provide a GCD convenience API:

protocolExcutableQueue{varqueue:dispatch_queue_t{get}}extensionExcutableQueue{funcexecute(closure:()->Void){dispatch_async(queue,closure)}}enumQueue:ExcutableQueue{caseMaincaseUserInteractivecaseUserInitiatedcaseUtilitycaseBackgroundvarqueue:dispatch_queue_t{switchself{case.Main:returndispatch_get_main_queue()case.UserInteractive:returndispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE,0)case.UserInitiated:returndispatch_get_global_queue(QOS_CLASS_USER_INITIATED,0)case.Utility:returndispatch_get_global_queue(QOS_CLASS_UTILITY,0)case.Background:returndispatch_get_global_queue(QOS_CLASS_BACKGROUND,0)}}}enumSerialQueue:String,ExcutableQueue{caseDownLoadImage="myApp.SerialQueue.DownLoadImage"caseUpLoadFile="myApp.SerialQueue.UpLoadFile"varqueue:dispatch_queue_t{returndispatch_queue_create(rawValue,DISPATCH_QUEUE_SERIAL)}}

Downloading something then could be written like this:

Queue.UserInitiated.execute{leturl=NSURL(string:"http://image.jpg")!letdata=NSData(contentsOfURL:url)!letimage=UIImage(data:data)Queue.Main.execute{imageView.image=image}}

_ObjectiveCBridgeable

From Mathew Huusko V:

Using Swift’s _ObjectiveCBridgeable (implicit castability between types) to create a generic protocol for Obj-C compatible objects that wrap pure Swift structs (keeping Swift framework API clean, but Obj-C compatible for as long as desired).

This first part defines an extension with default implementations for the bridging bookkeeping methods:

publicprotocolBackedObjectType:AnyObject{typealiasBackingvarbackingObject:Backing{get}init(_backingObject:Backing)}publicprotocolObjectBackable:_ObjectiveCBridgeable{typealiasBacked:BackedObjectType}publicextensionObjectBackablewhereBacked.Backing==Self{staticfunc_isBridgedToObjectiveC()->Bool{returntrue}staticfunc_getObjectiveCType()->Any.Type{returnBacked.self}func_bridgeToObjectiveC()->Backed{returnBacked(self)}staticfunc_forceBridgeFromObjectiveC(source:Backed,inoutresult:Self?){result=source.backingObject}staticfunc_conditionallyBridgeFromObjectiveC(source:Backed,inoutresult:Self?)->Bool{_forceBridgeFromObjectiveC(source,result:&result)returntrue}functoBridgedObject()->Backed{return_bridgeToObjectiveC()}}

Here the Swift struct SomeModel and Objective-C class M5SomeModel are declared and bridged. Bridging between them is accomplished with an as cast:

publicstructSomeModel{publicletID:Intpublicletname:Stringpublicletcategory:String}extensionSomeModel:ObjectBackable{publictypealiasBacked=M5SomeModel}@objcpublicfinalclassM5SomeModel:NSObject,BackedObjectType{publicletbackingObject:SomeModelpublicinit(_backingObject:SomeModel){self.backingObject=backingObject}publicvarID:Int{returnbackingObject.ID}publicvarname:String{returnbackingObject.name}publicvarcategory:String{returnbackingObject.category}}// Usage:letmodel=SomeModel(ID:2,name:"awesome",category:"music")letobjcCompatibleModel=modelasM5SomeModelletoriginalModel=objcCompatibleModelasSomeModel

Phantom Types

Josip Ćavar writes in about getting additional type safety with phantom types. In the example below, Kilometer and Meter are used to constrain what kinds of DistanceT instances can be added together:

structKilometer{}structMeter{}structDistanceT<T>{privateletvalue:Intinit(value:Int){self.value=value}}func+<T>(left:DistanceT<T>,right:DistanceT<T>)->DistanceT<T>{returnDistanceT(value:left.value+right.value)}extensionInt{varkm:DistanceT<Kilometer>{returnDistanceT<Kilometer>(value:self)}varm:DistanceT<Meter>{returnDistanceT<Meter>(value:self)}}letdistanceKilometers=5.kmletdistanceMeters=15.mletnewDistance=distanceKilometers+distanceKilometers// OkletnewDistance=distanceKilometers+distanceMeters// Compiler error

Easier Configuration

From Kyle Van Essen by way of Orta Therox comes a function that streamlines multi-step initialization and configuration processes.

@warn_unused_resultpublicfuncInit<Type>(value:Type,@noescapeblock:(object:Type)->Void)->Type{block(object:value)returnvalue}funcexample(){letlabel=Init(UILabel()){$0.font=UIFont.boldSystemFontOfSize(13.0)$0.text="Hello, World"$0.textAlignment=.Center}}

Well, that rounds out this year’s list—thanks again to all who contributed!

Happy New Year! May your code continue to compile and inspire.

UITextChecker

$
0
0

Make no mistake, a tiny keyboard on a slab of glass doesn’t always lend itself to perfect typing. Whether for accuracy or hilarity, anyone typing on an iOS device notices when autocorrect steps in to help out. You might not know, however, that UIKit includes a class to help you with your user’s typing inside your app.

First introduced in iOS 3.2 (or should we call it iPhone OS 3.2, given the early date?), UITextChecker does exactly what it says: it checks text. Read on to learn how you can use this class for spell checking and text completion.


Spell Checking

What happens if you mistype a word in iOS? Type “hipstar” into a text field and iOS will offer to autocorrect to “hipster” most of the time.

Autocorrecting 'hipstar'

We can find the same suggested substitution using UITextChecker:

importUIKitletstr="hipstar"lettextChecker=UITextChecker()letmisspelledRange=textChecker.rangeOfMisspelledWordInString(str,range:NSRange(0..<str.utf16.count),startingAt:0,wrap:false,language:"en_US")ifmisspelledRange.location!=NSNotFound,letguesses=textChecker.guessesForWordRange(misspelledRange,inString:str,language:"en_US")as?[String]{print("First guess: \(guesses.first)")// First guess: hipster}else{print("Not found")}
NSString*str=@"hipstar";UITextChecker*textChecker=[[UITextCheckeralloc]init];NSRangemisspelledRange=[textCheckerrangeOfMisspelledWordInString:strrange:NSMakeRange(0,[strlength])startingAt:0wrap:NOlanguage:@"en_US"];NSArray*guesses=[NSArrayarray];if(misspelledRange.location!=NSNotFound){guesses=[textCheckerguessesForWordRange:misspelledRangeinString:strlanguage:@"en_US"];NSLog(@"First guess: %@",[guessesfirstObject]);// First guess: hipster}else{NSLog(@"Not found");}

The returned array of strings might look like this one:

["hipster","hip star","hip-star","hips tar","hips-tar"]

Or it might not—UITextChecker produces context- and device-specific guesses. According to the documentation, guessesForWordRange(_:inString:language:)“returns an array of strings, in the order in which they should be presented, representing guesses for words that might have been intended in place of the misspelled word at the given range in the given string.”

So no guarantee of idempotence or correctness, which makes sense for a method with guesses... in the name. How can NSHipsters trust a method that changes its return value? We’ll find the answer if we dig further.

Learning New Words

Let’s assume that you want your users to be able to type "hipstar" exactly. Let your app know that by telling it to learn the word, using the UITextChecker.learnWord(_:) class method:

UITextChecker.learnWord(str)
[UITextCheckerlearnWord:str];

"hipstar" is now a recognized word for the whole device and won’t show up as misspelled in further checks.

letmisspelledRange=textChecker.rangeOfMisspelledWordInString(str,range:NSRange(0..<str.utf16.count),startingAt:0,wrap:false,language:"en_US")// misspelledRange.location == NSNotFound
NSRangemisspelledRange=[textCheckerrangeOfMisspelledWordInString:strrange:NSMakeRange(0,[strlength])startingAt:0wrap:NOlanguage:@"en_US"];// misspelledRange.location == NSNotFound

As expected, the search above returns NSNotFound, for UITextChecker has learned the word we created. UITextChecker also provides class methods for checking and unlearning words: UITextChecker.hasLearnedWord(_:) and UITextChecker.unlearnWord(_:).

Suggesting Completions

There’s one more UITextChecker API, this time for finding possible completions for a partial word:

letpartial="hipst"letcompletions=textChecker.completionsForPartialWordRange(NSRange(0..<partial.utf16.count),inString:partial,language:"en_US")// completions == ["hipster", "hipsters"]
NSString*partial=@"hipst";NSArray*completions=[textCheckercompletionsForPartialWordRange:NSMakeRange(0,[partiallength])inString:partiallanguage:@"en_US"];// completions == ["hipster", "hipsters"]

completionsForPartialWordRange gives you an array of possible words from a group of initial characters. Although the documentation states that the returned array of strings will be sorted by probability, UITextChecker only sorts the completions alphabetically. UITextChecker’s OS X-based sibling, NSSpellChecker, does behave as it describes.

You won’t see any of the custom words you’ve taught UITextChecker show up as possible completions. Why not? Since vocabulary added via UITextChecker.learnWord(_:) is global to the device, this prevents your app’s words from showing up in another app’s autocorrections.


Building an app that leans heavily on a textual interface? Use UITextChecker to make sure the system isn’t flagging your own vocabulary. Writing a keyboard extension? With UITextChecker and UILexicon, which provides common and user-defined words from the system-wide dictionary and first and last names from the user’s address book, you can support nearly any language without creating your own dictionaries!

NSHipster Quiz #8

$
0
0

This year’s WWDC edition of the NSHipster Pub Quiz was held on June 14th, once again testing the assembled developers with questions both random and obscure. We’re enormously grateful to Realm, who hosted the quiz for the second year in a row, with delicious food and drink and enough tables to seat nearly two hundred contestants.

Competition was as fierce as always. Laughs and groans were heard. And after the points were tallied, team “Hey Siri” won the evening and the mustache medallions with a score of 31 out of a possible 43 points. A hearty congratulations to Alek Åström, Cezary Wojcik, Kyle Sherman, Marcus Brissman, Marius Rackwitz, Melissa Huang, Nevyn Bengtsson, and Rob Stevenson!

Now it’s time for you to play along with the home edition—sharpen your pencil and give it your best!

  • Four rounds of ten questions
  • Record your answers on a separate sheet of paper
  • Each correct answer earns 1 point (unless otherwise specified)
  • Play with friends for maximum enjoyment
  • Don’t be lame and look things up on the internet or in Xcode

Round 1: General Knowledge

  1. In the WWDC keynote, Apple introduced the new OS X, er… macOS Sierra. The actual Sierra mountain range is home to the highest peak in the contiguous US. What is the name of that mountain?
  2. The Sierra were one focal point of a mass migration to California. What San Francisco sports team has ties to the Sierra during that period in history?
  3. Another highlight of the keynote was when Bozoma Saint John introduced the new Apple Music and got the crowd singing along to “Rapper’s Delight"—who recorded the song, and in what year? (2 points)
  4. Which version of iPhoto first introduced “Faces and Places?”
  5. As part of Foundation’s Swiftification, many classes have lost their NS prefixes. Which of these classes remains unchanged so far: NSBundle, NSCalendar, NSExpression, or NSOperation?
  6. More than just class names have changed—write the new Swift signature for this NSString method:

    funcstringByReplacingCharactersInRange(_range:NSRange,        withStringreplacement:String)->String
  7. Write the Swift 3 code to execute an asynchronous “Hello, world!” using GCD.

  8. Swift went open source in November and the pace of community contributions has been amazing to see. Within 100, how many pull requests (open, closed, or merged) has the Swift project received on GitHub?

  9. Swift was released to the public just over two years ago, but was clearly under long before that at Apple. What were the month and year of the first commit to the Swift repository?

  10. Who was the second contributor to Swift? When did they begin?

Round 2: Name That Framework

Foundation classes are losing their NS prefixes left and right. What would it look like if we got rid of prefixes in every framework? For each question in this round, you’ll be given three classes with their identifying prefix removed. Name the framework that contains all three.

  1. Statistic, Sample, Correlation
  2. CallObserver, Transaction, Provider
  3. Visit, Heading, Region
  4. Conversation, Session, Sticker
  5. IndexSet, ValueTransformer, Scanner
  6. Participant, Reminder, StructuredLocation
  7. Circle, LocalSearch, GeodesicPolyline
  8. LabeledValue, PhoneNumber, SocialProfile
  9. Quadtree, NoiseSource, MonteCarloStrategist
  10. RideStatus, PaymentMethod, CarAirCirculationModeResolutionResult

Round 3: Who Is That?

Many Apple advertisements over the years have featured celebrity voiceovers intoning words of wisdom, inspiration, or at times something else entirely. So pop in your earbuds and for each of the ads below, name the person(s) providing their voice talents.

Round 4: Easy as 1, 2, 3

Swift is an easy language to learn and use, but its breakneck speed of development has meant breaking changes with each release. For the following snippets of code, answer with the version of Swift that will compile and give the desired result. Because some snippets can run in more than one version, some questions may be worth up to 2 points. Only the major versions are required—for example, if a snippet will run in Swift 2.2, "Swift 2” is a scoring answer.

1

leta=["1","2","3","four","5"]letnumbers=map(a){$0.toInt()}letonlyNumbers=filter(numbers){$0!=nil}letsum=reduce(onlyNumbers,0){$0+$1!}// sum == 11

2

leta=["1","2","3","four","5"]letsum=a.flatMap{Int($0)}.reduce(0,combine:+)// sum == 11

3

vara=[8,6,7,5,3,0,9]a.sort()print(a)// [0, 3, 5, 6, 7, 8, 9]

4

vara=[8,6,7,5,3,0,9]sort(a)print(a)// [0, 3, 5, 6, 7, 8, 9]

5

vara=[8,6,7,5,3,0,9]a.sort()print(a)// [8, 6, 7, 5, 3, 0, 9]

6

foriinstride(from:3,to:10,by:3){print(i)}// 3// 6// 9

7

foriin3.stride(to:10,by:3){print(i)}// 3// 6// 9

8

enumMyError:ErrorProtocol{caseOverflowcaseNegativeInput}funcsquare(_value:inoutInt)throws{guardvalue>=0else{throwMyError.NegativeInput}let(result,overflow)=Int.multiplyWithOverflow(value,value)guard!overflowelse{throwMyError.Overflow}value=result}varnumber=11try!square(&number)// number == 121

9

enumMyError:ErrorType{caseOverflowcaseNegativeInput}funcsquareInPlace(inoutvalue:Int)throws{guardvalue>=0else{throwMyError.NegativeInput}let(result,overflow)=Int.multiplyWithOverflow(value,value)guard!overflowelse{throwMyError.Overflow}value=result}varnumber=11try!squareInPlace(&number)// number == 121

10

vara:Int[]=[1,2,3,4,5]letb=aa[0]=100// b == [100, 2, 3, 4, 5]

That’s all! When you’re finished, scroll down a bit for the answers.



.

.

.


Answers

Round 1: General Knowledge

  1. Mount Whitney
  2. San Francisco 49ers
  3. The Sugarhill Gang, 1979 (2 points for both)
  4. iPhoto ’09
  5. NSExpression
  6. One of: “`swift // 1 replacingCharacters(in: NSRange, with: String)

    // 2 func replacingCharacters( in range: NSRange,         with replacement: String) -> String ”`

  7. One of: “`swift // 1 let queue = DispatchQueue(label: "quiz”) queue.async { print(“Hello, world!”) }

    // 2 DispatchQueue.main.async { print(“Hello, world!”) } “`

  8. 3,012 (correct if between 2,912 and 3,112)

  9. July 2010 (1 point if year, 2 if both)

  10. Doug Gregor, July 2011 (2 points)

Round 2: Name That Framework

  1. HealthKit
  2. CallKit
  3. Core Location
  4. Messages
  5. Foundation
  6. EventKit
  7. MapKit
  8. Contacts
  9. GamePlayKit
  10. Intents

Round 3: Who Is That?

  1. Jimmy Fallon & Justin Timberlake (2 points for both)
  2. Martin Scorsese
  3. Jeff Goldblum
  4. Lake Bell
  5. Kiefer Sutherland
  6. Robin Williams
  7. Jony Ive
  8. Jeff Daniels
  9. Richard Dreyfuss
  10. Drunk Jeff Goldblum

Round 4: Easy as 1, 2, 3

If you listed multiple versions, all must be correct for the answer to score.

  1. Swift 1
  2. Swift 2 or 3 (2 points for both)
  3. Swift 3
  4. Swift 1
  5. Swift 2
  6. Swift 1 or 3 (2 points for both)
  7. Swift 2
  8. Swift 3
  9. Swift 2
  10. Initial beta release of Swift

How’d you do? Tweet out your score to see how you stack up to your peers!


NSRegularExpression

$
0
0

“Some people, when confronted with a problem, think ‘I know, I’ll use NSRegularExpression.’ Now they have three problems.”

Regular expressions fill a controversial role in the programming world. Some find them impenetrably incomprehensible, thick with symbols and adornments, more akin to a practical joke than part of a reasonable code base. Others rely on their brevity and their power, wondering how anyone could possibly get along without such a versatile tool in their arsenal.

Happily, on one thing we can all agree. In NSRegularExpression, Cocoa has the most long-winded and byzantine regular expression interface you’re ever likely to come across. Don’t believe me? Let’s try extracting the links from this snippet of HTML, first using Ruby:

htmlSource="Questions? Corrections? <a href=\"https://twitter.com/NSHipster\">@NSHipster</a> or <a href=\"https://github.com/NSHipster/articles\">on GitHub</a>."linkRegex=/<a\s+[^>]*href="([^"]*)"[^>]*>/ilinks=htmlSource.scan(linkRegex)puts(links)# https://twitter.com/NSHipster# https://github.com/NSHipster/articles

Two or three lines, depending on how you count—not bad. Now we’ll try the same thing in Swift using NSRegularExpression:

lethtmlSource="Questions? Corrections? <a href=\"https://twitter.com/NSHipster\">@NSHipster</a> or <a href=\"https://github.com/NSHipster/articles\">on GitHub</a>."letlinkRegexPattern="<a\\s+[^>]*href=\"([^\"]*)\"[^>]*>"letlinkRegex=try!NSRegularExpression(pattern:linkRegexPattern,options:.caseInsensitive)letmatches=linkRegex.matches(in:htmlSource,range:NSMakeRange(0,htmlSource.utf16.count))letlinks=matches.map{result->StringinlethrefRange=result.rangeAt(1)letstart=String.UTF16Index(hrefRange.location)letend=String.UTF16Index(hrefRange.location+hrefRange.length)returnString(htmlSource.utf16[start..<end])!}print(links)// ["https://twitter.com/NSHipster", "https://github.com/NSHipster/articles"]

The prosecution rests.

This article won’t get into the ins and outs of regular expressions themselves (you may need to learn about wildcards, backreferences, lookaheads and the rest elsewhere), but read on to learn about NSRegularExpression, NSTextCheckingResult, and a particularly sticky point when bringing it all together in Swift.


NSString Methods

The simplest way to use regular expressions in Cocoa is to skip NSRegularExpression altogether. The range(of:...) method on NSString (which is bridged to Swift’s native String type) switches into regular expression mode when given the .regularExpression option, so lightweight searches can be written easily:

letsource="For NSSet and NSDictionary, the breaking..."// Matches anything that looks like a Cocoa type: // UIButton, NSCharacterSet, NSURLSession, etc.lettypePattern="[A-Z]{3,}[A-Za-z0-9]+"iflettypeRange=source.range(of:typePattern,options:.regularExpression){print("First type: \(source[typeRange])")// First type: NSSet}
NSString*source=@"For NSSet and NSDictionary, the breaking...";// Matches anything that looks like a Cocoa type: // UIButton, NSCharacterSet, NSURLSession, etc.NSString*typePattern=@"[A-Z]{3,}[A-Za-z0-9]+";NSRangetypeRange=[sourcerangeOfString:typePatternoptions:NSRegularExpressionSearch];if(typeRange.location!=NSNotFound){NSLog(@"First type: %@",[sourcesubstringWithRange:typeRange]);// First type: NSSet}

Replacement is also a snap using replacingOccurrences(of:with:...) with the same option. Watch how we surround each type name in our text with Markdown-style backticks using this one weird trick:

letmarkedUpSource=source.replacingOccurrences(of:typePattern,with:"`$0`",options:.regularExpression)print(markedUpSource)// "For `NSSet` and `NSDictionary`, the breaking...""
NSString*markedUpSource=[sourcestringByReplacingOccurrencesOfString:typePatternwithString:@"`$0`"options:NSRegularExpressionSearchrange:NSMakeRange(0,source.length)];NSLog(@"%@",markedUpSource);// "For `NSSet` and `NSDictionary`, the breaking...""

This approach to regular expressions can even handle subgroup references in the replacement template. Lo, a quick and dirty Pig Latin transformation:

letourcesay=source.replacingOccurrences(of:"([bcdfghjklmnpqrstvwxyz]*)([a-z]+)",with:"$2$1ay",options:[.regularExpression,.caseInsensitive])print(ourcesay)// "orFay etNSSay anday ictionaryNSDay, ethay eakingbray..."
NSString*ourcesay=[sourcestringByReplacingOccurrencesOfString:@"([bcdfghjklmnpqrstvwxyz]*)([a-z]+)"withString:@"$2$1ay"options:NSRegularExpressionSearch|NSCaseInsensitiveSearchrange:NSMakeRange(0,source.length)];NSLog(@"%@",ourcesay);// "orFay etNSSay anday ictionaryNSDay, ethay eakingbray..."

These two methods will suffice for many places you might want to use regular expressions, but for heavier lifting, we’ll need to work with NSRegularExpression itself. First, though, let’s sort out a minor complication when using this class from Swift.

NSRange and Swift

Swift provides a more comprehensive, more complex interface to a string’s characters and substrings than does Foundation’s NSString. The Swift standard library provides four different views into a string’s data, giving you quick access to the elements of a string as characters, Unicode scalar values, or UTF-8 or UTF-16 code units.

How does this relate to NSRegularExpression? Well, many NSRegularExpression methods use NSRanges, as do the NSTextCheckingResult instances that store a match’s data. NSRange, in turn, uses integers for its location and length, while none of String’s views use integers as an index:

letrange=NSRange(location:4,length:5)// Not one of these will compile:source[range]source.characters[range]source.substring(with:range)source.substring(with:range.toRange()!)

Confusion. Despair.

But don’t give up! Everything isn’t as disconnected as it seems—the utf16 view on a Swift String is meant specifically for interoperability with Foundation’s NSString APIs. As long as Foundation has been imported, you can create new indices for a utf16 view directly from integers:

letstart=String.UTF16Index(range.location)letend=String.UTF16Index(range.location+range.length)letsubstring=String(source.utf16[start..<end])!// substring is now "NSSet"

With that in mind, here are a few additions to String that will make straddling the Swift/Objective-C divide a bit easier:

extensionString{/// An `NSRange` that represents the full range of the string.varnsrange:NSRange{returnNSRange(location:0,length:utf16.count)}/// Returns a substring with the given `NSRange`, /// or `nil` if the range can't be converted.funcsubstring(withnsrange:NSRange)->String?{guardletrange=nsrange.toRange()else{returnnil}letstart=UTF16Index(range.lowerBound)letend=UTF16Index(range.upperBound)returnString(utf16[start..<end])}/// Returns a range equivalent to the given `NSRange`,/// or `nil` if the range can't be converted.funcrange(fromnsrange:NSRange)->Range<Index>?{guardletrange=nsrange.toRange()else{returnnil}letutf16Start=UTF16Index(range.lowerBound)letutf16End=UTF16Index(range.upperBound)guardletstart=Index(utf16Start,within:self),letend=Index(utf16End,within:self)else{returnnil}returnstart..<end}}

We’ll put these to use in the next section, where we’ll finally see NSRegularExpression in action.

NSRegularExpression& NSTextCheckingResult

If you’re doing more than just searching for the first match or replacing all the matches in your string, you’ll need to build an NSRegularExpression to do your work. Let’s build a miniature text formatter that can handle *bold* and _italic_ text.

Pass a pattern and, optionally, some options to create a new instance. miniPattern looks for an asterisk or an underscore to start a formatted sequence, one or more characters to format, and finally a matching character to end the formatted sequence. The initial character and the string to format are both captured:

letminiPattern="([*_])(.+?)\\1"letminiFormatter=try!NSRegularExpression(pattern:miniPattern,options:.dotMatchesLineSeparators)// the initializer throws an error if the pattern is invalid
NSString*miniPattern=@"([*_])(.+?)\\1";NSError*error=nil;NSRegularExpression*miniFormatter=[NSRegularExpressionregularExpressionWithPattern:miniPatternoptions:NSRegularExpressionDotMatchesLineSeparatorserror:&error];

The initializer throws an error if the pattern is invalid. Once constructed, you can use an NSRegularExpression as often as you need with different strings.

lettext="MiniFormatter handles *bold* and _italic_ text."letmatches=miniFormatter.matches(in:text,options:[],range:text.nsrange)// matches.count == 2
NSString*text=@"MiniFormatter handles *bold* and _italic_ text.";NSArray<NSTextCheckingResult*>*matches=[miniFormattermatchesInString:textoptions:kNilOptionsrange:NSMakeRange(0,text.length)];// matches.count == 2

Calling matches(in:options:range:) fetches an array of NSTextCheckingResult, the type used as the result for a variety of text handling classes, such as NSDataDetector and NSSpellChecker. The resulting array has one NSTextCheckingResult for each match.

The information we’re most interested are the range of the match, stored as range in each result, and the ranges of any capture groups in the regular expression. You can use the numberOfRanges property and the rangeAt(_:)method to find the captured ranges—range 0 is always the full match, with the ranges at indexes 1 up to, but not including, numberOfRanges covering each capture group.

Using the NSRange-based substring method we declared above, we can use these ranges to extract the capture groups:

formatchinmatches{letstringToFormat=text.substring(with:match.rangeAt(2))!switchtext.substring(with:match.rangeAt(1))!{case"*":print("Make bold: '\(stringToFormat)'")case"_":print("Make italic: '\(stringToFormat)'")default:break}}// Make bold: 'bold'// Make italic: 'italic'
for(NSTextCheckingResult*matchinmatches){NSString*delimiter=[textsubstringWithRange:[matchrangeAtIndex:1]];NSString*stringToFormat=[textsubstringWithRange:[matchrangeAtIndex:2]];if([delimiterisEqualToString:@"*"]){NSLog(@"Make bold: '%@'",stringToFormat);}elseif([delimiterisEqualToString:@"_"]){NSLog(@"Make italic: '%@'",stringToFormat);}}// Make bold: 'bold'// Make italic: 'italic'

For basic replacement, head straight to stringByReplacingMatches(in:options:range:with:), the long-winded version of String.replacingOccurences(of:with:options:). In this case, we need to use different replacement templates for different matches (bold vs. italic), so we’ll loop through the matches ourselves (moving in reverse order, so we don’t mess up the ranges of later matches):

varformattedText=textFormat:formatchinmatches.reversed(){lettemplate:Stringswitchtext.substring(with:match.rangeAt(1))??""{case"*":template="<strong>$2</strong>"case"_":template="<em>$2</em>"default:breakFormat}letmatchRange=formattedText.range(from:match.range)!// see aboveletreplacement=miniFormatter.replacementString(for:match,in:formattedText,offset:0,template:template)formattedText.replaceSubrange(matchRange,with:replacement)}// 'formattedText' is now:// "MiniFormatter handles <strong>bold</strong> and <em>italic</em> text."
NSMutableString*formattedText=[NSMutableStringstringWithString:text];for(NSTextCheckingResult*matchin[matchesreverseObjectEnumerator]){NSString*delimiter=[textsubstringWithRange:[matchrangeAtIndex:1]];NSString*template=[delimiterisEqualToString:@"*"]?@"<strong>$2</strong>":@"<em>$2</em>";NSString*replacement=[miniFormatterreplacementStringForResult:matchinString:formattedTextoffset:0template:template];[formattedTextreplaceCharactersInRange:[matchrange]withString:replacement];}// 'formattedText' is now:// @"MiniFormatter handles <strong>bold</strong> and <em>italic</em> text."

Calling miniFormatter.replacementString(for:in:...) generates a replacement string specific to each NSTextCheckingResult instance with our customized template.

Expression and Matching Options

NSRegularExpression is highly configurable—you can pass different sets of options when creating an instance or when calling any method that performs matching.

NSRegularExpression.Options

Pass one or more of these as options when creating a regular expression.

  • .caseInsensitive: Turns on case insensitive matching. Equivalent to the i flag.
  • .allowCommentsAndWhitespace: Ignores any whitespace and comments between a # and the end of a line, so you can format and document your pattern in a vain attempt at making it readable. Equivalent to the x flag.
  • .ignoreMetacharacters: The opposite of the .regularExpression option in String.range(of:options:)—this essentially turns the regular expression into a plain text search, ignoring any regular expression metacharacters and operators.
  • .dotMatchesLineSeparators: Allows the . metacharacter to match line breaks as well as other characters. Equivalent to the s flag.
  • .anchorsMatchLines: Allows the ^ and $ metacharacters (beginning and end) to match the beginnings and ends of lines instead of just the beginning and end of the entire input string. Equivalent to the m flag.
  • .useUnixLineSeparators, .useUnicodeWordBoundaries: These last two opt into more specific line and word boundary handling: UNIX line separators
NSRegularExpression.MatchingOptions

Pass one or more of these as options to any matching method on an NSRegularExpression instance.

  • .anchored: Only match at the start of the search range.
  • .withTransparentBounds: Allows the regex to look past the search range for lookahead, lookbehind, and word boundaries (though not for actual matching characters).
  • .withoutAnchoringBounds: Makes the ^ and $ metacharacters match only the beginning and end of the string, not the beginning and end of the search range.
  • .reportCompletion, .reportProgress: These only have an effect when passed to the method detailed in the next section. Each option tells NSRegularExpression to call the enumeration block additional times, when searching is complete or as progress is being made on long-running matches, respectively.

Partial Matching

Finally, one of the most powerful features of NSRegularExpression is the ability to scan only as far into a string as you need. This is especially valuable on a large string, or when using an pattern that is expensive to run.

Instead of using the firstMatch(in:...) or matches(in:...) methods, call enumerateMatches(in:options:range:using:) with a closure to handle each match. The closure receives three parameters: the match, a set of flags, and a pointer to a Boolean that acts as an out parameter, so you can stop enumerating at any time.

We can use this method to find the first several names in Dostoevsky’s Brothers Karamazov, where names follow a first and patronymic middle name style (e.g., “Ivan Fyodorovitch”):

letnameRegex=try!NSRegularExpression(pattern:"([A-Z]\\S+)\\s+([A-Z]\\S+(vitch|vna))")letbookString=...varnames:Set<String>=[]nameRegex.enumerateMatches(in:bookString,range:bookString.nsrange){(result,_,stopPointer)inguardletresult=resultelse{return}letname=nameRegex.replacementString(for:result,in:bookString,offset:0,template:"$1 $2")names.insert(name)// stop once we've found six unique namesstopPointer.pointee=ObjCBool(names.count==6)}// names.sorted(): // ["Adelaïda Ivanovna", "Alexey Fyodorovitch", "Dmitri Fyodorovitch", //  "Fyodor Pavlovitch", "Pyotr Alexandrovitch", "Sofya Ivanovna"]
NSString*namePattern=@"([A-Z]\\S+)\\s+([A-Z]\\S+(vitch|vna))";NSRegularExpression*nameRegex=[NSRegularExpressionregularExpressionWithPattern:namePatternoptions:kNilOptionserror:&error];NSString*bookString=...NSMutableSet*names=[NSMutableSetset];[nameRegexenumerateMatchesInString:bookStringoptions:kNilOptionsrange:NSMakeRange(0,[bookStringlength])usingBlock:^(NSTextCheckingResult*result,NSMatchingFlagsflags,BOOL*stop){if(result==nil)return;NSString*name=[nameRegexreplacementStringForResult:resultinString:bookStringoffset:0template:@"$1 $2"];[namesaddObject:name];// stop once we've found six unique names*stop=(names.count==6);}];

With this approach we only need to look at the first 45 matches, instead of nearly 1300 in the entirety of the book. Not bad!


Once you get to know it, NSRegularExpression can be a truly useful tool. In fact, you may have used it already to find dates, addresses, or phone numbers in user-entered text—NSDataDetector is an NSRegularExpression subclass with patterns baked in to identify useful info. Indeed, as we’ve come to expect of text handling throughout Foundation, NSRegularExpression is thorough, robust, and has surprising depth beneath its tricky interface.

The Death of Cocoa

$
0
0

Cocoa is the de facto standard library of Objective-C, containing many of the essential frameworks for working in the language, such as Foundation, AppKit, and Core Data. Cocoa Touch is basically just Cocoa with UIKit substituted for AppKit, and is often used interchangeably with Cocoa to refer to the system frameworks on iOS.

For many of us, the simplicity, elegance, and performance of Apple’s hardware and software working together are the reason why we build on their platforms. Indeed, no shortage of words have been penned on this site in adulation of Cocoa’s design and functionality.

And yet, after just a few months of working with Swift, Cocoa has begun to lose its luster. We all saw Swift as the beginning of the end for Objective-C, but Cocoa? (It wouldn’t be the first time an Apple standard library would be made obsolete. Remember Carbon?)

Swift is designed with modern language features that allow for safer, more performant code. However, one could be forgiven for seeing Swift as little more than a distraction for the compiler tools team, as very few of Swift’s advantages trickle down into conventional application usage.

Having Objective-C and Swift code interoperate in a meaningful way from launch was a strategic—and arguably necessary—decision. Allowing the more adventurous engineers within a team a low-risk way to introduce Swift into existing code bases has been crucial to the wide adoption the new language has already seen. But for all of the effort that’s been put into source mapping and API auditing, there’s an argument to be made that Cocoa has become something of a liability.

What if we were to build a new Foundation from the Swift Standard Library? What would we do differently, and how could we learn from the mistakes of our past? This may seem an odd thesis for NSHipster, a site founded upon a great deal of affection for Objective-C and Cocoa, but it’s one worth exploring.

So to close out this historic year for Apple developers, let’s take a moment to look forward at the possibilities going forward.


If I have seen further it is by standing on the shoulders of giants. Isaac Newton

We owe all of our productivity to standard libraries.

When done well, standard libraries not only provide a common implementation of the most useful programming constructs, but they clarify those concepts in a transferable way. It’s when a language’s standard library diverges from existing (or even internal) convention that things go south.

For example, NSURLComponents conforms to RFC 3986—a fact made explicit in the documentation. Not only do API consumers osmotically absorb the proper terminology and concepts as a byproduct of usage, but newcomers to the API that are already familiar with RFC 3986 can hit the ground running. (And how much easier it is to write documentation; just “RTFM” with a link to the spec!)

Standard libraries should implement standards.

When we talk about technologies being intuitive, what we usually mean is that they’re familiar. Standards from the IETF, ISO, and other bodies should be the common ground on which any new standard library should be built.

Based on this assertion, let’s take a look at some specific examples of what Cocoa does, and how a new Swift standard library could improve.

Numbers

NSNumber exists purely as an object wrapper around integer, float, double, and boolean primitives. Without such concerns in Swift, there is no practical role for such a construct.

Swift’s standard library has done a remarkable job in structuring its numeric primitives, through a clever combination of top-level functions and operators and type hierarchies. (And bonus points for including literals for binary, octal, and hexadecimal in addition to decimal). For lack of any real complaints about what’s currently there, here are some suggestions for what might be added:

Strings

The peril of strings is that they can encode so many different kinds of information. As written previously:

Strings are perhaps the most versatile data type in computing. They’re passed around as symbols, used to encode numeric values, associate values to keys, represent resource paths, store linguistic content, and format information.

NSString is perhaps too versatile, though. Although it handles Unicode like a champ, the entire API is burdened by the conflation of strings as paths. stringByAppendingPathComponent: and its ilk are genuinely useful, but this usefulness ultimately stems from a misappropriation of strings as URLs.

Much of this is due to the fact that @"this" (a string literal) is much more convenient than [NSURL URLWithString:@"that"] (a constructor). However, with Swift’s literal convertibles, it can be just as easy to build URL or Path values.

One of the truly clever design choices for Swift’s String is the internal use of encoding-independent Unicode characters, with exposed “views” to specific encodings:

  • A collection of UTF-8 code units (accessed with the string’s utf8 property)
  • A collection of UTF-16 code units (accessed with the string’s utf16 property)
  • A collection of 21-bit Unicode scalar values, equivalent to the string’s UTF-32 encoding form (accessed with the string’s unicodeScalars property)

One of the only complaints of Swift Strings are how much of its functionality is hampered by the way functionality is hidden in top-level functions. Most developers are trained to type . and wait for method completion for something like “count”; it’s less obvious to consult the top-level countElements function. (Again, as described in the Default Protocol Implementations article, this could be solved if either Xcode or Swift itself allowed automatic bridging of explicit and implicit self in functions).

URI, URL, and URN

An ideal URL implementation would be a value-type (i.e. struct) implementation of NSURLComponents, which would take over all of the aforementioned path-related APIs currently in NSString. Something along these lines. A clear implementation of URI schemes, according to RFC 4395, could mitigate the conflation of file (file://) URLs as they are currently in NSURL. A nice implementation of URNs, according to RFC 2141 would do wonders for getting developers to realize what a URN is, and how URIs, URLs, and URNs all relate to one another. (Again, it’s all about transferrable skills).

Data Structures

Swift’s functional data structures, from generators to sequences to collections, are, well, beautiful. The use of Array and Dictionary literals for syntactic sugar strikes a fine balance with the more interesting underlying contours of the Standard Library.

Building on these strong primitives, it is remarkably easy to create production-ready implementations of the data structures from an undergraduate Computer Science curriculum. Armed with Wikipedia and a spare afternoon, pretty much anyone could do it—or at least get close.

It’d be amazing if the Swift standard library provided canonical implementations of a bunch of these (e.g. Tree, Singly- Doubly-Linked Lists, Queue / Stack). But I’ll only make the case for one: Set.

The three big collections in Foundation are NSArray, NSDictionary, and NSSet (and their mutable counterparts). Of these, Set is the only one currently missing. As a fundamental data structure, they are applicable to a wide variety of use cases. Specifically for Swift, though, Set could resolve one of the more awkward corners of the language—RawOptionSetType.

For your consideration, Nate Cook has built a nice, complete implementation of Set.

Dates & Times

The calendaring functionality is some of the oldest and most robust in Cocoa. Whereas with most other languages, date and time programming is cause for fear, one does not get the same sense of dread when working with NSDate and NSCalendar. However, it suffers from being difficult to use and impossible to extend.

In order to do any calendaring calculations, such as getting the date one month from today, one would use NSCalendar and NSDateComponents. That’s the correct way to do it, at least… a majority of developers probably still use dateWithTimeIntervalSinceNow: with a constant number of seconds hardcoded. Tragically, it’s not enough for an API to do things the right way, it must also be easier than doing it the wrong way.

Another shortfall (albeit incredibly minor) of NSCalendar is that it doesn’t allow for new calendars to be added. For someone doing their darnedest to advocate conversion to the French Republican Calendar, this is bothersome.

Fortunately, all of the new language features of Swift could be used to solve both of these problems in a really elegant way. It’d take some work to implement, but a calendaring system based on generics could really be something. If anyone wants to take me up on that challenge, here are some ideas.

Interchange Formats

One of the most astonishing things about Objective-C is how long it took for it to have a standard way of working with JSON (iOS 5 / OS X Lion!). Developers hoping to work with the most popular interchange format for new web services were forced to choose from one of a handful of mutually incompatible third-party libraries.

However, NSJSONSerialization is such a miserable experience in Swift that we’re repeating history with a new crop of third-party alternatives:

letdata:NSDatavarerror:NSError?=nilifletJSON=NSJSONSerialization.JSONObjectWithData(data,options:nil,error:&error)as?NSDictionary{forproductinJSON["products"]!asNSArray{letname:String=product["name"]asStringletprice:Double=product["price"]asDouble}}
NSData*data;NSError*error=nil;idJSON=[NSJSONSerializationJSONObjectWithData:dataoptions:0error:&error];if(!error){for(idproductinJSON[@"products"]){NSString*name=product[@"name"];NSNumber*price=product[@"price"];// ...}}

In defense of Apple, I once asked an engineer at a WWDC Lab why it took so long for iOS to support JSON. Their answer made a lot of sense. Paraphrasing:

Apple is a company with a long view of technology. It’s really difficult to tell whether a technology like JSON is going to stick, or if it’s just another fad. Apple once released a framework for PubSub, which despite not being widely known or used, still has to be supported for the foreseeable future. Each technology is a gamble of engineering resources.

Data marshaling and serialization are boring tasks, and boring tasks are exactly what a standard library should take care of. Apple knew this when developing Cocoa, which has robust implementations for both text and binary property lists, which are the lifeblood of iOS and OS X. It may be difficult to anticipate what other interchange formats will be viable in the long term, but providing official support for emerging technologies on a probationary basis would do a lot to improve things for developers.

Regular Expressions

Regexes are a staple of scripting languages—enough so that they often have a dedicated syntax for literals, / /. If Swift ever moves on from Cocoa, it would be well-advised to include a successor to NSRegularExpression, such as this wrapper.

Errors

Objective-C is rather exceptional in how it uses error pointers (NSError **) to communicate runtime failures rather than @throw-ing exceptions. It’s a pattern every Cocoa developer should be familiar with:

NSError*error=nil;BOOLsuccess=[[NSFileManagerdefaultManager]moveItemAtPath:@"/path/to/target"toPath:@"/path/to/destination"error:&error];if(!success){NSLog(@"%@",error);}

The out parameter for error is a workaround for the fact that Objective-C can only have a single return value. If something goes wrong, the NSError instance will be populated with a new object with details about the issue.

In Swift, this pattern is unnecessary, since a method can return a tuple with an optional value and error instead:

funcmoveItemAtPath(from:StringtoPathto:String)->(Bool,NSError?){...}

We can even take things a step further and define a generic Result type, with associated values for success and failure cases:

structError{...}publicenumResult<T>{caseSuccess(T)caseFailure(Error)}

Using this new pattern, error handling is enforced by the compiler in order to exhaust all possible cases:

HTTPClient.getUser{(result)inswitchresult{case.Success(letuser):// Successcase.Failure(leterror):// Failure}}

Patterns like this have emerged from a community eager to improve on existing patterns in pure Swift settings. It would be helpful for a standard library to codify the most useful of these patterns in order to create a shared vocabulary that elevates the level of discourse among developers.

AppKit & UIKit

AppKit and UIKit are entire topics unto themselves. It’s much more likely that the two would take further steps to unify than be rewritten or adapted to Swift anytime soon. A much more interesting question is whether Swift will expand beyond the purview of iOS & OS X development, such as for systems or web scripting, and how that would fundamentally change the role of Cocoa as a de facto standard library.


Thinking Further

Perhaps we’re thinking too small about what a standard library can be.

The Wolfram Language has The Mother of All Demos (with apologies to Douglas Engelbart) for a programming language.

Granted, Wolfram is a parallel universe of computation where nothing else exists, and the language itself is a hot mess.

Here’s an overview of the functionality offered in its standard library:

2D / 3D VisualizationGraph AnalysisData AnalyticsImage Processing
Audio ProcessingMachine LearningEquation SolvingAlgebraic Computation
Arbitrary PrecisionCalculus ComputationMatrix ComputationString Manipulation
Combinatorial OptimizationComputational GeometryDatabase ConnectivityBuilt-In Testing
Device ConnectivityFunctional ProgrammingNatural Language UnderstandingSequence Analysis
Time SeriesGeographic DataGeomappingWeather Data
Physics & Chemistry DataGenomic DataUnits & MeasuresControl Theory
Reliability AnalysisParallel ComputationEngineering DataFinancial Data
Financial ComputationSocioeconomic DataPopular Culture DataBoolean Computation
Number TheoryDocument GenerationTable FormattingMathematical Typesetting
Interactive ControlsInterface BuildingForm ConstructionXML Templating

Conventional wisdom would suggest that, yes: it is unreasonable for a standard library to encode the production budget of the movie Avatar, the max speed of a McDonnell Douglas F/A-18 Hornet, or the shape of France. That is information that can be retrieved by querying IMDB, scraping Wikipedia, and importing from a GIS system.

But other things, like converting miles to kilometers, clustering values, or knowing the size of the Earth—these are things that would be generally useful to a variety of different applications.

Indeed, what sets Cocoa apart from most other standard libraries is all of the specific information it encodes in NSLocale and NSCalendar, but most of this comes from the Unicode Common Locale Data Repository (CLDR).

What’s to stop a standard library from pulling in other data sources? Why not expose an interface to libphonenumber, or expand on what HealthKit is already doing for fundamental units?

Incorporating this kind of data in an organized, meaningful way is too much to expect for a third-party framework, and too important to delegate to the free market of open source.

Yes, in many ways, the question of the role of a standard library is the same as the question of what roles the public and private sectors have in society. Third-Party Libertarians, meet Third-Party Librarytarians.


Swift is compelling not just in terms of what the language itself can do, but what it means to Apple, to iOS & OS X developers, and the developer community at large. There are so many factors in play that questions of technical feasibility cannot be extricated from their social and economic consequences.

Will Swift be released under an open source license? Will Swift unseat Javascript as the only viable web scripting language by adding interpreter to Safari? Ultimately, these will be the kinds of deciding factors for what will become of the Swift Standard Library. If Swift is to become a portable systems and scripting language, it would first need to extricate itself from the Objective-C runtime and a web of heavy dependencies.

What is almost certain, however, is that Cocoa, like Objective-C, is doomed. It’s not as much a question of whether, but when. (And we’re talking years from now; no one is really arguing that Objective-C & Cocoa are going away entirely all at once).

The Swift Standard Library is on a collision course with Cocoa, and if the new language continues to gain momentum, one should expect to see further fracturing and reinvention within the system frameworks.

For 30 years, these technologies have served us well, and the best we can do to honor their contributions is to learn from their mistakes and make sure that what replaces them are insanely great.

Reader Submissions - New Year's 2015

$
0
0

As we take a moment to reflect on our experiences over the past year, one thing is clear: 2014 was an incredible year professionally for Apple developers. So much has happened in such a short timespan, and yet it’s hard to remember our relationship to Objective-C before Swift, or what APIs could have captivated our imagination as much as iOS 8 or WatchKit.

It’s an NSHipster tradition to ask you, dear readers, to send in your favorite tips and tricks from the past year for publication over the New Year’s holiday. This year, with a deluge of new developments—both from Cupertino and the community at large—there were no shortage of interesting tidbits to share.

Thanks to Colin Rofls, Cédric Luthi, Florent Pillet, Heath Borders, Joe Zobkiw, Jon Friskics, Justin Miller, Marcin Matczuk, Mikael Konradsson, Nolan O'Brien, Robert Widmann, Sachin Palewar, Samuel Defago, Sebastian Wittenkamp, Vadim Shpakovski, and Zak Remer for their contributions.


The Secret Lives of Member Functions

From Robert Widmann:

Member functions on Swift classes and structures always have the following type when used statically:

Object -> (Args) -> Thing

For example, you can call reverse() on an array in two ways:

[1,2,3,4].reverse()Array.reverse([1,2,3,4])()

@( ) for Boxing C-Strings

From Samuel Defago:

Given the fact that literals are most of the time associated with numbers and collections, I often forget that they work for UTF8-encoded NULL-terminated C-strings as well, especially when I write code using the runtime:

NSString*propertyAttributesString=@(property_getAttributes(class_getProperty([NSObjectclass],"description")));// T@"NSString",R,C

AmIBeingDebugged

Nolan O'Brien brings the AmIBeingDebugged function to our attention from from this Technical Q&A document:

#include <assert.h>#include <stdbool.h>#include <sys/types.h>#include <unistd.h>#include <sys/sysctl.h>staticBoolAmIBeingDebugged(void){intmib[4];structkinfo_procinfo;size_tsize=sizeof(info);info.kp_proc.p_flag=0;mib[0]=CTL_KERN;mib[1]=KERN_PROC;mib[2]=KERN_PROC_PID;mib[3]=getpid();sysctl(mib,sizeof(mib)/sizeof(*mib),&info,&size,NULL,0);return(info.kp_proc.p_flag&P_TRACED)!=0;}

Use Lazy Variables

From Colin Rofls:

Optionals should be avoided. Implicitly unwrapped optionals should be strongly avoided. Want to declare a var but don’t necessarily have an initial value at init time? Use the lazy keyword, and just don’t call the getter before you have your real value.

lazyvarsomeModelStructure=ExpensiveClass()

If you call set on this var without having ever called the getter, the lazy expression is never evaluated. Great for references to views that you don’t necessarily want to init until viewDidLoad, for instance.

Accessing Child Controllers Inserted Into Storyboard Container Views

From Vadim Shpakovski:

Here is a convenient way to access child controllers inserted into Storyboard container views:

// 1. A property has the same name as a segue identifier in XIB@property(nonatomic)ChildViewController1*childController1;@property(nonatomic)ChildViewController2*childController2;// #pragma mark - UIViewController-(void)prepareForSegue:(UIStoryboardSegue*)seguesender:(id)sender{[superprepareForSegue:seguesender:sender];// 2. All known destination controllers assigned to propertiesif([selfrespondsToSelector:NSSelectorFromString(segue.identifier)]){[selfsetValue:segue.destinationViewControllerforKey:segue.identifier];}}-(void)viewDidLoad{[superviewDidLoad];// 3. Controllers already available bc viewDidLoad is called after prepareForSegueself.childController1.view.backgroundColor=[UIColorredColor];self.childController2.view.backgroundColor=[UIColorblueColor];}

Re-Run without Re-Building

From Heath Borders:

If you’re repeatedly debugging the same problem over and over, you can run your app without rebuilding with “Product > Perform Action > Run without Building” (⌘⌃R).

Quick Access to Playground Resources

From Jon Friskics:

Swift Playgrounds all share the same Shared Playground Data folder that’s symlinked to /Users/HOME/Documents/Shared Playground Data.

If you like using lots of Playgrounds, you’ll want to organize the data that each Playground is using into subfolders of that shared folder, but then you’ve got to let the Playground know where to look. Here’s a helper function that I use that makes that easy:

funcpathToFileInSharedSubfolder(file:String)->String{returnXCPSharedDataDirectoryPath+"/"+NSProcessInfo.processInfo().processName+"/"+file}

That processName property in NSProcessInfo contains the name of the Playground file, so as long as you have already created a sub-folder in the Shared Playground Data folder with the same name you can access those files pretty easily, like reading local JSON:

varjsonReadError:NSError?letjsonData=NSFileManager.defaultManager().contentsAtPath(pathToFileInSharedSubfolder("data.json"))!letjsonArray=NSJSONSerialization.JSONObjectWithData(jsonData,options:nil,error:&jsonReadError)as[AnyObject]

…or pulling out a local image:

letimageView=UIImageView()imageView.image=UIImage(contentsOfFile:pathToFileInSharedSubfolder("image.png"))

The rest of this year’s reader submissions come from Cédric Luthi, who (as in yearspast) contributed a payload of tips and tricks worthy of an entire article unto themselves. Thanks so much for these, Cédric!

CocoaPods, Exposed!

Here’s a quick way to check all the pods used by a (closed source) app:

$ class-dump -C Pods_ /Applications/Squire.app | grep -o "Pods_\w+"

CREATE_INFOPLIST_SECTION_IN_BINARY

Check out the CREATE_INFOPLIST_SECTION_IN_BINARY Xcode setting for command-line apps. It’s much easier to use than the -sectcreate __TEXT __info_plist linker flag and it embeds the processed Info.plist file into the binary.

It’s also a lesson on filing radars. This feature request was filed as rdar://4722772 in 2006 and was addressed about 7 years later.

Stop dylib Hooking

Make hackers' lives tougher with this trick from Sam Marshall:

Add this one line to your “Other Linker Flags”:

-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null

NSBundle -preferredLocalizations

Sometimes, you need to know which language your app is running in. Often, people will use NSLocale +preferredLanguages. Unfortunately this tells nothing about the language the app is actually displaying. It will just give you the ordered list as found in “Settings → General → Language & Region → Preferred Language” Order on iOS, or “System Preferences → Language & Region → Preferred Languages” on OS X.

Imagine that the preferred language order is {English, French} but your app is German only. Calling [[NSLocale preferredLanguages] firstObject] will give you English when you want German.

The proper way to get the actual language used by the app is to use [[NSBundle mainBundle] preferredLocalizations].

From the documentation:

An array of NSString objects containing language IDs for localizations in the bundle. The strings are ordered according to the user’s language preferences and available localizations.

From a comment in NSBundle.h:

A subset of this bundle’s localizations, re-ordered into the preferred order for this process’s current execution environment; the main bundle’s preferred localizations indicate the language (of text) the user is most likely seeing in the UI

You may also need to use NSLocale +canonicalLanguageIdentifierFromString: in order to ensure a canonical language identifier.

Preserve SDK Headers

If you are installing Xcode from the dmg, check out this technique by Joar Wingfors in order to avoid to accidentally modifying SDK headers by preserving ownership, permissions and hard links:

$ sudo ditto /Volumes/Xcode/Xcode.app /Applications/Xcode.app

Inspecting void * Instance Variables

For reverse engineering purpose, it’s often useful to look at instance variables of objects. It’s usually pretty easy to do so with valueForKey:, since very few classes override +accessInstanceVariablesDirectly to disable ivar access through Key-Value Coding.

There’s one case where it doesn’t work though: when the ivar has a void * type.

Here is an excerpt of the MediaPlayer framework class-dump on iOS 6.1:

@interfaceMPMoviePlayerController : NSObject<MPMediaPlayback>{void*_internal;// 4 = 0x4BOOL_readyForDisplay;// 8 = 0x8}

Since id internal = [moviePlayerController valueForKey:@"internal"] doesn’t work, here is the hardcore way to access the internal ivar:

idinternal=*((constid*)(void*)((uintptr_t)moviePlayerController+sizeof(Class)));

Don’t ship this code, its’s very fragile because the ivar layout may change. Use this for reverse engineering only!

NSDateFormatter +dateFormatFromTemplate:options:locale:

A friendly reminder that if you are using NSDateFormatter -setDateFormat: without NSDateFormatter +dateFormatFromTemplate:options:locale:, you’re probably doing it wrong.

From the documentation:

+(NSString*)dateFormatFromTemplate:(NSString*)templateoptions:(NSUInteger)optslocale:(NSLocale*)locale

Different locales have different conventions for the ordering of date components. You use this method to get an appropriate format string for a given set of components for a specified locale (typically you use the current locale—see currentLocale).

The following example shows the difference between the date formats for British and American English:

NSLocale*usLocale=[[NSLocalealloc]initWithLocaleIdentifier:@"en_US"];NSLocale*gbLocale=[[NSLocalealloc]initWithLocaleIdentifier:@"en_GB"];NSString*dateFormat;NSString*dateComponents=@"yMMMMd";dateFormat=[NSDateFormatterdateFormatFromTemplate:dateComponentsoptions:0locale:usLocale];NSLog(@"Date format for %@: %@",[usLocaledisplayNameForKey:NSLocaleIdentifiervalue:[usLocalelocaleIdentifier]],dateFormat);dateFormat=[NSDateFormatterdateFormatFromTemplate:dateComponentsoptions:0locale:gbLocale];NSLog(@"Date format for %@: %@",[gbLocaledisplayNameForKey:NSLocaleIdentifiervalue:[gbLocalelocaleIdentifier]],dateFormat);// Output:// Date format for English (United States): MMMM d, y// Date format for English (United Kingdom): d MMMM y

Deriving Internal Constants with the Debugger

Recently, Matthias Tretter asked on Twitter:

Search for duration in a class-dump of UIKit, find the UITransitionView +defaultDurationForTransition: method, and set a breakpoint for that method:

(lldb) br set -n "+[UITransitionView defaultDurationForTransition:]"

Present a modal view controller, you will hit the breakpoint, type finish to execute the method:

(lldb) finish

At that point defaultDurationForTransition: has executed, and you can read the result (it’s in the xmm0 register):

(lldb) register read xmm0 --format float64
    xmm0 = {0.4 0}

Answer: the default duration is 0.4s.

DIY Weak Associated Objects

Unfortunately, the associated objects OBJC_ASSOCIATION_ASSIGN policy does not support zeroing weak references. Fortunately, it’s quite easy to implement yourself. You just need a simple class to wrap an object with a weak reference:

@interfaceWeakObjectContainter : NSObject@property(nonatomic,readonly,weak)idobject;@end@implementationWeakObjectContainter-(instancetype)initWithObject:(id)object{self=[superinit];if(!self){returnnil;}self.object=object;returnself;}@end

Then, associate the WeakObjectContainter with OBJC_ASSOCIATION_RETAIN(_NONATOMIC):

objc_setAssociatedObject(self,&MyKey,[[WeakObjectContainteralloc]initWithObject:object],OBJC_ASSOCIATION_RETAIN_NONATOMIC);

Use the object property to access it in order to get a zeroing weak reference to the desired object:

idobject=[objc_getAssociatedObject(self,&MyKey)object];

And with that, we bring in a brand new year of possibilities and opportunities. Happy 2015, everyone!

May your code continue to compile and inspire.

Changing of the Guard

$
0
0

I started NSHipster in the summer of 2012. What began as a snarky writing exercise to capture observations from my daily interactions with Cocoa & Objective-C became a full-time passion for learning as much as I could about my craft.

As of today, I am stepping down from my role as managing editor of NSHipster. Nate Cook (@nnnnnnnn) will be taking over ownership and editorial oversight for the site. Nate is a talented engineer and writer who embodies the spirit of curiosity that characterizes NSHipster and its readers. I’m fully confident that he’ll continue to create and cultivate the great content you’ve come to expect every week.

The best is yet to come for NSHipster.

It’s been the thrill of a lifetime to wake up every Monday morning and share a little bit of myself with such an amazing community. I can’t thank you enough for your support, patience, and enthusiasm for me and my work. This was a lot of fun.

May your code continue to compile and inspire.

Long Live Cocoa

$
0
0

It’s the start of a new year—2015, the year of Watch, the first full year for Swift, and a bit of a new start for NSHipster. Before we get caught up in the excitement of new devices and the next beta of Xcode or start planning our trips to WWDC 2015, let’s take a moment to look at our tools as they are today: Objective-C, Swift, and most importantly, Cocoa.

Swift is an exciting language for many of us, but it’s still brand new. The stability of Objective-C and the history and strength of Cocoa mean that Swift isn’t ready to be the driving force behind a major change, at least not quite yet. Cocoa’s depth and the power it affords, along with the way it and Swift go hand in hand, make Cocoa as relevant and as promising as ever. In fact, I don’t think there’s been a more exciting time to be a Cocoa developer.


Cocoa is an impressively deep API—dig a little below the surface of any common tool and you unearth a trove of functionality. You need look no further for proof than the incredible work Mattt has done in these very pages over the last few years, illuminating what we didn’t know Cocoa could do. To name just a few:

The list goes on and on. (Check it out—right there on the front page.)

Hand in Hand

What’s more, Cocoa and Swift are practically—and in Swift’s case, literally—made for each other.

On the Cocoa side, changes to the toolset over the past few years paved the way for Cocoa to be Swift-friendly right out of the gate. Shifting to LLVM/Clang, adding block syntax to Objective-C, pushing the NS_ENUM& NS_OPTIONS macros, converting initializers to return instancetype—all these steps make the Cocoa APIs we’re using today far more compatible with Swift than they could have been even a few years ago. Whenever you supply a Swift closure as a NSURLSession completion handler, or use the suggested completions for UIModalTransitionStyle, you’re building on that work, done years ago when Swift was still behind closed doors (or in Chris Lattner’s head).

Swift was then designed from the ground up to be used with Cocoa. If I could nominate a single Swift feature as most confusing to newcomers, it would be Optionals, with their extra punctuation and unwrapping requirements. Even so, Optionals represent a crowning achievement, one so foundational it fades into the woodwork: Swift is a brand-new language that doesn’t require a brand-new API. It’s a type-safe, memory-safe language whose primary purpose is interacting directly with the enormous C-based Cocoa API, with pointers and raw memory lying all over the place.

This is no small feat. The developer tools team at Apple has been busy annotating the entire API with information about memory management for parameters and return values. Once annotated, functions can be used safely from within Swift, since the compiler knows how to bridge types back and forth from Swift to annotated C code.

Here’s an example of similar annotated and unannotated functions. First, the C versions:

// Creates an immutable copy of a string.CFStringRefCFStringCreateCopy(CFAllocatorRefalloc,CFStringReftheString);// Encodes an OSType into a string suitable for use as a tag argument.CFStringRefUTCreateStringForOSType(OSTypeinOSType);

Both of these functions return a CFStringRef—a reference to a CFString. A CFStringRef can be bridged to a Swift CFString instance, but this is only safe if the method has been annotated. In Swift, you can readily see the difference:

// annotated: returns a memory-managed Swift `CFString`funcCFStringCreateCopy(alloc:CFAllocator!,theString:CFString!)->CFString!// unannotated: returns an *unmanaged* `CFString`funcUTCreateStringForOSType(inOSType:OSType)->Unmanaged<CFString>!

Upon receiving an Unmanaged<CFString>!, you need to follow up with .takeRetainedValue() or .takeUnretainedValue() to get a memory-managed CFString instance. Which to call? To know that, you have to read the documentation or know the conventions governing whether the result you get back is retained or unretained. By annotating these functions, Apple has done that work for you, already guaranteeing memory safety across a huge swath of Cocoa.


Moreover, Swift doesn’t just embrace Cocoa APIs, it actively improves them. Take the venerable CGRect, for example. As a C struct, it can’t contain any instance methods, so all the tools to manipulate CGRects live in top-level functions. These tools are powerful, but you need to know they exist and how to put them to use. These four lines of code, dividing a CGRect into two smaller pieces, might require three trips to the documentation:

CGRectnextRect;CGRectremainingRect;CGRectDivide(sourceRect,&nextRect,&remainingRect,250,CGRectMinXEdge);NSLog("Remaining rect: %@",NSStringFromCGRect(remainingRect));

In Swift, structs happily contain both static and instance methods and computed properties, so Core Graphics extends CGRect to make finding and using those tools far easier. Because CGRect* functions are mapped to instance methods or properties, the code above is reduced to this:

let(nextRect,remainingRect)=sourceRect.rectsByDividing(250,CGRectEdge.MinXEdge)println("Remaining rect: \(remainingRect)")

Getting Better All The Time

To be sure, working with Cocoa and Swift together is sometimes awkward. Where that does happen, it often comes from using patterns that are idiomatic to Objective-C. Delegates, target-selector, and NSInvocation still have their place, but with closures so easy in Swift, it can feel like overkill to add a whole method (or three) just to accomplish something simple. Bringing more closure- or block-based methods to existing Cocoa types can easily smooth out these bumps.

For example, NSTimer has a perfectly fine interface, but it suffers from requiring an Objective-C method to call, either via target-selector or invocation. When defining a timer, chances are I already have everything ready to go. With a simple NSTimer extension using its toll-free bridged Core Foundation counterpart, CFTimer, we’re in business in no time:

letmessage="Are we there yet?"letalert=UIAlertController(title:message,message:nil,preferredStyle:.Alert)alert.addAction(UIAlertAction(title:"No",style:.Default,handler:nil))NSTimer.scheduledTimerWithTimeInterval(10,repeats:true){[weakself]timerinifself?.presentedViewController==nil{self?.presentViewController(alert,animated:true,completion:nil)}}// I swear I'll turn this car around.

None of this is to refute Mattt’s last post, though—on an infinite time scale, we’ll surely be coding against Cocoa’s successor on our 42" iPads while looking out across the Titan moonscape. But as long as Cocoa’s still around, isn’t it great?

Viewing all 384 articles
Browse latest View live