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

Swift & the Objective-C Runtime

$
0
0

Even when written without a single line of Objective-C code, every Swift app executes inside the Objective-C runtime, opening up a world of dynamic dispatch and associated runtime manipulation. To be sure, this may not always be the case—Swift-only frameworks, whenever they come, may lead to a Swift-only runtime. But as long as the Objective-C runtime is with us, let’s use it to its fullest potential.

This week we take a new, Swift-focused look at two runtime techniques covered on NSHipster back when Objective-C was the only game in town: associated objects and method swizzling.

Note: This post primarily covers the use of these techniques in Swift—for the full run-down, please refer to the original articles.

Associated Objects

Swift extensions allow for great flexibility in adding to the functionality of existing Cocoa classes, but they’re limited in the same way as their Objective-C brethren, categories. Namely, you can’t add a stored property to an existing class via an extension.

Happily, Objective-C associated objects come to the rescue again, by acting as the storage for computed properties. For example, to add a descriptiveName property to all the view controllers in a project, we add a computed property using objc_get/setAssociatedObject() in the backing get and set blocks:

extensionUIViewController{privatestructAssociatedKeys{staticvardescriptiveName="nsh_DescriptiveName"}vardescriptiveName:String?{get{returnobjc_getAssociatedObject(self,&AssociatedKeys.descriptiveName)as?String}set{ifletnewValue=newValue{objc_setAssociatedObject(self,&AssociatedKeys.descriptiveName,newValueasNSString?,.OBJC_ASSOCIATION_RETAIN_NONATOMIC)}}}}

Note the use of static var in a private nested struct—this pattern creates the static associated object key we need but doesn’t muck up the global namespace.

Method Swizzling

Sometimes for convenience, sometimes to work around a bug in a framework, or sometimes because there’s just no other way, you need to modify the behavior of an existing class’s methods. Method swizzling lets you swap the implementations of two methods, essentially overriding an existing method with your own while keeping the original around.

In this example, we swizzle UIViewController’s viewWillAppear method to print a message any time a view is about to appear on screen. The swizzling happens in the special class method initialize (see note below); the replacement implementation is in the nsh_viewWillAppear method:

extensionUIViewController{publicoverrideclassfuncinitialize(){structStatic{staticvartoken:dispatch_once_t=0}// make sure this isn't a subclassifself!==UIViewController.self{return}dispatch_once(&Static.token){letoriginalSelector=Selector("viewWillAppear:")letswizzledSelector=Selector("nsh_viewWillAppear:")letoriginalMethod=class_getInstanceMethod(self,originalSelector)letswizzledMethod=class_getInstanceMethod(self,swizzledSelector)letdidAddMethod=class_addMethod(self,originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod))ifdidAddMethod{class_replaceMethod(self,swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod))}else{method_exchangeImplementations(originalMethod,swizzledMethod)}}}// MARK: - Method Swizzlingfuncnsh_viewWillAppear(animated:Bool){self.nsh_viewWillAppear(animated)ifletname=self.descriptiveName{print("viewWillAppear: \(name)")}else{print("viewWillAppear: \(self)")}}}

load vs. initialize (Swift Edition)

The Objective-C runtime typically calls two class methods automatically when loading and initializing classes in your app’s process: load and initialize. In the full article on method swizzling, Mattt writes that swizzling should always be done in load(), for safety and consistency. load is called only once per class and is called on each class that is loaded. On the other hand, a single initialize method can be called on a class and all its subclasses, which are likely to exist for UIViewController, or not called at all if that particular class isn’t ever messaged.

Unfortunately, a load class method implemented in Swift is never called by the runtime, rendering that recommendation an impossibility. Instead, we’re left to pick among second-choice options:

  • Implement method swizzling in initialize
    This can be done safely, so long as you check the type at execution time and wrap the swizzling in dispatch_once (which you should be doing anyway).

  • Implement method swizzling in the app delegate
    Instead of adding method swizzling via a class extension, simply add a method to the app delegate to be executed when application(_:didFinishLaunchingWithOptions:) is called. Depending on the classes you’re modifying, this may be sufficient and should guarantee your code is executed every time.


In closing, remember that tinkering with the Objective-C runtime should be much more of a last resort than a place to start. Modifying the frameworks that your code is based upon, as well as any third-party code you run, is a quick way to destabilize the whole stack. Tread softly!


IBInspectable / IBDesignable

$
0
0

Show, don’t tell. Seeing is believing. A picture is worth a thousand emails words.

Whatever the cliché, replacing an interface that requires us to memorize and type with one we can see and manipulate can be an enormous improvement. Xcode 6 makes just such a substitution, building new interactions on top of old technologies. With IBInspectable and IBDesignable, it’s possible to build a custom interface for configuring your custom controls and have them rendered in real-time while designing your project.

IBInspectable

IBInspectable properties provide new access to an old feature: user-defined runtime attributes. Currently accessible from the identity inspector, these attributes have been available since before Interface Builder was integrated into Xcode. They provide a powerful mechanism for configuring any key-value coded property of an instance in a NIB, XIB, or storyboard:

User-Defined Runtime Attributes

While powerful, runtime attributes can be cumbersome to work with. The key path, type, and value of an attribute need to be set on each instance, without any autocompletion or type hinting, which requires trips to the documentation or a custom subclass’s source code to double-check the settings. IBInspectable properties solve this problem outright: in Xcode 6 you can now specify any property as inspectable and get a user interface built just for your custom class.

For example, these properties in a UIView subclass update the backing layer with their values:

@IBInspectablevarcornerRadius:CGFloat=0{didSet{layer.cornerRadius=cornerRadiuslayer.masksToBounds=cornerRadius>0}}@IBInspectablevarborderWidth:CGFloat=0{didSet{layer.borderWidth=borderWidth}}@IBInspectablevarborderColor:UIColor?{didSet{layer.borderColor=borderColor?.CGColor}}

Marked with @IBInspectable (or IBInspectable in Objective-C), they are easily editable in Interface Builder’s inspector panel. Note that Xcode goes the extra mile here—property names are converted from camel- to title-case and related names are grouped together:

IBInspectable Attribute Inspector

Since inspectable properties are simply an interface on top of user-defined runtime attributes, the same list of types is supported: booleans, strings, and numbers (i.e., NSNumber or any of the numeric value types), as well as CGPoint, CGSize, CGRect, UIColor, and NSRange, adding UIImage for good measure.

Those already familiar with runtime attributes will have noticed a bit of trickery in the example above. UIColor is the only color type supported, not the CGColor native to a view’s backing CALayer. The borderColor computed property maps the UIColor (set via runtime attribute) to the layer’s required CGColor.

Making Existing Types Inspectable

Built-in Cocoa types can also be extended to have inspectable properties beyond the ones already in Interface Builder’s attribute inspector. If you like rounded corners, you’ll love this UIView extension:

extensionUIView{@IBInspectablevarcornerRadius:CGFloat{get{returnlayer.cornerRadius}set{layer.cornerRadius=newValuelayer.masksToBounds=newValue>0}}}

Presto! A configurable border radius on any UIView you create.

IBDesignable

As if that weren’t enough, IBDesignable custom views also debut in Xcode 6. When applied to a UIView or NSView subclass, the @IBDesignable designation lets Interface Builder know that it should render the view directly in the canvas. This allows seeing how your custom views will appear without building and running your app after each change.

To mark a custom view as IBDesignable, prefix the class name with @IBDesignable (or the IB_DESIGNABLE macro in Objective-C). Your initializers, layout, and drawing methods will be used to render your custom view right on the canvas:

@IBDesignableclassMyCustomView:UIView{...}

IBDesignable Live Preview

The time-savings from this feature can’t be overstated. Combined with IBInspectable properties, a designer or developer can easily tweak the rendering of a custom control to get the exact result she wants. Any changes, whether made in code or the attribute inspector, are immediately rendered on the canvas.

Moreover, any problems can be debugged without compiling and running the whole project. To kick off a debugging session right in place, simply set a breakpoint in your code, select the view in Interface Builder, and choose EditorDebug Selected Views.

Since the custom view won’t have the full context of your app when rendered in Interface Builder, you may need to generate mock data for display, such as a default user profile image or generic weather data. There are two ways to add code for this special context:

  • prepareForInterfaceBuilder(): This method compiles with the rest of your code but is only executed when your view is being prepared for display in Interface Builder.

  • TARGET_INTERFACE_BUILDER: The #if TARGET_INTERFACE_BUILDER preprocessor macro will work in either Objective-C or Swift to conditionally compile the right code for the situation:

#if !TARGET_INTERFACE_BUILDER// this code will run in the app itself#else// this code will execute only in IB#endif

IBCalculatorConstructorSet

What can you create with a combination of IBInspectable attributes in your IBDesignable custom view? As an example, let’s update an old classic from Apple folklore: the “Steve Jobs Roll Your Own Calculator Construction Set,” Xcode 6-style (gist):

Calculator Construction Set



That was almost a thousand words—let’s see some more pictures. What are you creating with these powerful new tools? Tweet an image of your IBInspectable or IBDesignable creations with the hashtag #IBInspectable—we can all learn from seeing what’s possible.

Swift 1.2

$
0
0

Swift, true to its name, is moving fast. This week marks the beta release of Swift 1.2, a major update to the language. The Swift team has responded to so many of the community’s requests in one fell swoop, we’re overflowing with new and exciting features. Every line-item in this announcement is a big advancement: incremental builds, improved error messages and stability in Xcode, static class properties, support for C unions and bitfields, bridging of Swift enums to Objective-C, safer type casting, improvements to single-line closures, and more.

In what follows, let’s look at two major aspects of the release that will significantly improve the experience of working in Swift: first, big changes in if let optional binding (“finally”), and second, new access to nullability annotations in Objective-C.

Improved Optional Binding

Swift 1.2 allows multiple simultaneous optional bindings, providing an escape from the trap of needing deeply nested if let statements to unwrap multiple values. Multiple optional bindings are separated by commas and can be paired with a where clause that acts like the expression in a traditional if statement. As such, the byzantine pyramid of doom has been renovated into a mid-century modern ranch:

Old:

leta="10".toInt()letb="5".toInt()letc="3".toInt()ifleta=a{ifletb=b{ifletc=c{ifc!=0{println("(a + b) / c = \((a + b) / c)")}}}}

New:

ifleta=a,b=b,c=cwherec!=0{println("(a + b) / c = \((a + b) / c)")// (a + b) / c = 5}

The order of execution in these two examples is identical. Using the new syntax, each binding is evaluated in turn, stopping if any of the attempted bindings is nil. Only after all the optional bindings are successful is the where clause checked.

An if statement can actually have more than one let binding separated by commas. Since each let binding can bind multiple optionals and include a where clause, some truly sophisticated logic is possible with this construct. (Thanks to Stephen Celis for helping clarify this point.)

What’s more, later binding expressions can reference earlier bindings. This means you can delve into Dictionary instances or cast an AnyObject? value to a specific type, then use it in another expression, all in a single if let statement.

To revisit the canonical example, this is what parsing a big block of JSON can look like in Swift 1.2. The example uses one if let block to handle the optionals that come with using NSBundle, NSURL and NSData, then another to map several AnyObject? instances from the interpreted JSON to specific types:

varusers:[User]=[]// load and parse the JSON into an arrayifletpath=NSBundle.mainBundle().pathForResource("users",ofType:"json"),url=NSURL(fileURLWithPath:path),data=NSData(contentsOfURL:url),userList=NSJSONSerialization.JSONObjectWithData(data,options:nil,error:nil)as?[[String:AnyObject]]{// extract individual usersforuserDictinuserList{ifletid=userDict["id"]as?Int,name=userDict["name"]as?String,email=userDict["email"]as?String,address=userDict["address"]as?[String:AnyObject]{users.append(User(id:id,name:name,...))}}}
[{"id":1,"name":"Leanne Graham","username":"Bret","email":"Sincere@april.biz","address":{"street":"Kulas Light","suite":"Apt. 556","city":"Gwenborough","zipcode":"92998-3874","geo":{"lat":"-37.3159","lng":"81.1496"}},"phone":"1-770-736-8031 x56442","website":"hildegard.org","company":{"name":"Romaguera-Crona","catchPhrase":"Multi-layered client-server neural-net","bs":"harness real-time e-markets"}},{"id":2,"name":"Ervin Howell","username":"Antonette","email":"Shanna@melissa.tv","address":{"street":"Victor Plains","suite":"Suite 879","city":"Wisokyburgh","zipcode":"90566-7771","geo":{"lat":"-43.9509","lng":"-34.4618"}},"phone":"010-692-6593 x09125","website":"anastasia.net","company":{"name":"Deckow-Crist","catchPhrase":"Proactive didactic contingency","bs":"synergize scalable supply-chains"}},...]

I see many commas in our future.

Nullability Annotations

When Swift was first released, every call to a Cocoa API method took and returned implicitly unwrapped optionals (i.e., AnyObject!). Because they crash a program on access, implicitly unwrapped return values are inherently unsafe if there isn’t clear documentation of whether or not a method will return a null value. All those exclamation marks were a sign of bad form. Sure, Swift bridged to the Cocoa APIs, but it always looked grumpy about it.

As the beta releases rolled on through the summer and fall, internal audits gradually removed the offending punctuation, replacing implicitly unwrapped optionals with either true optionals or non-optional, never-gonna-be-nil values. This vastly improved the experience of working with Cocoa, but there was no mechanism to mark up third-party code in the same way, leaving part of the problem in place.

But no longer—Swift 1.2 ships alongside a new version of Clang. New property attributes and pointer annotations allow you to indicate whether a pointer, be it an Objective-C property, method parameter, or return value, can or won’t ever be nil.

  • nonnull: Indicates that the pointer should/will never be nil. Pointers annotated with nonnull are imported into Swift as their non-optional base value (i.e., NSData).
  • nullable: Indicates that the pointer can be nil in general practice. Imported into Swift as an optional value (NSURL?).
  • null_unspecified: Continues the current functionality of importing into Swift as an implicitly unwrapped optional, ideally to be used during this annotation process only.
  • null_resettable: Indicates that while a property will always have a value, it can be reset by assigning nil. Properties with a non-nil default value can be annotated this way, like tintColor. Imported into Swift as a (relatively safe) implicitly unwrapped optional. Document accordingly!

The first three annotations can also be used with C pointers and block pointers, using the doubly-underscored __nonnull, __nullable, and __null_unspecified. The last annotation, null_resettable, is only valid as an Objective-C property attribute.

Nullability in Action

As an example to show the benefit of these annotations, let’s take a look at a data controller used to handle a list of locations, each with a possible attached photo:

@interfaceLocationDataController : NSObject@property(nonatomic,readonly)NSArray*locations;@property(nonatomic,readonly)Location*latestLocation;-(void)addPhoto:(Photo*)photoforLocation:(Location*)location;-(Photo*)photoForLocation:(Location*)location;@end

Without any nullability annotations, each pointer in my LocationDataController class is imported to Swift as an implicitly unwrapped optional:

classLocationDataController:NSObject{varlocations:[AnyObject]!{get}varlatestLocation:Location!{get}funcaddPhoto(photo:Photo!,forLocationlocation:Location!)funcphotoForLocation(location:Location!)->Photo!}

Enough! With! The shouting! Here’s how I can now annotate the Objective-C interface:

@interfaceLocationDataController : NSObject@property(nonnull,nonatomic,readonly)NSArray*locations;@property(nullable,nonatomic,readonly)Location*latestLocation;-(void)addPhoto:(nonnullPhoto*)photoforLocation:(nonnullLocation*)location;-(nullablePhoto*)photoForLocation:(nonnullLocation*)location;@end

First, the properties—my locations list is nonnull, since at worst it will be an empty array, but latestLocationcan be nil if there are no locations in the list yet. Likewise, the parameters to my two methods should always have a value, yet because not all locations have a photo, that second method returns a nullable photo. Back in Swift, the results are much better—that is, clearer about how to safely use the data controller and no more grumpy !s:

classLocationDataController:NSObject{varlocations:[AnyObject]{get}varlatestLocation:Location?{get}funcaddPhoto(photo:Photo,forLocationlocation:Location)funcphotoForLocation(location:Location)->Photo?}

NS_ASSUME_NONNULL_BEGIN/END

Annotating any pointer in an Objective-C header file causes the compiler to expect annotations for the entire file, bringing on a cascade of warnings. Given that most annotations will be nonnull, a new macro can help streamline the process of annotating existing classes. Simply mark the beginning and end of a section of your header with NS_ASSUME_NONNULL_BEGIN and ..._END, then mark the exceptions.

Another revision of our data controller interface from above results in a more readable version with the exact same Swift profile:

@interfaceLocationDataController : NSObjectNS_ASSUME_NONNULL_BEGIN@property(nonatomic,readonly)NSArray*locations;@property(nullable,nonatomic,readonly)Location*latestLocation;-(void)addPhoto:(Photo*)photoforLocation:(Location*)location;-(nullablePhoto*)photoForLocation:(Location*)location;NS_ASSUME_NONNULL_END@end

Not Just for Swift

The new Objective-C nullability annotations have huge benefits for code on the Swift side of the fence, but there’s a substantial gain here even without writing a line of Swift. Pointers marked as nonnull will now give a hint during autocomplete and yield a warning if sent nil instead of a proper pointer:

// Can I remove a photo by sending nil?[dataControlleraddPhoto:nilforLocation:currentLocation];// Nope -- Warning: Null passed to a callee that requires a non-null argument

Excitingly, all that is just half the story. In addition to the changes in Swift syntax and compiler savoir faire, the standard library has also seen a major revision, including a proper Set class (so long, dear friend). Okaaay, so none of our code works anymore, and Stack Overflow has 21,000 out-of-date Swift questions? It’s still fun to be along for the ride.

Swift Collection Protocols

$
0
0

Swift has a well-designed and expansive suite of built-in collection types. Beyond Array, Dictionary, and the brand new Set types, the standard library provides slices, lazy collections, repeated sequences, and more, all with a consistent interface and syntax for operations. A group of built-in collection protocols—SequenceType, CollectionType, and several others—act like the steps on a ladder. With each step up, a collection type gains more functionality within the language and the standard library.

By conforming to these protocols, custom collection types can gain access to the same language constructs and powerful top-level functions we use with Array and Dictionary. This week we’ll explore these protocols: what they are, how to conform to them, and what benefits they can provide for your own custom collection types.


To demonstrate the different protocols, along the way we’ll build a new collection type, SortedCollection, which keeps its elements in ascending order for simple, always-sorted access. The implementation shown here is deliberately kept minimal—you can only create a collection and insert, remove, or find elements:

structSortedCollection<T:Comparable>{privatevarcontents:[T]=[]init<S:SequenceTypewhereS.Generator.Element==T>(_sequence:S){contents=sorted(sequence)}funcindexOf(value:T)->Int?{letindex=_insertionIndex(contents,forValue:value)ifindex>=contents.count{returnnil}returncontents[index]==value?index:nil}mutatingfuncinsert(value:T){contents.insert(value,atIndex:_insertionIndex(contents,forValue:value))}mutatingfuncremove(value:T)->T?{ifletindex=indexOf(value){returncontents.removeAtIndex(index)}returnnil}}// Note: _insertionIndex is a private function that returns the // insertion point for a value in a sorted collection.

A complete implementation of SortedCollectionis available as a framework.

Not too much there! Let’s see what kind of functionality we can add by conforming to a few of Swift’s collection protocols.


SequenceType / GeneratorType

The first two collection protocols are inextricably linked: a sequence (a type that conforms to SequenceType ) represents a series of values, while a generator (conforming to GeneratorType, of course) provides a way to use the values in a sequence, one at a time, in sequential order. The SequenceType protocol only has one requirement: every sequence must provide a generator from its generate() method.

A generator works by providing a single method, namely, next(), which simply returns the next value from the underlying sequence. next() will continue returning values until there are none left (), at which point it will return nil. Note that this may never comes to pass, since sequences aren’t necessarily finite.

Whenever you iterate over a sequence, Swift creates a generator and successively calls its next() method. The familiar code for element in myArray { ... } is in fact just a nice wrapper for this:

vargenerator=myArray.generate()whileletelement=generator.next(){// do something}

The relationship between the two protocols is asymmetrical. That is, every sequence has a generator, but only some generators are themselves also sequences (which they can become by returning themselves from their generate() method). Swift includes a whole host of generators, including one that is perfect for our SortedCollection case: the type-erasing GeneratorOf. GeneratorOf is initialized with either the next method implemented as a closure, or another generator:

extensionSortedCollection:SequenceType{typealiasGenerator=GeneratorOf<T>funcgenerate()->Generator{varindex=0returnGeneratorOf{ifindex<self.contents.count{returnself.contents[index++]}returnnil}}}

Ten lines of code later, we can now use SortedCollection with a dozen or so top-level functions, including the bedrock of the functional approach in Swift—reduce, map, and filter.

  • contains: Returns true if (1) a particular given element is found in the sequence or (2) an element satisfies the given predicate closure.
  • enumerate: Converts a sequence into a sequence of tuples, where each tuple is made up of a zero-based index and the value at that index in the sequence.
  • filter: Converts a sequence to an Array, keeping only the elements that match the given predicate closure.
  • join: Creates a collection from the sequence, with a given initial collection interposed between each element of the sequence. The initial element must be an ExtensibleCollectionType, described below.
  • lazy: Creates a “lazy sequence” from the given sequence. Subsequent calls to map, filter, and reverse on the sequence will be evaluated lazily—that is, until you access or iterate over the sequence, none of the transformations will be executed.
  • map: Converts a sequence to an Array after mapping each element with the transforming closure given.
  • maxElement: Returns the maximum value of a sequence of Comparable elements.
  • minElement: Returns the minimum value of a sequence of Comparable elements.
  • reduce: Given an initial value and a combining closure, this “reduces” a sequence to a single element through repeated calls to the closure.
  • sorted: Returns a sorted Array of the sequence’s elements. Sorting is automatically ascending for sequences of Comparable elements, or it can be based on a comparison closure.
  • startsWith: Returns true if one sequence starts with another sequence.
  • underestimateCount: Returns an underestimate of the number of elements in a sequence (SequenceTypes give no hints about length, so this will be zero for a sequence). Returns the actual count for CollectionType instances.
  • zip: Converts two sequences into a sequence of tuples, where each tuple is made up of one element from each sequence.

CollectionType / MutableCollectionType

A collection (a type that conforms to the next collection protocol, CollectionType) is a step beyond a sequence in that individual elements of a collection can be accessed multiple times via subscript. A type conforms by providing a subscripted getter for elements, then starting and ending index properties. No infinite collections here! A MutableCollectionType adds a setter for the same subscript.

Our SortedCollection can easily use Int for its index, so conforming to CollectionType is straightforward:

extensionSortedCollection:CollectionType{typealiasIndex=IntvarstartIndex:Int{return0}varendIndex:Int{returncount}subscript(i:Int)->T{returncontents[i]}}

Subscripting should always be considered an O(1) operation. Types that can’t provide efficient subscripted lookups should move the time-consuming work to the process of generating.

Swift’s built-in String, for example, lost its Int-based subscripting in an early beta. Why? Strings in Swift are fully Unicode-aware, so each visible character may be made up of multiple codepoints. Jumping to an arbitrary index in a string as if it were just an array of characters could land you in the middle of a multi-codepoint sequence, requiring the slightly cumbersome but intuitively O(n) use of startIndex, endIndex, and advance().

Like SequenceType, there are a host of global functions that can operate on CollectionType instances. Now that the start and end of the collection are known, count, sort, and other finite operations become available as top-level functions:

  • count: Returns the number of elements in a collection.
  • find: Returns the first index of a given element in the collection, or nil if not found.
  • first: Returns the first element in the collection, or nil if the collection is empty.
  • indices: Returns a Range of the collection’s indices. Equivalent to c.startIndex..<c.endIndex.
  • isEmpty: Returns true if the collection has no elements.
  • last: Returns the last element in the collection, or nil if the collection is empty.
  • partition: The primary function used in a quick sort (a fast, memory-efficient sorting algorithm). partition reorders part of a collection and returns a pivot index, where everything below the pivot is equal to or ordered before the pivot, and everything above the pivot is equal to or ordered after. Partitioning can be based on a comparison closure if the collection’s elements themselves are not Comparable. Requires MutableCollectionType.
  • reverse: Returns an Array with the elements of the collection in reverse order.
  • sort: Sorts a collection in place, modifying the order of the existing collection. Requires MutableCollectionType.

Sliceable / MutableSliceable

Next up, Sliceable collections promise efficient slicing of a subrange of a collection’s elements. That is, getting a slice of a collection should not require allocating new memory for the selected elements. Again, the standard library guides us: Array and ContiguousArray are the two Sliceable types (besides, well, Slice), and both share their internal storage with their slices until a mutation occurs. This saves both memory and the time needed to allocate new storage for a temporary slice.

For SortedCollection to conform to Sliceable, we need to fulfill that same promise. Happily, we can reuse our embedded Array’s sliceability in a new SortedSlice type, this time based on a Slice<T> instead of an Array:

extensionSortedCollection:Sliceable{typealiasSubSlice=SortedSlice<T>subscript(range:Range<Int>)->SortedSlice<T>{returnSortedSlice(sortedSlice:contents[range])}}// MARK: - SortedSlicestructSortedSlice<T:Comparable>{privatevarcontents:Slice<T>=[]privateinit(sortedSlice:Slice<T>){self.contents=sortedSlice}...}

For other custom collections, Swift 1.2 provides a ManagedBufferPointer type and an isUniquelyReferenced function to help implement the copy-on-write behavior needed for efficient slicing.

Sliceable’s mutable counterpart, MutableSliceable, allows setting a slice’s new contents via subscripting a range of values. Again, mutating by index doesn’t comport with SortedCollections’s requirement to always maintain ascending order. However, none of the Sliceable-ready top-level functions requires mutability:

  • dropFirst: Returns a slice with all but the first element of the collection.
  • dropLast: Returns a slice with all but the last element of the collection.
  • prefix: Returns a slice with the first x elements of the collection or the whole collection if x is greater than the collection’s count.
  • split: Returns an Array of slices separated by elements that match the given isSeparator closure. Optional parameters: a maximum number of splits; a boolean indicating whether empty slices are allowed when consecutive separators are found.
  • suffix: Returns a slice with the last x elements of the collection or the whole collection if x is greater than the collection’s count.

ExtensibleCollectionType / RangeReplaceableCollectionType

Finally, collections that conform to ExtensibleCollectionType and RangeReplaceableCollectionType provide methods to modify the collection. ExtensibleCollectionType requires an empty initializer and three methods: append and extend, which add a single element and a sequence of elements, respectively, and reserveCapacity, which (hopefully) expands the collection’s internal storage to allow additions to the collection without repeatedly reallocating memory.

RangeReplaceableCollectionType requires six methods that replace or remove a range of elements: replaceRange(:with:), insert(:atIndex:), splice(:atIndex:), removeAtIndex(:), removeRange(:), and removeAll(). Conforming types have access to top-level functions that do largely the same:

  • extend: Appends the elements of a collection to a given range-replaceable collection.
  • insert: Inserts a new element into the collection at a particular index.
  • removeAll: Removes all elements from a collection, optionally preserving the collection’s internal capacity.
  • removeLast: Removes a single element from the end of a collection.
  • removeRange: Removes a range of elements from a collection.
  • splice: Inserts a sequence of elements at a particular index of a range-replaceable collection.

Sorting Out the Oscars

So let’s put all this to (popcultural) use. We haven’t added any methods to SortedCollection other than those required to conform to SequenceType, CollectionType, and Sliceable, but yet we’ve gained access to many powerful top-level functions.

We start with the guest list of an after-party for some of last night’s big winners at the Oscars:

letattendees=SortedCollection(["Julianne","Eddie","Patricia","J.K.","Alejandro"])

How many attendees? Using count:

println("\(count(attendees)) attendees")// 5 attendees

Suppose I’d like the stars to line up alphabetically for a photo—how can I give them instructions? Using zip and dropFirst:

for(firstName,secondName)inzip(attendees,dropFirst(attendees)){println("\(firstName) is before \(secondName)")}// Alejandro is before Eddie// Eddie is before J.K.// J.K. is before Julianne// Julianne is before Patricia

Lastly, I need the names to be reformatted so I can use them as image file names. Using map:

letimageNames=map(attendees){$0.lowercaseString}.map{$0.stringByTrimmingCharactersInSet(NSCharacterSet.alphanumericCharacterSet().invertedSet)}.map{"\($0).jpg"}// alejandro.jpg// eddie.jpg// ...

Voila! A-list party!


Without a doubt, part of what makes Swift so fascinating to use is that the language seems largely self-defined. Browse through the standard library and you can see how each type is built—from Int and Double to Array, Dictionary, and the new Set. With an eye toward building more Swift-native types, follow Swift’s example in creating your own!

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 Substrings and Numeric Values

The raison d'être of NSScanner is to pull substrings 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 parameter and returns a boolean value indicating success or failure of the scan:

letwhitespaceAndPunctuationSet=NSMutableCharacterSet.whitespaceAndNewlineCharacterSet()whitespaceAndPunctuationSet.formUnionWithCharacterSet(NSCharacterSet.punctuationCharacterSet())letstringScanner=NSScanner(string:"John & Paul & Ringo & George.")stringScanner.charactersToBeSkipped=whitespaceAndPunctuationSet// using the latest Swift 1.2 beta 2 syntax:varname:NSString?whilestringScanner.scanUpToCharactersFromSet(whitespaceAndPunctuationSet,intoString:&name){println(name)}// John// Paul// Ringo// George
NSMutableCharacterSet*whitespaceAndPunctuationSet=[NSMutableCharacterSetpunctuationCharacterSet];[whitespaceAndPunctuationSetformUnionWithCharacterSet:[NSCharacterSetwhitespaceAndNewlineCharacterSet]];NSScanner*stringScanner=[[NSScanneralloc]initWithString:@"John & Paul & Ringo & George."];stringScanner.charactersToBeSkipped=whitespaceAndPunctuationSet;NSString*name;while([stringScannerscanUpToCharactersFromSet:whitespaceAndPunctuationSetintoString:&name]){NSLog(@"%@",name);}// John// Paul// Ringo// George

The NSScanner API has methods for two use-cases: scanning for strings generally, or for numeric types specifically.

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 (and a point-offsetting helper we’ll use later):

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"extensionCGPoint{funcoffset(p:CGPoint)->CGPoint{returnCGPoint(x:x+p.x,y:y+p.y)}}
staticNSString*constsvgPathData=@"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";CGPointoffsetPoint(CGPointp1,CGPointp2){returnCGPointMake(p1.x+p2.x,p1.y+p2.y);}

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 bezierPathFromSVGPath 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:

funcbezierPathFromSVGPath(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()
-(UIBezierPath*)bezierPathFromSVGPath:(NSString*)str{NSScanner*scanner=[NSScannerscannerWithString:str];// skip commas and whitespaceNSMutableCharacterSet*skipChars=[NSMutableCharacterSetcharacterSetWithCharactersInString:@","];[skipCharsformUnionWithCharacterSet:[NSCharacterSetwhitespaceAndNewlineCharacterSet]];scanner.charactersToBeSkipped=skipChars;// the resulting bezier pathUIBezierPath*path=[UIBezierPathbezierPath];

With the setup out of the way, it’s time to start scanning. We start by scanning for a string made up of 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){
// instructions codes can be upper- or lower-caseNSCharacterSet*instructionSet=[NSCharacterSetcharacterSetWithCharactersInString:@"MCSQTAmcsqta"];NSString*instruction;// scan for an instruction codewhile([scannerscanCharactersFromSet:instructionSetintoString:&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}
doublex,y;NSMutableArray*points=[NSMutableArrayarray];// scan for pairs of Double, adding them as CGPoints to the points arraywhile([scannerscanDouble:&x]&&[scannerscanDouble:&y]){[pointsaddObject:[NSValuevalueWithCGPoint:CGPointMake(x,y)]];}// new point in pathif([instructionisEqualToString:@"M"]){[pathmoveToPoint:[points[0]CGPointValue]];}elseif([instructionisEqualToString:@"C"]){[pathaddCurveToPoint:[points[2]CGPointValue]controlPoint1:[points[0]CGPointValue]controlPoint2:[points[1]CGPointValue]];}elseif([instructionisEqualToString:@"c"]){CGPointnewPoint=offsetPoint(path.currentPoint,[points[2]CGPointValue]);CGPointcontrol1=offsetPoint(path.currentPoint,[points[0]CGPointValue]);CGPointcontrol2=offsetPoint(path.currentPoint,[points[1]CGPointValue]);[pathaddCurveToPoint:newPointcontrolPoint1:control1controlPoint2:control2];}}[pathapplyTransform:CGAffineTransformMakeScale(1,-1)];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 Core Foundation 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 working with Unmanaged instances for as brief a time as possible. When you call a function with an unmanaged return value, take... the underlying instance immediately and bind that to your variable or constant.

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()if// Get Rule - unretainedletbestFriendRecord:ABRecord=ABAddressBookGetPersonWithRecordID(addressBook,bestFriendID)?.takeUnretainedValue(),// Create Rule (Copy) - retainedletname=ABRecordCopyCompositeName(bestFriendRecord)?.takeRetainedValue()as?String{print("\(name): BFF!")// Rhonda Shorsheimer: BFF!}

With Swift’s 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"asCFString,"Second"asCFString).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 endCF_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"asCFString,"Second"asCFString)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.    - parameter bar: Consectetur adipisicing elit.    - returns: Sed do eiusmod tempor.*/funcfoo(bar:String)->AnyObject{...}

So what is this not-so-strange new documentation format? After a yearlong sojourn in the lands of reStructuredText, Xcode 7 has settled on a Swift-flavored version of Markdown.

Basic Markup

Documentation comments are distinguished by using /** ... */ for multi-line comments or /// for single-line comments. Inside comment blocks, the conventions you’ve gotten used to when writing Markdown everywhere else apply:

  • Paragraphs are separated by blank lines
  • Unordered lists can use a variety of bullet characters: -, +, *,
  • Ordered lists use Arabic numerals (1, 2, 3, …) followed by a period 1. or right parenthesis 1):
  • Headers can be marked with preceding # signs or by underlining with = or -.
  • Even links and images work, with web-based images pulled down and displayed directly in Xcode.
/**    # Lists    You can apply *italic*, **bold**, or `code` inline styles.    ## Unordered Lists    - Lists are great,    - but perhaps don't nest    - Sub-list formatting      - isn't the best.    ## Ordered Lists    1. Ordered lists, too    2. for things that are sorted;    3. Arabic numerals    4. are the only kind supported.*/

Parameters & Return Values

Xcode 7 recognizes and makes separate from a symbol’s description a few special fields. The parameters, return value, and a new “throws” section (to go with Swift 2.0’s new throws keyword) are broken out in the Quick Help popover and inspector when styled as a bulleted item followed by a colon (:).

  • Parameters: Start the line with Parameter <param name>: and the description of the parameter.
  • Return values: Start the line with Returns: and information about the return value.
  • Thrown errors: Start the line with Throws: and a description of the errors that can be thrown. Since Swift doesn’t type-check thrown errors beyond ErrorType conformance, it’s especially important to document errors properly.
/**    Repeats a string `times` times.    - Parameter str:   The string to repeat.    - Parameter times: The number of times to repeat `str`.    - Throws: `MyError.InvalidTimes` if the `times` parameter         is less than zero.    - Returns: A new string with `str` repeated `times` times.*/funcrepeatString(str:String,times:Int)throws->String{guardtimes>=0else{throwMyError.InvalidTimes}returnRepeat(count:5,repeatedValue:"Hello").joinWithSeparator("")}

A longer list of parameters can be broken out into a sublist by using the Parameters: prefix. Simply indent each parameter in a bulleted list below.

/// Returns the magnitude of a vector in three dimensions/// from the given components.////// - Parameters:///     - x: The *x* component of the vector.///     - y: The *y* component of the vector.///     - z: The *z* component of the vector.funcmagnitude3D(x:Double,y:Double,z:Double)->Double{returnsqrt(pow(x,2)+pow(y,2)+pow(z,2))}

Description Fields

Swift-flavored Markdown includes another set of field headers to break out particular sections of a type or method’s description, formatted just as Returns and Throws above. Loosely organized, the recognized headers are:

  • Algorithm/Safety Information:Precondition, Postcondition, Requires, Invariant, Complexity, Important, Warning
  • Metadata: Author, Authors, Copyright, Date, SeeAlso, Since, Version
  • General Notes & Exhortations:Attention, Bug, Experiment, Note, Remark, ToDo

No matter which you choose, all fields are rendered as a bold header followed by a block of text:

Field Header:
The text of the subfield is displayed starting on the next line.

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 four 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}

Fenced code blocks are also recognized, with three backticks (&grave;) or tildes (~) marking the beginning and end of a block:

/**    The perimeter of the `Shape` instance.    Computation depends on the shape of the instance, and is    equivalent to:     ```    // Circles:    let perimeter = circle.radius * 2 * CGFloat(M_PI)    // Other shapes:    let perimeter = shape.sides.map { $0.length }                               .reduce(0, combine: +)    ```*/varperimeter: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.        - Parameters:            - style: The style of the bicycle            - gearing: The gearing of the bicycle            - handlebar: The handlebar of the bicycle            - frameSize: The frame size of the bicycle, in centimeters        - Returns: A beautiful, brand-new bicycle, 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.        - Parameter 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: CustomStringConvertibleextensionBicycle:CustomStringConvertible{publicvardescription: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?returndescriptors.joinWithSeparator(", ")}}

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 storeprint(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 a terrific open-source command-line utility that transforms your project’s documentation comments into a set of Apple-like HTML documentation. Jazzy uses Xcode’s SourceKitService to read your beautifully written type and method descriptions. Install Jazzy as a gem, then simply run from the root of your project folder.

$ [sudo] gem install jazzy
$ jazzy
Running xcodebuild
Parsing ...
building site
jam out ♪♫ to your fresh new docs in `docs`

Take a peek at a Jazzy-generated docset for the Bicycle class.


Although the tooling and documentation around Swift is still rapidly evolving, one would be wise to adopt good habits early, by using the new Markdown capabilities 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.

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 Bundle.main. For example, we can easily load a JSON file filled with weather data:

ifletjsonURL=Bundle.main.url(forResource:"weather",withExtension:"json"),letjsonData=try?Data(contentsOf:jsonURL),letjson=(try?JSONSerialization.jsonObject(with:jsonData))as?[String:AnyObject]{// ...}

Shared

The contents of a “Shared Playground Data” directory inside your “Documents” folder are available to any Playground you create. Access files in the shared folder via the playgroundSharedDataDirectory 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”:

letimageURL=playgroundSharedDataDirectory.appendingPathComponent("image.png")ifletimageData=try?Data(contentsOf:imageURL),letimage=UIImage(data:imageData){// ...}

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?

A Playground can show changing values as either a graph or a list. Choose Editor → Show Result For Current Line or click the “Show Result” toggle in the resuls pane to capture the current line’s values and display them 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, import the PlaygroundSupport module and set the needsIndefiniteExecution property of the current page to true:

importPlaygroundSupportPlaygroundPage.current.needsIndefiniteExecution=trueleturl=URL(string:"http://httpbin.org/image/png")!lettask=URLSession.shared.dataTask(with:url){data,_,_inguardletdata=dataelse{return}letimage=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.

Just like 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.com)- [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"

If you switch locales, you’ll need to reset the localized date format to get the correct template for the new locale:

fullFormatter.locale=NSLocale(localeIdentifier:"de_DE")fullFormatter.setLocalizedDateFormatFromTemplate("yyyyMMMMddhhmm")shortFormatter.locale=NSLocale(localeIdentifier:"de_DE")shortFormatter.setLocalizedDateFormatFromTemplate("yyMMMM")fullFormatter.stringFromDate(now)// "23. Juni 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 to 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]doubleValue];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 starting with the name ‘Apple Store’:

letpredicate=NSPredicate(format:"name BEGINSWITH 'Apple Store'")letquery=CKQuery(recordType:"Place",predicate:predicate)publicDB.performQuery(query,inZoneWithID:nil){results,errorin// ...}
NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"name BEGINSWITH '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 something starting with “party” around him or her; for example “party at Sarah’s house”. This is possible with CloudKit—the framework already provides something to achieve this using the CKSubscription class:

letpredicate=NSPredicate(format:"description BEGINSWITH '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 BEGINSWITH '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 is 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 development long before that at Apple. What were the month and year of the first commit to the Swift repository?

  10. After Chris Lattner, who was the second contributor to Swift? When was their first commit?

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:

    // 1replacingCharacters(in:NSRange,with:String)// 2funcreplacingCharacters(inrange:NSRange,        withreplacement:String)->String
  7. One of:

    // 1letqueue=DispatchQueue(label:"quiz")queue.async{print("Hello, world!")}// 2DispatchQueue.main.async{print("Hello, world!")}
  8. 3,012 as of June 14th, 2016 (correct if between 2,912 and 3,112)—check here for the current stats

  9. July 2010 (1 point if correct 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.

Viewing all 384 articles
Browse latest View live