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

Swift Comparison Protocols

$
0
0

Objective-C required us to wax philosophic about the nature of equality and identity. To the relief of any developer less inclined towards handwavy treatises, this is not as much the case for Swift.

In Swift, Equatable is a fundamental type, from which Comparable and Hashable are both derived. Together, these protocols form the central point of comparison throughout the language.


Equatable

Values of the Equatable type can be evaluated for equality and inequality. Declaring a type as equatable bestows several useful abilities, notably the ability values of that type to be found in a containing Array.

For a type to be Equatable, there must exist an implementation of the == operator function, which accepts a matching type:

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

For value types, equality is determined by evaluating the equality of each component property. As an example, consider a Complex type, which takes a generic type T, which conforms to SignedNumberType:

SignedNumberType is a convenient choice for a generic number type, as it inherits from both Comparable (and thus Equatable, as described in the section) and IntegerLiteralConvertible, which Int, Double, and Float all conform to.

structComplex<T:SignedNumberType>{letreal:Tletimaginary:T}

Since a complex number is comprised of a real and imaginary component, two complex numbers are equal if and only if their respective real and imaginary components are equal:

extensionComplex:Equatable{}// MARK: Equatablefunc==<T>(lhs:Complex<T>,rhs:Complex<T>)->Bool{returnlhs.real==rhs.real&&lhs.imaginary==rhs.imaginary}

The result:

leta=Complex<Double>(real:1.0,imaginary:2.0)letb=Complex<Double>(real:1.0,imaginary:2.0)a==b// truea!=b// false

As described in the article about Swift Default Protocol Implementations, an implementation of != is automatically derived from the provided == operator by the standard library.

For reference types, the equality becomes conflated with identity. It makes sense that two Name structs with the same values would be equal, but two Person objects can have the same name, but be different people.

For Objective-C-compatible object types, the == operator is already provided from the isEqual: method:

classObjCObject:NSObject{}ObjCObject()==ObjCObject()// false

For Swift reference types, equality can be evaluated as an identity check on an ObjectIdentifier constructed with an instance of that type:

classObject:Equatable{}// MARK: Equatablefunc==(lhs:Object,rhs:Object)->Bool{returnObjectIdentifier(lhs)==ObjectIdentifier(rhs)}Object()==Object()// false

Comparable

Building on Equatable, the Comparable protocol allows for more specific inequality, distinguishing cases where the left hand value is greater than or less than the right hand value.

Types conforming to the Comparable protocol provide the following operators:

func<=(lhs:Self,rhs:Self)->Boolfunc>(lhs:Self,rhs:Self)->Boolfunc>=(lhs:Self,rhs:Self)->Bool

What's interesting about this list, however, is not so much what is included, but rather what's missing.

The first and perhaps most noticeable omission is ==, since >= is a logical disjunction of > and == comparisons. As a way of reconciling this, Comparable inherits from Equatable, which provides ==.

The second omission is a bit more subtle, and is actually the key to understanding what's going on here: <. What happened to the "less than" operator? It's defined by the _Comparable protocol. Why is this significant? As described in the article about Swift Default Protocol Implementations, the Swift Standard Library provides a default implementation of the Comparable protocol based entirely on the existential type _Comparable. This is actually really clever. Since the implementations of all of the comparison functions can be derived from just < and ==, all of that functionality is made available automatically through type inference.

Contrast this with, for example, how Ruby derives equality and comparison operators from a single operator, <=> (a.k.a the "UFO operator"). Here's how this could be implemented in Swift.

As a more complex example, consider a CSSSelector struct, which implements cascade ordering of selectors:

importFoundationstructCSSSelector{letselector:StringstructSpecificity{letid:Intlet`class`:Intletelement:Intinit(_components:[String]){var(id,`class`,element)=(0,0,0)fortokenincomponents{iftoken.hasPrefix("#"){id++}elseiftoken.hasPrefix("."){`class`++}else{element++}}self.id=idself.`class`=`class`self.element=element}}letspecificity:Specificityinit(_string:String){self.selector=string// Naïve tokenization, ignoring operators, pseudo-selectors, and `style=`.letcomponents:[String]=self.selector.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet())self.specificity=Specificity(components)}}

Where as CSS selectors are evaluated by specificity rank and order, two selectors are only really equal if they resolve to the same elements:

extensionCSSSelector:Equatable{}// MARK: Equatablefunc==(lhs:CSSSelector,rhs:CSSSelector)->Bool{// Naïve equality that uses string comparison rather than resolving equivalent selectorsreturnlhs.selector==rhs.selector}

Instead, selectors are actually compared in terms of their specificity:

extensionCSSSelector.Specificity:Comparable{}// MARK: Comparablefunc<(lhs:CSSSelector.Specificity,rhs:CSSSelector.Specificity)->Bool{returnlhs.id<rhs.id||lhs.`class`<rhs.`class`||lhs.element<rhs.element}// MARK: Equatablefunc==(lhs:CSSSelector.Specificity,rhs:CSSSelector.Specificity)->Bool{returnlhs.id==rhs.id&&lhs.`class`==rhs.`class`&&lhs.element==rhs.element}

Bringing everything together:

For clarity, assume CSSSelectorconforms to StringLiteralConvertible.

leta:CSSSelector="#logo"letb:CSSSelector="html body #logo"letc:CSSSelector="body div #logo"letd:CSSSelector=".container #logo"b==c// falseb.specificity==c.specificity// truec.specificity<a.specificity// falsed.specificity>c.specificity// true

Hashable

Another important protocol derived from Equatable is Hashable.

Only Hashable types can be stored as the key of a Swift Dictionary:

structDictionary<Key:Hashable,Value>:CollectionType,DictionaryLiteralConvertible{...}

For a type to conform to Hashable,it must provide a getter for the hashValue property.

protocolHashable:Equatable{/// Returns the hash value.  The hash value is not guaranteed to be stable/// across different invocations of the same program.  Do not persist the hash/// value across program runs.////// The value of `hashValue` property must be consistent with the equality/// comparison: if two values compare equal, they must have equal hash/// values.varhashValue:Int{get}}

Determining the optimal hashing value is way outside the scope of this article. Fortunately, most values can derive an adequate hash value from an XOR of the hash values of its component properties.

The following built-in Swift types implement hashValue:

  • Double
  • Float, Float80
  • Int, Int8, Int16, Int32, Int64
  • UInt, UInt8, UInt16, UInt32, UInt64
  • String
  • UnicodeScalar
  • ObjectIdentifier

Based on this, here's how a struct representing Binomial Nomenclature in Biological Taxonomy:

structBinomen{letgenus:Stringletspecies:String}// MARK: HashableextensionBinomen:Hashable{varhashValue:Int{returngenus.hashValue^species.hashValue}}// MARK: Equatablefunc==(lhs:Binomen,rhs:Binomen)->Bool{returnlhs.genus==rhs.genus&&rhs.species==rhs.species}

Being able to hash this type makes it possible to key common name to the "Latin name":

varcommonNames:[Binomen:String]=[:]commonNames[Binomen(genus:"Canis",species:"lupis")]="Grey Wolf"commonNames[Binomen(genus:"Canis",species:"rufus")]="Red Wolf"commonNames[Binomen(genus:"Canis",species:"latrans")]="Coyote"commonNames[Binomen(genus:"Canis",species:"aureus")]="Golden Jackal"

UIAlertController

$
0
0

Did you know that UIAlertView and UIActionSheet (as well as their respective delegate protocols) are deprecated in iOS 8?

It's true. ⌘-click on UIAlertView or UIActionSheet in your code, and check out the top-level comment:

UIAlertView is deprecated. Use UIAlertController with a preferredStyle of UIAlertControllerStyleAlert instead.

Wondering why Xcode didn't alert you to this change? Just read down to the next line:

@availability(iOS,introduced=2.0)

Although these classes are technically deprecated, this is not communicated in the @availability attribute. This should be of little surprise, though; UIAlertView has always played it fast and loose.

From its very inception, UIAlertView has been laden with vulgar concessions, sacrificing formality and correctness for the whims of an eager developer audience. Its delegate protocol conformance was commented out of its initializer (delegate:(id /* <UIAlertViewDelegate */)delegate). And what protocol methods that could be implemented invoked the cringeworthy notion of having "clicked" rather than "tapped" a buttonAtIndex:. This, and trailing variable-length arguments for otherButtonTitles, awkward management of button indexes, a -show method with no regard for the view hierarchy... the list goes on.

UIActionSheet was nearly as bad, though developers can't be bothered to remember what the heck that control is called half the time, much less complain about its awkward parts.

As such, the introduction of UIAlertController should be met like an army liberating a city from occupation. Not only does it improve on the miserable APIs of its predecessors, but it carves a path forward to deal with the UIKit interface singularity brought on by the latest class of devices.

This week's article takes a look at UIAlertController, showing first how to port existing alert behavior, and then how this behavior can be extended.


UIAlertController replaces both UIAlertView and UIActionSheet, thereby unifying the concept of alerts across the system, whether presented modally or in a popover.

Unlike the classes it replaces, UIAlertController is a subclass of UIViewController. As such, alerts now benefit from the configurable functionality provided with view controller presentation.

UIAlertController is initialized with a title, message, and whether it prefers to be displayed as an alert or action sheet. Alert views are presented modally in the center of their presenting view controllers, whereas action sheets are anchored to the bottom. Alerts can have both buttons and text fields, while action sheets only support buttons.

Rather than specifying all of an alert's buttons in an initializer, instances of a new class, UIAlertAction, are added after the fact. Refactoring the API in this way allows for greater control over the number, type, and order of buttons. It also does away with the delegate pattern favored by UIAlertView& UIActionSheet in favor or much more convenient completion handlers.

Comparing the Old and New Ways to Alerts

A Standard Alert

A Standard Alert

The Old Way: UIAlertView

letalertView=UIAlertView(title:"Default Style",message:"A standard alert.",delegate:self,cancelButtonTitle:"Cancel",otherButtonTitles:"OK")alertView.alertViewStyle=.DefaultalertView.show()
// MARK: UIAlertViewDelegatefuncalertView(alertView:UIAlertView,clickedButtonAtIndexbuttonIndex:Int){switchbuttonIndex{// ...}}

The New Way: UIAlertController

letalertController=UIAlertController(title:"Default Style",message:"A standard alert.",preferredStyle:.Alert)letcancelAction=UIAlertAction(title:"Cancel",style:.Cancel){(action)in// ...}alertController.addAction(cancelAction)letdestroyAction=UIAlertAction(title:"Destroy",style:.Destructive){(action)inprintln(action)}alertController.addAction(destroyAction)letOKAction=UIAlertAction(title:"OK",style:.Default){(action)in// ...}alertController.addAction(OKAction)self.presentViewController(alertController,animated:true){// ...}

A Standard Action Sheet

A Standard Action Sheet

UIActionSheet

letactionSheet=UIActionSheet(title:"Takes the appearance of the bottom bar if specified; otherwise, same as UIActionSheetStyleDefault.",delegate:self,cancelButtonTitle:"Cancel",destructiveButtonTitle:"Destroy",otherButtonTitles:"OK")actionSheet.actionSheetStyle=.DefaultactionSheet.showInView(self.view)
// MARK: UIActionSheetDelegatefuncactionSheet(actionSheet:UIActionSheet,clickedButtonAtIndexbuttonIndex:Int){switchbuttonIndex{...}}

UIAlertController

letalertController=UIAlertController(title:nil,message:"Takes the appearance of the bottom bar if specified; otherwise, same as UIActionSheetStyleDefault.",preferredStyle:.ActionSheet)letcancelAction=UIAlertAction(title:"Cancel",style:.Cancel){(action)in// ...}alertController.addAction(cancelAction)letOKAction=UIAlertAction(title:"OK",style:.Default){(action)in// ...}alertController.addAction(OKAction)self.presentViewController(alertController,animated:true){// ...}

New Functionality

UIAlertController is not just a cleanup of pre-existing APIs, it's a generalization of them. Previously, one was constrained to whatever presets were provided (swizzling in additional functionality at their own risk). With UIAlertController, it's possible to do a lot more out-of-the-box:

Alert with Destructive Button

Alert with Destructive Button

The type of an action is specified UIAlertActionStyle has three values:

  • .Default: Apply the default style to the action’s button.
  • .Cancel: Apply a style that indicates the action cancels the operation and leaves things unchanged.
  • .Destructive: Apply a style that indicates the action might change or delete data.

So, to add a destructive action to a modal alert, just add a UIAlertAction with style .Destructive:

letalertController=UIAlertController(title:"Title",message:"Message",preferredStyle:.Alert)letcancelAction=UIAlertAction(title:"Cancel",style:.Cancel){(action)inprintln(action)}alertController.addAction(cancelAction)letdestroyAction=UIAlertAction(title:"Destroy",style:.Destructive){(action)inprintln(action)}alertController.addAction(destroyAction)self.presentViewController(alertController,animated:true){// ...}

Alert with >2 Buttons

Alert with More Than 2 Buttons

With one or two actions, buttons in an alert are stacked horizontally. Any more than that, though, and it takes on a display characteristic closer to an action sheet:

letoneAction=UIAlertAction(title:"One",style:.Default){(_)in}lettwoAction=UIAlertAction(title:"Two",style:.Default){(_)in}letthreeAction=UIAlertAction(title:"Three",style:.Default){(_)in}letcancelAction=UIAlertAction(title:"Cancel",style:.Cancel){(_)in}alertController.addAction(oneAction)alertController.addAction(twoAction)alertController.addAction(threeAction)alertController.addAction(cancelAction)

Creating a Login Form

Creating a Login Form

iOS 5 added the alertViewStyle property to UIAlertView, which exposed much sought-after private APIs that allowed login and password fields to be displayed in an alert, as seen in several built-in system apps.

In iOS 8, UIAlertController can add text fields with the addTextFieldWithConfigurationHandler method:

letloginAction=UIAlertAction(title:"Login",style:.Default){(_)inletloginTextField=alertController.textFields![0]asUITextFieldletpasswordTextField=alertController.textFields![1]asUITextFieldlogin(loginTextField.text,passwordTextField.text)}loginAction.enabled=falseletforgotPasswordAction=UIAlertAction(title:"Forgot Password",style:.Destructive){(_)in}letcancelAction=UIAlertAction(title:"Cancel",style:.Cancel){(_)in}alertController.addTextFieldWithConfigurationHandler{(textField)intextField.placeholder="Login"NSNotificationCenter.defaultCenter().addObserverForName(UITextFieldTextDidChangeNotification,object:textField,queue:NSOperationQueue.mainQueue()){(notification)inloginAction.enabled=textField.text!=""}}alertController.addTextFieldWithConfigurationHandler{(textField)intextField.placeholder="Password"textField.secureTextEntry=true}alertController.addAction(loginAction)alertController.addAction(forgotPasswordAction)alertController.addAction(cancelAction)

Creating a Sign Up Form

Creating a Sign Up Form

UIAlertController goes even further to allow any number of text fields, each with the ability to be configured and customized as necessary. This makes it possible to create a fully-functional signup form in a single modal alert:

alertController.addTextFieldWithConfigurationHandler{(textField)intextField.placeholder="Email"textField.keyboardType=.EmailAddress}alertController.addTextFieldWithConfigurationHandler{(textField)intextField.placeholder="Password"textField.secureTextEntry=true}alertController.addTextFieldWithConfigurationHandler{(textField)intextField.placeholder="Password Confirmation"textField.secureTextEntry=true}

Though, it must be said, caveat implementor. Just because you can implement a signup form in an alert doesn't mean you should. Suck it up and use a view controller, like you're supposed to.

Caveats

Attempting to add a text field to an alert controller with style .ActionSheet will throw the following exception:

Terminating app due to uncaught exception NSInternalInconsistencyException, reason: 'Text fields can only be added to an alert controller of style UIAlertControllerStyleAlert'

Likewise, attempting to add more than one .Cancel action to either an alert or action sheet will raise:

Terminating app due to uncaught exception NSInternalInconsistencyException, reason: 'UIAlertController can only have one action with a style of UIAlertActionStyleCancel'

Swift System Version Checking

$
0
0

While it's not accurate to say that Swift is "Objective-C without the C", it's for lack of resemblance to Objective-C, not the absence of C. Swift is vehementlynot C.

Swift certainly draws inspiration from Haskell, Rust, Python, D, and other modern languages, but one can perhaps best understand the language as a rejection of everything that's broken in C:

  • C is unsafe by default. Swift is safe by default (hence the unsafe naming of pointer manipulation functions).
  • C has undefined behavior. Swift has well-defined behavior(or at least theoretically; the compiler tools still have some catching up to do).
  • C uses preprocessor directives capable of unspeakable evil. Swift has a safe subset of preprocessor directives.

One could go as far to say that Swift's type system was specifically designed out of spite for C++.

In Objective-C, checking for the availability of an API was accomplished through a combination of C preprocessor directives, conditionals on class, respondsToSelector:, and instancesRespondToSelector::

#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000if([NSURLSessionclass]&&[NSURLSessionConfigurationrespondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]){// ...}#endif

However, as noted previously, Swift's compiler directives are extremely constrained, allowing only for compiler flags and conditional compilation against specific operating systems and architectures:

#if DEBUGprintln("OTHER_SWIFT_FLAGS = -D DEBUG")#endif
FunctionValid Arguments
os()OSX, iOS
arch()x86_64, arm, arm64, i386
#if os(iOS)varimage:UIImage?#elseif os(OSX)varimage:NSImage?#endif

Unfortunately, os() does not offer any insight into the specific version of OS X or iOS, which means that checks must be made at runtime. And with Swift's less-forgiving treatment of nil, checking for constants Objective-C-style results in a crash.

So how do you check the system version in Swift to determine API availability? Read on to find out.


NSProcessInfo

Anticipating the need for a Swift-friendly API for determining API version at runtime, iOS 8 introduces the operatingSystemVersion property and isOperatingSystemAtLeastVersion method on NSProcessInfo. Both APIs use a new NSOperatingSystemVersion value type, which contains the majorVersion, minorVersion, and patchVersion.

Apple software releases follow semantic versioning conventions.

isOperatingSystemAtLeastVersion

For a simple check, like "is this app running on iOS 8?", isOperatingSystemAtLeastVersion is the most straightforward approach.

ifNSProcessInfo().isOperatingSystemAtLeastVersion(NSOperatingSystemVersion(majorVersion:8,minorVersion:0,patchVersion:0)){println("iOS >= 8.0.0")}

operatingSystemVersion

For more involved version comparison, the operatingSystemVersion can be inspected directly. Combine this with Swift pattern matching and switch statements for syntactic concision:

letos=NSProcessInfo().operatingSystemVersionswitch(os.majorVersion,os.minorVersion,os.patchVersion){case(8,_,_):println("iOS >= 8.0.0")case(7,0,_):println("iOS >= 7.0.0, < 7.1.0")case(7,_,_):println("iOS >= 7.1.0, < 8.0.0")default:println("iOS < 7.0.0")}

UIDevice systemVersion

Ironically, the new NSProcessInfo APIs aren't especially useful at the time of writing, since they're unavailable for iOS 7.

As an alternative, one can use the systemVersion property UIDevice:

switchUIDevice.currentDevice().systemVersion.compare("8.0.0",options:NSStringCompareOptions.NumericSearch){case.OrderedSame,.OrderedDescending:println("iOS >= 8.0")case.OrderedAscending:println("iOS < 8.0")}

Use NSStringCompareOptions.NumericSearch when comparing version number strings to ensure that, for example, "2.5" < "2.10".

String comparison and NSComparisonResult aren't as sexy as a dedicated value type like NSOperatingSystemVersion, but it gets the job done all the same.

NSAppKitVersionNumber

Another approach to determining API availability is to check framework version numbers. Unfortunately, Foundation's NSFoundationVersionNumber and Core Foundation's kCFCoreFoundationVersionNumber have historically been out of date, missing constants for past OS releases.

This is a dead-end for iOS, but OS X can pretty reliably check against the version of AppKit, with NSAppKitVersionNumber:

ifrint(NSAppKitVersionNumber)>NSAppKitVersionNumber10_9{println("OS X >= 10.10")}

Apple uses rint in sample code to round off version numbers for NSAppKitVersionNumber comparison.


To summarize, here's what you need to know about checking the system version in Swift:

  • Use #if os(iOS) preprocessor directives to distinguish between iOS (UIKit) and OS X (AppKit) targets.
  • For minimum deployment targets of iOS 8.0 or above, use NSProcessInfooperatingSystemVersion or isOperatingSystemAtLeastVersion.
  • For minimum deployment targets of iOS 7.1 or below, use compare with NSStringCompareOptions.NumericSearch on UIDevicesystemVersion.
  • For OS X deployment targets, compare NSAppKitVersionNumber against available AppKit constants.

Inter-Process Communication

$
0
0

IPC Postman, illustrated by Conor Heelan

In many ways, the story of Apple has been about fusing together technologies through happy accidents of history to create something better than before: OS X as a hybrid of MacOS & NeXTSTEP. Objective-C as the combination of Smalltalk's OOP paradigm and C. iCloud as the byproduct of MobileMe and actual clouds (presumably).

While this is true for many aspects of Apple's technology stack, inter-process communication is a flagrant counter-example.

Rather than taking the best of what was available at each juncture, solutions just kinda piled up. As a result, a handful of overlapping, mutually-incompatible IPC technologies are scattered across various abstraction layers. footnote:[Whereas all of these are available on OS X, only Grand Central Dispatch and Pasteboard (albeit to a lesser extent) can be used on iOS.]

  • Mach Ports
  • Distributed Notifications
  • Distributed Objects
  • AppleEvents & AppleScript
  • Pasteboard
  • XPC

Ranging from low-level kernel abstractions to high-level, object-oriented APIs, they each have particular performance and security characteristics. But fundamentally, they're all mechanisms for transmitting and receiving data from beyond a context boundary.

Mach Ports

All inter-process communication ultimately relies on functionality provided by Mach kernel APIs.

Mach ports are light-weight and powerful, but poorly documented footnote:[How poorly documented? The most up-to-date authoritative resource was a Mach 3.0 PostScript file circa 1990 tucked away on a Carnegie Mellon University FTP server.] and inconvenient to use directly. footnote:[How inconvenient? Well, just look at the code samples.]

Sending a message over a given Mach port comes down to a single mach_msg_send call, but it takes a bit of configuration in order to build the message to be sent:

natural_tdata;mach_port_tport;struct{mach_msg_header_theader;mach_msg_body_tbody;mach_msg_type_descriptor_ttype;}message;message.header=(mach_msg_header_t){.msgh_remote_port=port,.msgh_local_port=MACH_PORT_NULL,.msgh_bits=MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,0),.msgh_size=sizeof(message)};message.body=(mach_msg_body_t){.msgh_descriptor_count=1};message.type=(mach_msg_type_descriptor_t){.pad1=data,.pad2=sizeof(data)};mach_msg_return_terror=mach_msg_send(&message.header);if(error==MACH_MSG_SUCCESS){// ...}

Things are a little easier on the receiving end, since the message only needs to be declared, not initialized:

mach_port_tport;struct{mach_msg_header_theader;mach_msg_body_tbody;mach_msg_type_descriptor_ttype;mach_msg_trailer_ttrailer;}message;mach_msg_return_terror=mach_msg_receive(&message.header);if(error==MACH_MSG_SUCCESS){natural_tdata=message.type.pad1;// ...}

Fortunately, higher-level APIs for Mach ports are provided by Core Foundation and Foundation. CFMachPort / NSMachPort are wrappers on top of the kernel APIs that can be used as a runloop source, while CFMessagePort / NSMessagePort facilitate synchronous communication between two ports.

CFMessagePort is actually quite nice for simple one-to-one communication. In just a few lines of code, a local named port can be attached as a runloop source to have a callback run each time a message is received:

staticCFDataRefCallback(CFMessagePortRefport,SInt32messageID,CFDataRefdata,void*info){// ...}CFMessagePortReflocalPort=CFMessagePortCreateLocal(nil,CFSTR("com.example.app.port.server"),Callback,nil,nil);CFRunLoopSourceRefrunLoopSource=CFMessagePortCreateRunLoopSource(nil,localPort,0);CFRunLoopAddSource(CFRunLoopGetCurrent(),runLoopSource,kCFRunLoopCommonModes);

Sending data is straightforward as well. Just specify the remote port, the message payload, and timeouts for sending and receiving. CFMessagePortSendRequest takes care of the rest:

CFDataRefdata;SInt32messageID=0x1111;// ArbitraryCFTimeIntervaltimeout=10.0;CFMessagePortRefremotePort=CFMessagePortCreateRemote(nil,CFSTR("com.example.app.port.client"));SInt32status=CFMessagePortSendRequest(remotePort,messageID,data,timeout,timeout,NULL,NULL);if(status==kCFMessagePortSuccess){// ...}

Distributed Notifications

There are many ways for objects to communicate with one another in Cocoa:

There is, of course, sending a message directly. There are also the target-action, delegate, and callbacks, which are all loosely-coupled, one-to-one design patterns. KVO allows for multiple objects to subscribe to events, but it strongly couples those objects together. Notifications, on the other hand, allow messages to be broadcast globally, and intercepted by any object that knows what to listen for. footnote:[It's pretty astonishing just how many notifications are fired off during the lifecycle of an application. Try adding NSNotificationCenter -addObserverForName:object:queue:usingBlock with nil values for name and object just as an application launches, and see just how many times that block fires.]

Each application manages its own NSNotificationCenter instance for infra-application pub-sub. But there is also a lesser-known Core Foundation API, CFNotificationCenterGetDistributedCenter that allows notifications to be communicated system-wide as well.

To listen for notifications, add an observer to the distributed notification center by specifying the notification name to listen for, and a function pointer to execute each time a notification is received:

staticvoidCallback(CFNotificationCenterRefcenter,void*observer,CFStringRefname,constvoid*object,CFDictionaryRefuserInfo){// ...}CFNotificationCenterRefdistributedCenter=CFNotificationCenterGetDistributedCenter();CFNotificationSuspensionBehaviorbehavior=CFNotificationSuspensionBehaviorDeliverImmediately;CFNotificationCenterAddObserver(distributedCenter,NULL,Callback,CFSTR("notification.identifier"),NULL,behavior);

Sending a distributed notification is even simpler; just post the identifier, object, and user info:

void*object;CFDictionaryRefuserInfo;CFNotificationCenterRefdistributedCenter=CFNotificationCenterGetDistributedCenter();CFNotificationCenterPostNotification(distributedCenter,CFSTR("notification.identifier"),object,userInfo,true);

Of all of the ways to link up two applications, distributed notifications are by far the easiest. It wouldn't be a great idea to use them to send large payloads, but for simple tasks like synchronizing preferences or triggering a data fetch, distributed notifications are perfect.

Distributed Objects

Distributed Objects (DO) is a remote messaging feature of Cocoa that had its heyday back in the mid-90's with NeXT. And though its not widely used any more, the dream of totally frictionless IPC is still unrealized in our modern technology stack.

Vending an object with DO is just a matter of setting up an NSConnection and registering it with a particular name:

@protocolProtocol;

id<Protocol>vendedObject;NSConnection*connection=[[NSConnectionalloc]init];[connectionsetRootObject:vendedObject];[connectionregisterName:@"server"];

Another application would then create a connection registered for that same registered name, and immediately get an atomic proxy that functioned as if it were that original object:

idproxy=[NSConnectionrootProxyForConnectionWithRegisteredName:@"server"host:nil];[proxysetProtocolForProxy:@protocol(Protocol)];

Any time a distributed object proxy is messaged, a Remote Procedure Call (RPC) would be made over the NSConnection to evaluate the message against the vended object and return the result back to the proxy. footnote:[Behind the scenes, a shared NSPortNameServer instance managed by the operating system was responsible for hooking up named connections.]

Distributed Objects are simple, transparent, and robust. And they would have been a flagpole feature of Cocoa had any of it worked as advertised.

In reality, Distributed Objects can't be used like local objects, if only because any message sent to a proxy could result in an exception being thrown. Unlike other languages, Objective-C doesn't use exceptions for control flow. As a result, wrapping everything in a @try/@catch is a poor fit to the conventions of Cocoa.

DO is awkward for other reasons, too. The divide between objects and primitives is especially pronounced when attempting to marshal values across a connection. Also, connections are totally unencrypted, and the lack of extensibility for the underlying communication channels makes it a deal-breaker for most serious usage.

All that's really left are traces of the annotations used by Distributed Objects to specify the proxying behavior of properties and method parameters:

  • in: Argument is used as input, but not referenced later
  • out: Argument is used to return a value by reference
  • inout: Argument is used as input and returned by reference
  • const: Argument is constant
  • oneway: Return without blocking for result
  • bycopy: Return a copy of the object
  • byref: Return a proxy of the object

AppleEvents & AppleScript

AppleEvents are the most enduring legacies of the classic Macintosh operating system. Introduced in System 7, AppleEvents allowed apps to be controlled locally using AppleScript, or remotely using a feature called Program Linking. To this day, AppleScript, using the Cocoa Scripting Bridge, remains the most direct way to programmatically interact with OS X applications. footnote:[Mac OS's Apple Event Manager provided the initial low-level transport mechanism for AppleEvents, but was reimplemented on top of Mach ports for OS X.]

That said, it's easily one of the weirdest technologies to work with.

AppleScript uses a natural language syntax, intended to be more accessible to non-programmers. And while it does succeed in communicating intent in a human-understandable way, it's a nightmare to write.

To get a better sense of the nature of the beast, here's how to tell Safari to open a URL in the active tab in the frontmost window:

tellapplication"Safari"settheURLofthefrontdocumentto"http://nshipster.com"endtell

In many ways, AppleScript's natural language syntax is more of a liability than an asset. English, much like any other spoken language, has a great deal of redundancy and ambiguity built into normal constructions. While this is perfectly acceptable for humans, computers have a tough time resolving all of this.

Even for a seasoned Objective-C developer, it's nearly impossible to write AppleScript without constantly referencing documentation or sample code.

Fortunately, the Scripting Bridge provides a proper programming interface for Cocoa applications.

Cocoa Scripting Bridge

In order to interact with an application through the Scripting Bridge, a programming interface must first be generated:

$ sdef /Applications/Safari.app | sdp -fh --basename Safari

sdef generates scripting definition files for an application. These files can then be piped into sdp to be converted into another format—in this case, a C header. The resulting .h file can then be added and #import-ed into a project to get a first-class object interface to that application.

Here's the same example as before, expressed using the Cocoa Scripting Bridge:

#import "Safari.h"SafariApplication*safari=[SBApplicationapplicationWithBundleIdentifier:@"com.apple.Safari"];for(SafariWindow*windowinsafari.windows){if(window.visible){window.currentTab.URL=[NSURLURLWithString:@"http://nshipster.com"];break;}}

It's a bit more verbose than AppleScript, but this is much easier to integrate into an existing codebase. It's also a lot clearer to understand how this same code could be adapted to slightly different behavior (though that could just be the effect of being more familiar with Objective-C).

Alas, AppleScript's star appears to be falling, as recent releases of OS X and iWork applications have greatly curtailed their scriptability. At this point, it's unlikely that adding support in your own applications will be worth it.

Pasteboard

Pasteboard is the most visible inter-process communication mechanism on OS X and iOS. Whenever a user copies or pastes a piece of text, an image, or a document between applications, an exchange of data from one process to another over mach ports is being mediated by the com.apple.pboard service.

On OS X there's NSPasteboard, and on iOS there's UIPasteboard. They're pretty much the same, although like most counterparts, iOS provides a cleaner, more modern set of APIs that are slightly less capable than what's found on OS X.

Programmatically writing to the Pasteboard is nearly as simple as invoking Edit > Copy in a GUI application:

NSImage*image;NSPasteboard*pasteboard=[NSPasteboardgeneralPasteboard];[pasteboardclearContents];[pasteboardwriteObjects:@[image]];

The reciprocal paste action is a bit more involved, requiring an iteration over the Pasteboard contents:

NSPasteboard*pasteboard=[NSPasteboardgeneralPasteboard];if([pasteboardcanReadObjectForClasses:@[[NSImageclass]]options:nil]){NSArray*contents=[pasteboardreadObjectsForClasses:@[[NSImageclass]]options:nil];NSImage*image=[contentsfirstObject];}

What makes Pasteboard especially compelling as a mechanism for transferring data is the notion of simultaneously providing multiple representations of content copied onto a pasteboard. For example, a selection of text may be copied as both rich text (RTF) and plain text (TXT), allowing, for example, a WYSIWYG editor to preserve style information by grabbing the rich text representation, and a code editor to use just the plain text representation.

These representations can even be provided on an on-demand basis by conforming to the NSPasteboardItemDataProvider protocol. This allows derivative representations, such as plain text from rich text, to be generated only as necessary.

Each representation is identified by a Unique Type Identifier (UTI), a concept discussed in greater detail in the next chapter.

XPC

XPC is the state-of-the-art for inter-process communication in the SDKs. Its architectural goals are to avoid long-running process, to adapt to the available resources, and to lazily initialize wherever possible. The motivation to incorporate XPC into an application is not to do things that are otherwise impossible, but to provide better privilege separation and fault isolation for inter-process communication.

It's a replacement for NSTask, and a whole lot more.

Introduced in 2011, XPC has provided the infrastructure for the App Sandbox on OS X, Remote View Controllers on iOS, and App Extensions on both. It is also widely used by system frameworks and first-party applications:

$ find /Applications -name \*.xpc

By surveying the inventory of XPC services in the wild, one can get a much better understanding of opportunities to use XPC in their own application. Common themes in applications emerge, like services for image and video conversion, system calls, webservice integration, and 3rd party authentication.

XPC takes responsibility for both inter-process communication and service lifecycle management. Everything from registering a service, getting it running, and communicating with other services is handled by launchd. An XPC service can be launched on demand, or restarted if they crash, or terminated if they idle. As such, services should be designed to be completely stateless, so as to allow for sudden termination at any point of execution.

As part of the new security model adopted by iOS and backported in OS X, XPC services are run with the most restricted environment possible by default: no file system access, no network access, and no root privilege escalation. Any capabilities must be whitelisted by a set of entitlements.

XPC can be accessed through either the libxpc C API, or the NSXPCConnection Objective-C API. footnote:[Though one should always try to use the highest-level API available to accomplish a particular task, this book does have the words "Low-Level" in the title, so the examples in this section will use libxpc.]

XPC services either reside within an application bundle or are advertised to run in the background using launchd.

Services call xpc_main with an event handler to receive new XPC connections:

staticvoidconnection_handler(xpc_connection_tpeer){xpc_connection_set_event_handler(peer,^(xpc_object_tevent){peer_event_handler(peer,event);});xpc_connection_resume(peer);}intmain(intargc,constchar*argv[]){xpc_main(connection_handler);exit(EXIT_FAILURE);}

Each XPC connection is one-to-one, meaning that the service operates on distinct connections, with each call to xpc_connection_create creating a new peer. footnote:[This is similar to accept in the BSD sockets API, in that the server listens on a single file descriptor that creates additional descriptors for each inbound connection.] :

xpc_connection_tc=xpc_connection_create("com.example.service",NULL);xpc_connection_set_event_handler(c,^(xpc_object_tevent){// ...});xpc_connection_resume(c);

When a message is sent over an XPC connection, it is automatically dispatched onto a queue managed by the runtime. As soon as the connection is opened on the remote end, messages are dequeued and sent.

Each message is a dictionary, with string keys and strongly-typed values:

xpc_dictionary_tmessage=xpc_dictionary_create(NULL,NULL,0);xpc_dictionary_set_uint64(message,"foo",1);xpc_connection_send_message(c,message);xpc_release(message)

XPC objects operate on the following primitive types:

  • Data
  • Boolean
  • Double
  • String
  • Signed Integer
  • Unsigned Integer
  • Date
  • UUID
  • Array
  • Dictionary
  • Null

XPC offers a convenient way to convert from the dispatch_data_t data type, which simplifies the workflow from GCD to XPC:

void*buffer;size_tlength;dispatch_data_tddata=dispatch_data_create(buffer,length,DISPATCH_TARGET_QUEUE_DEFAULT,DISPATCH_DATA_DESTRUCTOR_MUNMAP);xpc_object_txdata=xpc_data_create_with_dispatch_data(ddata);
dispatch_queue_tqueue;xpc_connection_send_message_with_reply(c,message,queue,^(xpc_object_treply){if(xpc_get_type(event)==XPC_TYPE_DICTIONARY){// ...}});

Registering Services

XPC can also be registered as launchd jobs, configured to automatically start on matching IOKit events, BSD notifications or CFDistributedNotifications. These criteria are specified in a service's launchd.plist file:

.launchd.plist

<key>LaunchEvents</key><dict><key>com.apple.iokit.matching</key><dict><key>com.example.device-attach</key><dict><key>idProduct</key><integer>2794</integer><key>idVendor</key><integer>725</integer><key>IOProviderClass</key><string>IOUSBDevice</string><key>IOMatchLaunchStream</key><true/><key>ProcessType</key><string>Adaptive</string></dict></dict></dict>

A recent addition to launchd property lists is the ProcessType key, which describe at a high level the intended purpose of the launch agent. Based on the prescribed contention behavior, the operating system will automatically throttle CPU and I/O bandwidth accordingly.

Process Types and Contention Behavior

Process TypeContention Behavior
StandardDefault value
AdaptiveContend with apps when doing work on their behalf
BackgroundNever contend with apps
InteractiveAlways contend with apps

To register a service to run approximately every 5 minutes (allowing a grace period for system resources to become more available before scheduling at a more aggressive priority), a set of criteria is passed into xpc_activity_register:

xpc_object_tcriteria=xpc_dictionary_create(NULL,NULL,0);xpc_dictionary_set_int64(criteria,XPC_ACTIVITY_INTERVAL,5*60);xpc_dictionary_set_int64(criteria,XPC_ACTIVITY_GRACE_PERIOD,10*60);xpc_activity_register("com.example.app.activity",criteria,^(xpc_activity_tactivity){// Process Dataxpc_activity_set_state(activity,XPC_ACTIVITY_STATE_CONTINUE);dispatch_async(dispatch_get_main_queue(),^{// Update UIxpc_activity_set_state(activity,XPC_ACTIVITY_STATE_DONE);});});

CMDeviceMotion

$
0
0

Beneath the smooth glass of each shiny iPhone, nestled on a logic board between touch screen controllers and Apple-designed SoCs, the gyroscope and accelerometer sit largely neglected.

Need it be so? The Core Motion framework makes it surprisingly easy to harness these sensors, opening the door to user interactions above and beyond the tapping and swiping we do every day.

For devices that include the M7 or M8 motion processor, the Core Motion framework also provides access to stored motion activity, such as step counts, stairs climbed, and movement type (walking, cycling, etc.).


Core Motion allows a developer to observe and respond to the motion and orientation of an iOS device by inspecting the raw and processed data from a combination of built-in sensors, including the accelerometer, gyroscope, and magnetometer.

Both accelerometer and gyroscope data are presented in terms of three axes that run through the iOS device. For an iPhone held in portrait orientation, the X axis runs through the device from left (negative values) to right (positive values), the Y axis through the device from bottom (-) to top (+), and the Z axis runs perpendicularly through the screen from the back (-) to the front (+).

The composited device motion data are presented in a few different ways, each with their own uses, as we'll see below.

Device X-, Y-, and Z-axes

CoreMotionManager

The CoreMotionManager class provides access to all the motion data on an iOS device. Interestingly, Core Motion provides both "pull" and "push" access to motion data. To "pull" motion data, you can access the current status of any sensor or the composited data as read-only properties of CoreMotionManager. To receive "pushed" data, you start the collection of your desired data with a block or closure that receives updates at a specified interval.

To keep performance at the highest level, Apple recommends using a single shared CoreMotionManager instance throughout your app.

CoreMotionManager provides a consistent interface for each of the four motion data types: accelerometer, gyro, magnetometer, and deviceMotion. As an example, here are the ways you can interact with the gyroscope -- simply replace gyro with the motion data type you need:

Checking for Availability

letmanager=CoreMotionManager()ifmanager.gyroAvailable{// ...}

To make things simpler and equivalent between Swift and Objective-C, assume we've declared a manager instance as a view controller property for all the examples to come.

Setting the Update Interval

manager.gyroUpdateInterval=0.1

This is an NSTimeInterval, so specify your update time in seconds: lower for smoother responsiveness, higher for less CPU usage.

Starting Updates to "pull" Data

manager.startGyroUpdates()

After this call, manager.gyroData is accessible at any time with the device's current gyroscope data.

Starting Updates to "push" Data

letqueue=NSOperationQueue.mainQueuemanager.startGyroUpdatesToQueue(queue){(data,error)in// ...}

The handler closure will be called at the frequency given by the update interval.

Stopping Updates

manager.stopGyroUpdates()

Using the Accelerometer

Let's say we want to give the splash page of our app a fun effect, with the background image staying level no matter how the phone is tilted.

Consider the following code:

First, we check to make sure our device makes accelerometer data available, next we specify a very high update rate, and then we begin updates to a closure that will rotate a UIImageView property.

ifmanager.accelerometerAvailable{manager.accelerometerUpdateInterval=0.01manager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()){[weakself](data:CMAccelerometerData!,error:NSError!)inletrotation=atan2(data.acceleration.x,data.acceleration.y)-M_PIself?.imageView.transform=CGAffineTransformMakeRotation(CGFloat(rotation))}}
RotationViewController*__weakweakSelf=self;if(manager.accelerometerAvailable){manager.accelerometerUpdateInterval=0.01f;[managerstartAccelerometerUpdatesToQueue:[NSOperationQueuemainQueue]withHandler:^(CMAccelerometerData*data,NSError*error){doublerotation=atan2(data.acceleration.x,data.acceleration.y)-M_PI;weakSelf.imageView.transform=CGAffineTransformMakeRotation(rotation);}];}

Each packet of CMAccelerometerData includes an x, y, and z value -- each of these shows the amount of acceleration in Gs (where G is one unit of gravity) for that axis. That is, if your device were stationary and straight up in portrait orientation, it would have acceleration (0, -1, 0); laying flat on its back on the table would be (0, 0, -1); and tilted forty-five degrees to the right would be something like (0.707, -0.707, 0).

We're calculating the rotation by computing the arctan2 of the x and y components from the accelerometer data, and then using that rotation in a CGAffineTransform. Our image should stay right-side up no matter how the phone is turned:

Rotation with accelerometer

The results are not terribly satisfactory -- the image movement is jittery, and moving the device in space affects the accelerometer as much as or even more than rotating. These issues could be mitigated by sampling multiple readings and averaging them together, but instead let's look at what happens when we involve the gyroscope.

Adding the Gyroscope

Rather than use the raw gyroscope data that we would get with startGyroUpdates..., let's get composited gyroscope and accelerometer data from the deviceMotion data type. Using the gyroscope, Core Motion separates user movement from gravitational acceleration and presents each as its own property of the CMDeviceMotionData instance that we receive in our handler. The code is very similar to our first example:

ifmanager.deviceMotionAvailable{manager.deviceMotionUpdateInterval=0.01manager.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue()){[weakself](data:CMDeviceMotionData!,error:NSError!)inletrotation=atan2(data.gravity.x,data.gravity.y)-M_PIself?.imageView.transform=CGAffineTransformMakeRotation(CGFloat(rotation))}}
RotationViewController*__weakweakSelf=self;if(manager.deviceMotionAvailable){manager.deviceMotionUpdateInterval=0.01f;[managerstartDeviceMotionUpdatesToQueue:[NSOperationQueuemainQueue]withHandler:^(CMDeviceMotion*data,NSError*error){doublerotation=atan2(data.gravity.x,data.gravity.y)-M_PI;weakSelf.imageView.transform=CGAffineTransformMakeRotation(rotation);}];}

Much better!

Rotation with gravity

UIClunkController

We can also use the other, non-gravity portion of this composited gyro/acceleration data to add new methods of interaction. In this case, let's use the userAcceleration property of CMDeviceMotionData to navigate backward whenever a user taps the left side of her device against her hand.

Remember that the X axis runs laterally through the device in our hand, with negative values to the left. If we sense a user acceleration to the left of more than 2.5 Gs, that will be the cue to pop our view controller from the stack. The implementation is only a couple lines different from our previous example:

ifmanager.deviceMotionAvailable{manager.deviceMotionUpdateInterval=0.02manager.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue()){[weakself](data:CMDeviceMotion!,error:NSError!)inifdata.userAcceleration.x<-2.5{self?.navigationController?.popViewControllerAnimated(true)}}}
ClunkViewController*__weakweakSelf=self;if(manager.deviceMotionAvailable){manager.deviceMotionUpdateInterval=0.01f;[managerstartDeviceMotionUpdatesToQueue:[NSOperationQueuemainQueue]withHandler:^(CMDeviceMotion*data,NSError*error){if(data.userAcceleration.x<-2.5f){[weakSelf.navigationControllerpopViewControllerAnimated:YES];}}];}

And it works like a charm:

Clunk to go back

Getting an Attitude

Better acceleration data isn't the only thing we gain by including gyroscope data - we now also know the device's true orientation in space. We find this data in the attitude property of CMDeviceMotionData, an instance of CMAttitude. CMAttitude contains three different representations of the device's orientation: Euler angles, a quaternion, and a rotation matrix. Each of these is in relation to a given reference frame.

Finding a Frame of Reference

You can think of a reference frame as the resting orientation of the device from which an attitude is calculated. All four possible reference frames describe the device laying flat on a table, with increasing specificity about the direction it's pointing.

  • CMAttitudeReferenceFrameXArbitraryZVertical describes a device laying flat (vertical Z axis) with an "arbitrary" X axis. In practice the X axis is fixed to the orientation of the device when you first start device motion updates.
  • CMAttitudeReferenceFrameXArbitraryCorrectedZVertical is essentially the same, but uses the magnetometer to correct possible variation in the gyroscope's measurement over time. Using the magnetometer adds a CPU (and therefore battery) cost.
  • CMAttitudeReferenceFrameXMagneticNorthZVertical describes a device laying flat, with its X axis (i.e., the right side of the device) pointed toward magnetic north. This setting may require your user to perform that figure-eight motion with their device to calibrate the magnetometer.
  • CMAttitudeReferenceFrameXTrueNorthZVertical is the same as the last, but adjusts for the magnetic/true north discrepancy, and therefore requires location data as well as the magnetometer.

For our purposes, the default "arbitrary" reference frame will be fine - you'll see why in a moment.

Euler Angles

Of the three attitude representations, Euler angles are the most readily understood, as they simply describe rotation around each of the axes we've already been working with. pitch is rotation around the X-axis, increasing as the device tilts toward you, decreasing as it tilts away; roll is rotation around the Y-axis, decreasing as the device rotates to the left, increasing to the right; and yaw is rotation around the (vertical) Z-axis, decreasing clockwise, increasing counter-clockwise.

Each of these values follows what's called the "right hand rule": make a cupped hand with your thumb pointing up and point your thumb in the direction of any of the three axes. Turns that move toward your fingertips are positive, turns away are negative.

Keep It To Yourself

Lastly, let's try using the device's attitude to enable a new interaction for a flash-card app, designed to be used by two study buddies. Instead of manually switching between the prompt and the answer, we'll automatically switch the view as the device turns around, so the quizzer sees the answer while the person being quizzed only sees the prompt.

Figuring out this switch from the reference frame would be tricky. We'd need to somehow take into account the starting orientation of the device and then determine which direction the device is pointing so we know which angles to monitor. Instead, we can save a CMAttitude instance and use it as the "zero point" for an adjusted set of Euler angles, using the multiplyByInverseOfAttitude() method to translate all future attitude updates.

When the quizzer taps the button to begin the quiz, we first configure the interaction - note the "pull" of the deviceMotion for initialAttitude:

// get magnitude of vector via Pythagorean theoremfuncmagnitudeFromAttitude(attitude:CMAttitude)->Double{returnsqrt(pow(attitude.roll,2)+pow(attitude.yaw,2)+pow(attitude.pitch,2))}// initial configurationvarinitialAttitude=manager.deviceMotion.attitudevarshowingPrompt=false// trigger values - a gap so there isn't a flicker zoneletshowPromptTrigger=1.0letshowAnswerTrigger=0.8
// --- class method to get magnitude of vector via Pythagorean theorem+(double)magnitudeFromAttitude:(CMAttitude*)attitude{returnsqrt(pow(attitude.roll,2.0f)+pow(attitude.yaw,2.0f)+pow(attitude.pitch,2.0f));}// --- In @IBAction handler// initial configurationCMAttitude*initialAttitude=manager.deviceMotion.attitude;__blockBOOLshowingPrompt=NO;// trigger values - a gap so there isn't a flicker zonedoubleshowPromptTrigger=1.0f;doubleshowAnswerTrigger=0.8f;

Then, in our now familiar call to startDeviceMotionUpdates, we calculate the magnitude of the vector described by the three Euler angles and use that as a trigger to show or hide the prompt view:

ifmanager.deviceMotionAvailable{manager.startDeviceMotionUpdatesToQueue(NSOperationQueue.mainQueue()){[weakself](data:CMDeviceMotion!,error:NSError!)in// translate the attitudedata.attitude.multiplyByInverseOfAttitude(initialAttitude)// calculate magnitude of the change from our initial attitudeletmagnitude=magnitudeFromAttitude(data.attitude)??0// show the promptif!showingPrompt&&magnitude>showPromptTrigger{ifletpromptViewController=self?.storyboard?.instantiateViewControllerWithIdentifier("PromptViewController")as?PromptViewController{showingPrompt=truepromptViewController.modalTransitionStyle=UIModalTransitionStyle.CrossDissolveself!.presentViewController(promptViewController,animated:true,completion:nil)}}// hide the promptifshowingPrompt&&magnitude<showAnswerTrigger{showingPrompt=falseself?.dismissViewControllerAnimated(true,completion:nil)}}}
FacingViewController*__weakweakSelf=self;if(manager.deviceMotionAvailable){[managerstartDeviceMotionUpdatesToQueue:[NSOperationQueuemainQueue]withHandler:^(CMDeviceMotion*data,NSError*error){// translate the attitude[data.attitudemultiplyByInverseOfAttitude:initialAttitude];// calculate magnitude of the change from our initial attitudedoublemagnitude=[FacingViewControllermagnitudeFromAttitude:data.attitude];// show the promptif(!showingPrompt&&(magnitude>showPromptTrigger)){showingPrompt=YES;PromptViewController*promptViewController=[weakSelf.storyboardinstantiateViewControllerWithIdentifier:@"PromptViewController"];promptViewController.modalTransitionStyle=UIModalTransitionStyleCrossDissolve;[weakSelfpresentViewController:promptViewControlleranimated:YEScompletion:nil];}// hide the promptif(showingPrompt&&(magnitude<showAnswerTrigger)){showingPrompt=NO;[weakSelfdismissViewControllerAnimated:YEScompletion:nil];}}];}

Having implemented all that, let's take a look at the interaction. As the device rotates, the display automatically switches views and the quizee never sees the answer:

Prompt by turning the device

Further Reading

I skimmed over the quaternion and rotation matrix components of CMAttitude earlier, but they are not without intrigue. The quaternion, in particular, has an interesting history, and will bake your noodle if you think about it long enough.

Queueing Up

To keep the code examples readable, we've been sending all our CoreMotionManager updates to the main queue. As a best practice, it would be better to have these updates on their own queue, so they can't get slow down user interaction, but then we'll need to get back on the main queue to update user interface elements. NSOperationQueue makes this easy, with its addOperationWithBlock method:

letqueue=NSOperationQueue()manager.startDeviceMotionUpdatesToQueue(queue){[weakself](data:CMDeviceMotion!,error:NSError!)in// motion processing hereNSOperationQueue.mainQueue().addOperationWithBlock{// update UI here}}
NSOperationQueue*queue=[[NSOperationQueuealloc]init];[managerstartDeviceMotionUpdatesToQueue:queuewithHandler:^(CMDeviceMotion*data,NSError*error){// motion processing here[[NSOperationQueuemainQueue]addOperationWithBlock:^{// update UI here}];}];

Clearly not all interactions made possible by Core Motion are good ones. Navigation through motion can be fun but also hard to discover or easy to accidentally trigger; purposeless animations can make it harder to focus on the task at hand. Prudent developers will skip over gimmicks that distract and find ways to use device motion that enrich their apps and delight their users.

UISplitViewController

$
0
0

The introduction of iPhone 6+ brought on a new importance for UISplitViewController. With just a few little tweaks, an app can now become Universal, with Apple handling most of the UI logic for all the different screen sizes.

Check out the UISplitViewController doing it's magic on iPhone 6+:

Note that the view does not split when the iPhone 6+ is in Zoomed Display mode! (You can change between Standard and Zoomed Display Mode by going to Settings.app → Display & Brightness → View)

Again, Apple handles the logic for figuring out exactly when to show the split views.

The Storyboard Layout

Here is an overview of what a storyboard layout looks like with a split view controller:

Let's get into more detail:

Master / Detail

The first step to using a UISplitViewController is dragging it onto the storyboard. Next, specify which view controller is the Master and which one is the Detail.

Do this by selecting the appropriate Relationship Segue:

The Master view controller is usually the navigation controller containing the list view (a UITableView in most cases). The Detail view controller is the Navigation Controller for the view corresponding to what shows up when the user taps on the list item.

Show Detail

There is one last part to making the split view controller work: specifying the "Show Detail" segue:

In the example below, when the user taps on a cell in the SelectColorTableViewController, they'll be shown a navigation controller with the ColorViewController at it's root.

Double Navigation Controllers‽

At this point, you might be wondering why both the Master and the Detail view controllers have to be navigation controllers—especially since there is a "Show Detail" segue from a table view (which is part of the navigation stack) to the Detail view controller. What if the Detail View Controller didn't start with a Navigation Controller?

By all accounts, the app would still work just fine. On an iPhone 6+, the only difference is the lack of a navigation toolbar when the phone is in landscape mode:

It's not a big deal, unless you do want your navigation bar to show a title. This ends up being a deal-breaker on an iPad.

Notice that when the iPad app is first opened up, there is no indication that this is a split view controller at all! To trigger the Master view controller, the user has to magically know to swipe left to right.

Even when the navigation controller is in place, the UI is not that much better at first glance (although seeing a title is definitely an improvement):

displayModeButtonItem

The simplest way to fix this issue would be to somehow indicate that there is more to the app than what's currently on-screen. Luckily, the UISplitViewController has a displayModeButtonItem, which can be added to the navigation bar:

overridefuncviewDidLoad(){super.viewDidLoad()// ...navigationItem.leftBarButtonItem=splitViewController?.displayModeButtonItem()navigationItem.leftItemsSupplementBackButton=true}

Build and Run on the iPad again, and now the user gets a nice indication of how to get at the rest of the app:

UISplitViewController's displayModeButtonItem adds a bit of extra-cool usability to the iPhone 6+ in Landscape mode, too:

By using the displayModeButtonItem, you're once again letting Apple figure out what's appropriate for which screen sizes / rotations. Instead of sweating the small (and big) stuff yourself, you can sit back and relax.

Collapse Detail View Controller

There is one more optimization we can do for the iPhone 6+ via UISplitViewControllerDelegate.

When the user first launches the app, we can make the Master view controller fully displayed until the user selects a list item:

importUIKitclassSelectColorTableViewController:UITableViewController,UISplitViewControllerDelegate{privatevarcollapseDetailViewController=true// MARK: UITableViewControlleroverridefuncviewDidLoad(){super.viewDidLoad()splitViewController?.delegate=self}// ...// MARK: UITableViewDelegateoverridefunctableView(tableView:UITableView,didSelectRowAtIndexPathindexPath:NSIndexPath){collapseDetailViewController=false}// MARK: - UISplitViewControllerDelegatefuncsplitViewController(splitViewController:UISplitViewController,collapseSecondaryViewControllersecondaryViewController:UIViewController!,ontoPrimaryViewControllerprimaryViewController:UIViewController!)->Bool{returncollapseDetailViewController}}

When the user first opens up the app on iPhone 6+ in portrait orientation, SelectColorViewController gets displayed as the primary view controller. Once the user selects a color or the app goes into the background, the SelectColorViewController gets collapsed again, and the ColorViewController is displayed:


Be sure to check out the UISplitViewControllerDelegate documentation to learn about all the other fancy things you can do with the UISplitViewController.

Given the new different device sizes we now have to work with as iOS developers, the UISplitViewController will soon have to become our new best friend!

You can get the complete source code for the project used in this post on Github.

Core Location in iOS 8

$
0
0

For as long has the iPhone has existed, location services have been front and center. Maps.app was one of the killer features that launched with the original iPhone. The Core Location API has existed in public form since the first public iPhone OS SDK. With each release of iOS, Apple has steadily added new features to the framework, like background location services, geocoding, and iBeacons.

iOS 8 continues that inexorable march of progress. Like many other aspects of the latest update, Core Location has been shaken up, with changes designed both to enable developers to build new kinds of things they couldn't before and to help maintain user privacy. Specifically, iOS 8 brings three major sets of changes to the Core Location framework: more granular permissions, indoor positioning, and visit monitoring.

Permissions

There are a number of different reasons an app might request permission to access your location. A GPS app that provides turn-by-turn directions needs constant access to your location so it can tell you when to turn. A restaurant recommendation app might want to be able to access your location (even when it's not open) so it can send you a push notification when you're near a restaurant your friends like. A Twitter app might want your location when it posts, but shouldn't need to monitor your location when you're not using it.

Prior to iOS 8, location services permissions were binary: you either gave an app permission to use location services, or you didn't. Settings.app would show you which apps were accessing your location in the background, but you couldn't do anything about it short of blocking the app from using location services entirely.

iOS 8 fixes that by breaking up location services permissions into two different types of authorization.

  • "When In Use" authorization only gives the app permission to receive your location when — you guessed it — the app is in use.

  • "Always" authorization, gives the app traditional background permissions, just as has always existed in prior iOS versions.

This is a major boon for user privacy, but it does mean a bit more effort on the part of us developers.

Requesting Permission

In earlier versions of iOS, requesting permission to use location services was implicit. Given an instance of CLLocationManager, the following code would trigger the system prompting the user to authorize access to location services if they hadn't yet explicitly approved or denied the app:

importFoundationimportCoreLocationletmanager=CLLocationManager()ifCLLocationManager.locationServicesEnabled(){manager.startUpdatingLocation()}

To make things simpler, assume we've declared a manager instance as a property for all the examples to come, and that its delegate is set to its owner.

The very act of telling a CLLocationManager to get the latest location would cause it to prompt for location services permission if appropriate.

With iOS 8, requesting permission and beginning to use location services are now separate actions. Specifically, there are two different methods you can use to explicitly request permissions: requestWhenInUseAuthorization and requestAlwaysAuthorization. The former only gives you permission to access location data while the app is open; the latter gives you the traditional background location services that prior versions of iOS have had.

ifCLLocationManager.authorizationStatus()==.NotDetermined{manager.requestAlwaysAuthorization()}

or

ifCLLocationManager.authorizationStatus()==.NotDetermined{manager.requestWhenInUseAuthorization()}

Since this happens asynchronously, the app can't start using location services immediately. Instead, one must implement the locationManager:didChangeAuthorizationStatus delegate method, which fires any time the authorization status changes based on user input.

If the user has previously given permission to use location services, this delegate method will also be called after the location manager is initialized and has its delegate set with the appropriate authorization status. Which conveniently makes for a single code path for using location services.

funclocationManager(manager:CLLocationManager!,didChangeAuthorizationStatusstatus:CLAuthorizationStatus){ifstatus==.AuthorizedAlways||status==.AuthorizedWhenInUse{manager.startUpdatingLocation()// ...}}

Descriptive String

Another change is required to use location services in iOS 8. In the past, one could optionally include a 'NSLocationUsageDescription' key in Info.plist. This value was a plain-text string explaining to the user for what the app planning to use location services. This has since been split up into two separate keys (NSLocationWhenInUseUsageDescription and NSLocationAlwaysUsageDescription), and is now mandatory; if you call requestWhenInUseAuthorization or requestAlwaysAuthorization without the corresponding key, the prompt simply won't be shown to the user.

Core Location Always Authorization

Core Location When In Use Authorization

Requesting Multiple Permissions

Another detail worth noting is that the authorization pop-up will only ever be shown to a user once. After CLLocationManager.authorizationStatus() returns anything other than NotDetermined, calling either requestWhenInUseAuthorization() or requestAlwaysAuthorization() won't result in a UIAlertController being displayed to the user. After the user's initial choice, the only way to change authorization settings is to go to Settings.app and go to either the privacy settings or the settings page for that specific app.

While more of an inconvenience under the old permission system, this significantly complicates things if an app asks for both "when-in-use" and "always" authorization at different times in its lifecycle. To help mitigate this, Apple has introduced a string constant, UIApplicationOpenSettingsURLString, that represents a URL that will open the current app's settings screen in Settings.app.

Here's an example of how an app that prompts for both kinds of permissions might decide what to do when trying to prompt for Always permissions.

switchCLLocationManager.authorizationStatus(){case.Authorized:// ...case.NotDetermined:manager.requestWhenAlwaysAuthorization()case.AuthorizedWhenInUse,.Restricted,.Denied:letalertController=UIAlertController(title:"Background Location Access Disabled",message:"In order to be notified about adorable kittens near you, please open this app's settings and set location access to 'Always'.",preferredStyle:.Alert)letcancelAction=UIAlertAction(title:"Cancel",style:.Cancel,handler:nil)alertController.addAction(cancelAction)letopenAction=UIAlertAction(title:"Open Settings",style:.Default){(action)inifleturl=NSURL(string:UIApplicationOpenSettingsURLString){UIApplication.sharedApplication().openURL(url)}}alertController.addAction(openAction)self.presentViewController(alertController,animated:true,completion:nil)}

Core Location Settings Alert

Core Location Settings Location Never

Core Location Settings Location Always

Backwards Compatibility

All of these new APIs require iOS 8. For apps still supporting iOS 7 or earlier, one must maintain two parallel code paths—one that explicitly asks for permission for iOS 8 and one that maintains the traditional method of just asking for location updates. A simple implementation might look something like this:

functriggerLocationServices(){ifCLLocationManager.locationServicesEnabled(){ifself.manager.respondsToSelector("requestWhenInUseAuthorization"){manager.requestWhenInUseAuthorization()}else{startUpdatingLocation()}}}funcstartUpdatingLocation(){manager.startUpdatingLocation()}// MARK: - CLLocationManagerDelegatefunclocationManager(manager:CLLocationManager!,didChangeAuthorizationStatusstatus:CLAuthorizationStatus){ifstatus==.AuthorizedWhenInUse||status==.Authorized{startUpdatingLocation()}}

Building User Trust

There is a common thread running throughout all of the changes in iOS 8: they all make it easier to earn the trust of your users.

Explicit calls to request authorization encourage apps to not ask for permission until the user attempts to do something that requires authorization. Including a usage description makes it easy to explain why you need location access and what the app will use it for. The distinction between "When In Use" and "Always" authorization makes users feel comfortable that you only have as much of their data as is needed.

Of course, there is little in these new APIs to stop one from doing things the same way as always. All one "needs" to do for iOS 8 support is to add in a call to useAlwaysAuthorization and add in a generic usage string. But with these new changes, Apple is sending the strong message that you should to respect your users. Once users get accustomed to apps that respect users' privacy in this way, it isn't hard to imagine that irresponsible use of location services could result in negative App Store ratings.

Indoor Positional Tracking

If one were to peruse the API diffs for CoreLocation.framework, among the most baffling discoveries would be the introduction of CLFloor, a new object with a quite simple interface:

classCLFloor:NSObject{varlevel:Int{get}}
@interfaceCLFLoor : NSObject@property(readonly,nonatomic)NSIntegerlevel@end

That's it. A single property, an integer that tells you what floor of a building the location represents.

Europeans will surely be glad to know that the ground floor of a building is represented by '0', not '1'.

A CLLocation object returned by a CLLocationManager may include a floor property, but if you were to write a sample app that used location services you'd notice that your CLLocation objects' floor property was always nil.

That's because this API change is the tip of the iceberg for a larger suite of features introduced in iOS 8 to facilitate indoor location tracking. For developers building applications in large spaces, like art museums or department stores, Apple now has tooling to support IPS using the built-in Core Location APIs and a combination of WiFi, GPS, cellular, and iBeacon data.

That said, information about this new functionality is surprisingly hard to come by. The program is currently limited to businesses who have applied for the program via Apple Maps Connect, with a pretty strict barrier to entry. Limited information about the program was outlined in this year's WWDC (Session 708: Taking Core Location Indoors), but for the most part, most logistical details are locked behind closed doors. For all but the most well-connected of us, we have no choice but to be content writing this off as an idle curiosity.

CLVisit

In many apps, the reason to use location monitoring is determining whether a user is in a given place. Conceptually, you're thinking in terms of nouns like "places" or "visits", rather than raw GPS coordinates.

However, unless you have the benefit of being able to use region monitoring (which is limited to a relatively small number of regions) or iBeacon ranging (which requires beacon hardware to be physically installed in a space) Core Location's background monitoring tools aren't a great fit. Building a check-in app or a more comprehensive journaling app along the lines of Moves has traditionally meant eating up a lot of battery life with location monitoring and spending a lot of time doing custom processing.

With iOS 8, Apple has tried to solve this by introducing CLVisit, a new type of background location monitoring. A single CLVisit represents a period of time a user has spent in a single location, including both a coordinate and start / end timestamps.

In theory, using visit monitoring is no more work than any other background location tracking. Simply calling manager.startMonitoringVisits() will enable background visit tracking, assuming the user has given Always authorization to your app. Once started, your app will be woken up periodically in the background when new updates come in. Unlike with basic location monitoring, if the system has a number of visit updates queued up (typically be enabling deferred updates), your delegate method will be called multiple times, with each call having a single visit, rather than the array of CLLocation objects that locationManager:didReceiveUpdates: is called with. Calling manager.stopMonitoringVisits() will stop tracking.

Handling Visits

Each CLVisit object contains a few basic properties: its average coordinate, a horizontal accuracy, and dates for arrival and departure times.

Every time a visit is tracked, the CLLocationManagerDelegate might be informed twice: once while the user has just arrived to a new place, and again when they leave it. You can figure out which is which by checking the departureDate property; a departure time of NSDate.distantFuture() means that the user is still there.

funclocationManager(manager:CLLocationManager!,didVisitvisit:CLVisit!){ifvisit.departureDate.isEqualToDate(NSDate.distantFuture()){// User has arrived, but not left, the location}else{// The visit is complete}}

Caveat Implementor

CLVisit is, as of iOS 8.1, not all that precise. While start and end times are generally accurate within a minute or two, lines get blurred at the edges of what is and is what not a visit. Ducking into a corner coffee shop for a minute might not trigger a visit, but waiting at a particularly long traffic light might. It's likely that Apple will improve the quality of visit detection in future OS upgrades, but for now you might want to hold off on relying on CLVisit in favor of your own visit detection for use cases where it's vital your data is as accurate can be.

UIPrintInteractionController

$
0
0

With all the different means to comment, mark up, save, and share right at our fingertips, it's easy to overlook the value of a printed sheet of paper.

UIKit makes it easy to print straight from a user's device with custom designs that you can adapt to both your content and the paper size. This article will first walk through how to format your content for printing, then detail the different ways to present (or not!) the printing interface.


The "printed" images throughout this article are taken from Apple's Printer Simulator. (The yellow edges represent the non-printable margins of the paper)

As of Xcode 6, the printer simulator must be downloaded as part of the Hardware IO Tools for Xcode.

Download Hardware I/O Tools from Apple Developer Website

PrintSimulator App Info

PrintSimulator App Load Paper


At the heart of the UIKit Printing APIs is UIPrintInteractionController. A shared instance of this class manages details of print jobs and configure any UI that will be presented to the user. It also provides three levels of control for the formatting of your content.

Printing is a Job

Before we look at formatting actual content for printing, let's go through the options for configuring the print job and the print options presented to the user.

UIPrintInfo

Print job details are set in a UIPrintInfo instance. You can use the following properties:

  • jobNameString: A name for this print job. The name will be displayed in the Print Center on the device and, for some printers, on the LCD display.
  • orientationUIPrintInfoOrientation: Either .Portrait (the default) or .Landscape—this is ignored if what you print has an intrinsic orientation, such as a PDF.
  • duplexUIPrintInfoDuplex: .None, .ShortEdge, or .LongEdge. The short- and long-edge settings indicate how double-sided pages could be bound, while .None suppresses double-sided printing (though not the UI toggle for duplexing, perplexingly).
  • outputTypeUIPrintInfoOutputType: Gives UIKit a hint about the type of content you're printing. Can be any of:
    • .General (default): For mixed text and graphics; allows duplexing.
    • .Grayscale: Can be better than .General if your content includes black text only.
    • .Photo: For color or black and white images; disables duplexing and favors photo media for the paper type.
    • .PhotoGrayscale: Can be better than .Photo for grayscale-only images, depending on the printer.
  • printerIDString?: An ID for a particular printer—you can retrieve this only after the user has selected a printer through the UI and save it to use as a preset for a future print job.

In addition, UIPrintInfo provides a dictionaryRepresentation property, which can be saved and used to create a new UIPrintInfo instance later.

UIPrintInteractionController Settings

There are a handful of settings on the UIPrintInteractionController that you can configure before displaying the printing UI. These include:

  • printInfoUIPrintInfo: The aforementioned print job configuration.
  • printPaperUIPrintPaper: A simple type that describes the physical and printable size of a paper type; except for specialized applications, this will be handled for you by UIKit.
  • showsNumberOfCopiesBool: When true, lets the user choose the number of copies.
  • showsPageRangeBool: When true, lets the user choose a sub-range from the printed material. This only makes sense with multi-page content—it's turned off by default for images.
  • showsPaperSelectionForLoadedPapersBool: When this is true and the selected printer has multiple paper options, the UI will let the user choose which paper to print on.

Formatting Your Content

Through four different properties of UIPrintInteractionController, you can select the level of control (and complexity) you want for your content.

  1. printingItemAnyObject! or printingItems[AnyObject]!: At the most basic level, the controller simply takes content that is already printable (images and PDFs) and sends them to the printer.
  2. printFormatterUIPrintFormatter: At the next level, you can use a UIPrintFormatter subclass to format content inside your application, then hand the formatter off to the UIPrintInteractionController. You have some control over the format, and the printing API largely takes care of the rest.
  3. printPageRendererUIPrintPageRenderer: At the highest level, you can create a custom subclass of UIPrintPageRenderer, combining page formatters and your own drawing routines for headers, footers, and page content.

Since Thanksgiving (my favorite holiday) is around the corner, to illustrate these properties we'll add printing to different screens of a hypothetical app for Thanksgiving recipes.

Printing With printItem(s)

You can print pre-existing printable content by setting either the printItem or printItems property of UIPrintInteractionController. Images and PDFs can be given either as image data (in a NSData, UIImage, or ALAsset instance) or via any NSURL referencing something that can be loaded into an NSData object. To be printable, images must be in a format that UIImage supports.

Let's walk through a very simple case: showing the UI to print an image when the user taps a button. (We'll look at alternate ways of initiating printing below.) The process will be largely the same, no matter what you're printing—configure your print info, set up the print interaction controller, and provide your content before displaying the UI:

@IBActionfuncprint(sender:UIBarButtonItem){ifUIPrintInteractionController.canPrintURL(imageURL){letprintInfo=UIPrintInfo(dictionary:nil)printInfo.jobName=imageURL.lastPathComponentprintInfo.outputType=.PhotoletprintController=UIPrintInteractionController.sharedPrintController()!printController.printInfo=printInfoprintController.showsNumberOfCopies=falseprintController.printingItem=imageURLprintController.presentAnimated(true,completionHandler:nil)}}
-(IBAction)print:(id)sender{if([UIPrintInteractionControllercanPrintURL:self.imageURL]){UIPrintInfo*printInfo=[UIPrintInfoprintInfo];printInfo.jobName=self.imageURL.lastPathComponent;printInfo.outputType=UIPrintInfoOutputGeneral;UIPrintInteractionController*printController=[UIPrintInteractionControllersharedPrintController];printController.printInfo=printInfo;printController.printingItem=self.imageURL;[printControllerpresentAnimated:truecompletionHandler:nil];}}

Easy as pie! (Or, in this case, sautéed Swiss chard.)

Print with .printingItem

The presentAnimated(:completionHandler:) method is for presenting the printing UI on the iPhone. If printing from the iPad, use one of the presentFromBarButtonItem(:animated:completionHandler:) or presentFromRect(:inView:animated:completionHandler:) methods instead.

UIPrintFormatter

The UIPrintFormatter class has two subclasses that can be used to format text (UISimpleTextPrintFormatter and UIMarkupTextPrintFormatter) plus another (UIViewPrintFormatter) that can format the content of three views: UITextView, UIWebView, and MKMapView. Print formatters have a few properties that allow you to define the printed area of the page in different ways; the final print area for the formatter will be the smallest rectangle that meets the following criteria:

  • contentInsetsUIEdgeInsets: A set of insets from the edges of the page for the entire block of content. The left and right insets are applied on every page, but the top inset is only applied on the first page. The bottom inset is ignored.
  • perPageContentInsetsUIEdgeInsets (iOS 8 only): A set of insets from the edges of the page for every page of formatted content.
  • maximumContentWidth and maximumContentHeightCGFloat: If specified, these can further constrain the width and height of the content area.

Though not clearly documented by Apple, all of these values are based on 72 points per inch.

The two text-based print formatters are initialized with the text they will be formatting. UISimpleTextPrintFormatter can handle plain or attributed text, while UIMarkupTextPrintFormatter takes and renders HTML text in its markupText property. Let's try sending an HTML version of our Swiss chard recipe through the markup formatter:

letformatter=UIMarkupTextPrintFormatter(markupText:htmlString)formatter.contentInsets=UIEdgeInsets(top:72,left:72,bottom:72,right:72)// 1" marginsprintController.printFormatter=formatter
UIMarkupTextPrintFormatter*formatter=[[UIMarkupTextPrintFormatteralloc]initWithMarkupText:htmlString];formatter.contentInsets=UIEdgeInsetsMake(72,72,72,72);// 1" marginsprintController.printFormatter=formatter;

The result? A handsomely rendered HTML page:

Print with UIMarkupTextPrintFormatter

On the other hand, to use a UIViewPrintFormatter, you retrieve one from the view you want to print via its viewPrintFormatter property. Here's a look at how the formatter does its job for each of the three supported views:

1) UITextView

Print with UITextView

2) UIWebView

Print with UIWebView

3) MKMapView

Print with MKMapView

UIPrintPageRenderer

The built-in formatters are fine, but for the most control over the printed page, you can implement a subclass of UIPrintPageRenderer. In your subclass you can combine the print formatters we saw above with your own custom drawing routines to create terrific layouts for your app's content. Let's look at one more way of printing a recipe, this time using a page renderer to add a header and to draw the images alongside the text of the recipe.

In the initializer, we save the data that we'll need to print, then set the headerHeight (the header and footer drawing methods won't even be called unless you set their respective heights) and create a markup text formatter for the text of the recipe.

Complete Objective-C and Swift source code for the following examples is available as a gist.

classRecipePrintPageRenderer:UIPrintPageRenderer{letauthorName:Stringletrecipe:Recipeinit(authorName:String,recipe:Recipe){self.authorName=authorNameself.recipe=recipesuper.init()self.headerHeight=0.5*POINTS_PER_INCHself.footerHeight=0.0// defaultletformatter=UIMarkupTextPrintFormatter(markupText:recipe.html)formatter.perPageContentInsets=UIEdgeInsets(top:POINTS_PER_INCH,left:POINTS_PER_INCH,bottom:POINTS_PER_INCH,right:POINTS_PER_INCH*3.5)addPrintFormatter(formatter,startingAtPageAtIndex:0)}// ...}
@interfaceRecipePrintPageRenderer : UIPrintPageRenderer@property(nonatomic,strong)NSString*authorName;@property(nonatomic,strong)Recipe*recipe;-(id)initWithAuthorName:(NSString*)authorNamerecipe:(Recipe*)recipe;@end@implementationRecipePrintPageRenderer-(id)initWithAuthorName:(NSString*)authorNamerecipe:(Recipe*)recipe{if(self=[superinit]){self.authorName=authorName;self.recipe=recipe;self.headerHeight=0.5;self.footerHeight=0.0;// defaultUIMarkupTextPrintFormatter*formatter=[[UIMarkupTextPrintFormatteralloc]initWithMarkupText:recipe.html];formatter.perPageContentInsets=UIEdgeInsetsMake(POINTS_PER_INCH,POINTS_PER_INCH,POINTS_PER_INCH,POINTS_PER_INCH*3.5);[selfaddPrintFormatter:formatterstartingAtPageAtIndex:0];}returnself;}// ...@end

When you use one or more print formatters as part of your custom renderer (as we're doing here), UIKit queries them for the number of pages to print. If you're doing truly custom page layout, implement the numberOfPages() method to provide the correct value.

Next, we override drawHeaderForPageAtIndex(:inRect:) to draw our custom header. Unfortunately, those handy per-page content insets on print formatters are gone here, so we first need to inset the headerRect parameter to match my margins, then simply draw into the current graphics context. There's a similar drawFooterForPageAtIndex(:inRect:) method for drawing the footer.

overridefuncdrawHeaderForPageAtIndex(pageIndex:Int,varinRectheaderRect:CGRect){varheaderInsets=UIEdgeInsets(top:CGRectGetMinY(headerRect),left:POINTS_PER_INCH,bottom:CGRectGetMaxY(paperRect)-CGRectGetMaxY(headerRect),right:POINTS_PER_INCH)headerRect=UIEdgeInsetsInsetRect(paperRect,headerInsets)// author name on leftauthorName.drawAtPointInRect(headerRect,withAttributes:nameAttributes,andAlignment:.LeftCenter)// page number on rightletpageNumberString:NSString="\(pageIndex + 1)"pageNumberString.drawAtPointInRect(headerRect,withAttributes:pageNumberAttributes,andAlignment:.RightCenter)}
-(void)drawHeaderForPageAtIndex:(NSInteger)indexinRect:(CGRect)headerRect{UIEdgeInsetsheaderInsets=UIEdgeInsetsMake(CGRectGetMinY(headerRect),POINTS_PER_INCH,CGRectGetMaxY(self.paperRect)-CGRectGetMaxY(headerRect),POINTS_PER_INCH);headerRect=UIEdgeInsetsInsetRect(self.paperRect,headerInsets);// author name on left[self.authorNamedrawAtPointInRect:headerRectwithAttributes:self.nameAttributesandAlignment:NCStringAlignmentLeftCenter];// page number on rightNSString*pageNumberString=[NSStringstringWithFormat:@"%ld",index+1];[pageNumberStringdrawAtPointInRect:headerRectwithAttributes:self.pageNumberAttributesandAlignment:NCStringAlignmentRightCenter];}

Lastly, let's provide an implementation of drawContentForPageAtIndex(:inRect:):

overridefuncdrawContentForPageAtIndex(pageIndex:Int,inRectcontentRect:CGRect){ifpageIndex==0{// only use rightmost two inches of contentRectletimagesRectWidth=POINTS_PER_INCH*2letimagesRectHeight=paperRect.height-POINTS_PER_INCH-(CGRectGetMaxY(paperRect)-CGRectGetMaxY(contentRect))letimagesRect=CGRect(x:CGRectGetMaxX(paperRect)-imagesRectWidth-POINTS_PER_INCH,y:paperRect.origin.y+POINTS_PER_INCH,width:imagesRectWidth,height:imagesRectHeight)drawImages(recipe.images,inRect:imagesRect)}}
-(void)drawContentForPageAtIndex:(NSInteger)pageIndexinRect:(CGRect)contentRect{if(pageIndex==0){// only use rightmost two inches of contentRectCGFloatimagesRectWidth=POINTS_PER_INCH*2;CGFloatimagesRectHeight=CGRectGetHeight(self.paperRect)-POINTS_PER_INCH-(CGRectGetMaxY(self.paperRect)-CGRectGetMaxY(contentRect));CGRectimagesRect=CGRectMake(CGRectGetMaxX(self.paperRect)-imagesRectWidth-POINTS_PER_INCH,CGRectGetMinY(self.paperRect)+POINTS_PER_INCH,imagesRectWidth,imagesRectHeight);[selfdrawImages:self.recipe.imagesinRect:imagesRect];}}

With the implementation of our custom page renderer complete, we can set an instance as the pageRenderer property on the print interaction controller and we're ready to print.

letrenderer=RecipePrintPageRenderer(authorName:"Nate Cook",recipe:selectedRecipe)printController.printPageRenderer=renderer
RecipePrintPageRenderer*renderer=[[RecipePrintPageRendereralloc]initWithAuthorName:@"Nate Cook"recipe:selectedRecipe];printController.printPageRenderer=renderer;

The final result is much nicer than any of the built-in formatters.

Note that the text of the recipe is being formatted by a UIMarkupTextPrintFormatter, while the header and images are drawn via custom code.

Print with UIPrintPageRenderer subclass

Printing via a Share Sheet

With the tools we've learned above, adding printing capability in a share sheet is simple. Instead of using UIPrintInteractionController to present the printing UI, we pass off our configured UIPrintInfo and printing item(s), formatter, or renderer to a UIActivityViewController. If the user selects the Print button in the share sheet, the printing UI will be displayed with all our configurations intact.

@IBActionfuncopenShareSheet(){letprintInfo=...letformatter=...letactivityItems=[printInfo,formatter,textView.attributedText]letactivityController=UIActivityViewController(activityItems:activityItems,applicationActivities:nil)presentViewController(activityController,animated:true,completion:nil)}
-(IBAction)openShareSheet:(id)sender{UIPrintInfo*printInfo=...UISimpleTextPrintFormatter*formatter=...NSArray*activityItems=@[printInfo,formatter,self.textView.attributedText];UIActivityViewController*activityController=[[UIActivityViewControlleralloc]initWithActivityItems:activityItemsapplicationActivities:nil];[selfpresentViewController:activityControlleranimated:YEScompletion:nil];}

While UIPrintInfo and subclasses of UIPrintFormatter and UIPrintPageRenderer can be passed to a UIActivityViewController as activities, none of them seem to conform to the UIActivityItemSource protocol, so you'll see a (harmless) warning in your console about "Unknown activity items."

Skipping the Printing UI

New in iOS 8 is a way to print without any presentation of the printing UI. Instead of presenting the UI each time the user presses a print button, you can provide a way for your users to select a printer somewhere in your app with the easy-to-use UIPrinterPickerController. It accepts an optional UIPrinter instance in its constructor for a pre-selection, uses the same presentation options as explained above, and has a completion handler for when the user has selected her printer:

letprinterPicker=UIPrinterPickerController(initiallySelectedPrinter:savedPrinter)printerPicker.presentAnimated(true){(printerPicker,userDidSelect,error)inifuserDidSelect{self.savedPrinter=printerPicker.selectedPrinter}}
UIPrinterPickerController*printPicker=[UIPrinterPickerControllerprinterPickerControllerWithInitiallySelectedPrinter:self.savedPrinter];[printPickerpresentAnimated:YEScompletionHandler:^(UIPrinterPickerController*printerPicker,BOOLuserDidSelect,NSError*error){if(userDidSelect){self.savedPrinter=printerPicker.selectedPrinter;}}];

Now you can tell your UIPrintInteractionController to print directly by calling printToPrinter(:completionHandler:) with the saved printer instead of using one of the present... methods.


As one final recommendation, consider the printed page as you would any other way of interacting with your content. In the same way you scrutinize font size and weight or the contrast between elements on screen, make sure to test your print layouts on paper—the contrast, size, and margins should all be appropriate to the medium.


WatchKit

$
0
0

Everyone is excited about ᴡᴀᴛᴄʜ. Developers especially.

What's the best way to get started? Look no further than Apple's WatchKit developer resources.

  • Watch the "Getting Started" video—it's like being in Moscone.
  • Read the Human Interface Guidelines—consider it a prerequisite for designing your application.
  • Peruse the WatchKit Programming Guide and WKInterfaceCatalog for an introduction everything the framework provides.
  • And when you're finally ready to add that App Extension target to your app, consult the Lister Sample App (in both Objective-C & Swift!) to see how everything fits together.

Apple's Developer Publications, Developer Evangelism, and WatchKit teams knocked it out of the park with the WatchKit launch. The official resources are superb.

That said, after taking a look at everything, there were a few things that jumped out as it relates to UIKit. They're the kind of subjective, opinionated things that don't make for good documentation, but might be interesting or useful to anyone else as they're getting started.

So this week, some initial impressions of WatchKit from the perspective of an iOS developer.


WatchKit harkens back to the earliest days of iOS development, when the platform limitations acted as liberating constraints for developers. Compared to OS X & AppKit, mired in decades of cruft and churn, iPhoneOS (as it was originally known) & UIKit were a breath of fresh air. Apps were small, simple, and humble.

After 7 years and as many major releases, iOS has grown to encompass myriad devices and configurations, from iPhones and iPads of all shapes and sizes, to the TV and CarPlay. It's still an amazing developer experience (for the most part), but it feels like some of the magic has been lost along the way.

Counterpoint: Nostalgia is bullshit. Remember, back in those days, there was no ARC, no GCD, no Interface Builder support for iOS and certainly no Swift. The indispensable open source libraries of the time were Three20 and asi-http-request. The state of the art for table view scroll performance was to manually composite text and images in a cell's content view drawRect:. Life was solitary, poor, nasty, brutish, and short.

Regardless of where you're personally coming from, the simplicity of WatchKit is something to be enjoyed and celebrated.

Comparison to UIKit

WatchKit bears a striking resemblance to UIKit, which is not entirely surprising, given their shared history and purpose. Watches are different from phones and tablets, which themselves are different from desktops. Some concepts are shared, but each have unique purposes and constraints that shape the topography of their software.

For sake of comparison, here is how one might understand WatchKit in terms of UIKit / Cocoa concepts:

WatchKitUIKit
WKInterfaceControllerUIViewController
WKUserNotificationInterfaceControllerUIApplicationDelegate + UIAlertController
WKInterfaceDeviceUIDevice
WKInterfaceObjectUIView
WKInterfaceButtonUIButton
WKInterfaceDateUILabel + NSDateFormatter
WKInterfaceGroupUIScrollView
WKInterfaceImageUIImageView
WKInterfaceLabelUILabel
WKInterfaceMapMKMapView
WKInterfaceSeparatorUITableView.separatorColor / .separatorStyle
WKInterfaceSliderUIStepper + UISlider
WKInterfaceSwitchUISwitch
WKInterfaceTableUITableView
WKInterfaceTimerUILabel + NSDateFormatter + NSTimer

As a namespace prefix, WKInterface sticks out like a sore thumb, but WK on its own was recently claimed by the new WebKit framework. Although the Watch platform is a long way from being able to embed web views, the decision to demarcate things as they are is sensible.

There's a lot of overlap, but there are important differences. Understanding those differences is both informative to making great WatchKit apps as well as enlightening in terms of how Apple's thinking about API and best practices continues to evolve.

WKInterfaceController

WKInterfaceController manages the elements in a scene, whereas UIViewController manages a view and its subviews. Interface objects are not views, but they act in similar ways.

The designated initializer for WKInterfaceController is initWithContext:, which accepts an optional context object:

overrideinit(context:AnyObject?){super.init(context:context)// ...}

What's a context? It's anything you want it to be: a date, a string, a model object, or nothing at all.

The open-endedness of context may be disorienting at first, but it's actually a remarkably clever solution to a long-standing problem of UIKit—namely, the difficulty passing information between view controllers. Without a standard convention for configuring UIViewControllers as they are pushed, popped, and presented, developers are regularly faced with a miserable choice between custom designated initializers (incompatible with Storyboards), property configuration (prone to creating controllers with incomplete state), custom delegates (overly ceremonious when done correctly), or notifications (just... no). It's for this reason that so many applications backed by Core Data fall into the pattern of referencing a shared managedObjectContext in the App Delegate.

But I digress.

Overall, WKInterfaceController's API has a tiny footprint, and there's no better illustration than its lifecycle methods:

// MARK: - WKInterfaceControlleroverridefuncwillActivate(){// ...}overridefuncdidDeactivate(){// ...}

That's it: 2 methods. No loading / unloading, no will / did appear / disappear, no animated:YES. Watch apps are simple by necessity. Communication between the iOS device driving the application and the watch is both time-consuming and battery-consuming, so all of the setup for the interface controller's scene is to be done in the initializer and willActivate. Both during and after didDeactivate, the Watch will ignore attempts to update the state of interface elements.

Watch applications are either Hierarchical or Page-Based. This is similar to Xcode's project templates for "Master-Detail Application", "Page-Based Application", and "Tabbed Application", except that the design choices are mutually exclusive.

Hierarchical applications have an implicit navigational stack, which WKInterfaceController can manage with -pushControllerWithName:context:& -popController. One thing to note here is how -pushControllerWithName:context: takes a string—the name of the controller as specified in the Storyboard—rather than an instance of the controller itself.

Page-Based applications, on the other hand, act like a horizontal or vertical paged UIScrollView, with a pre-determined number of scenes each managed by a controller. It will be interesting to see which use cases are best served by Page-Based and Hierarchical interfaces. Without trying out stock applications on a real device, there's not much context to understand the trade-offs of each intuitively.

As a final aside, there is an interesting pattern in Apple's Swift sample code, to use inner structs as namespaces for Storyboard constants:

classWatchListsInterfaceController:WKInterfaceController,ListsControllerDelegate{structWatchStoryboard{staticletinterfaceControllerName="WatchListsInterfaceController"structRowTypes{staticletlist="WatchListsInterfaceControllerListRowType"staticletnoLists="WatchListsInterfaceControllerNoListsRowType"}structSegues{staticletlistSelection="WatchListsInterfaceControllerListSelectionSegue"}}// ...}

WKInterfaceObject

WKInterfaceObject resembles a streamlined rendition of UIView, with properties for alpha, hidden, horizontal& vertical alignment, and width& height.

The most striking difference is the lack of a frame. Instead of manually specifying coordinate points or setting up Auto Layout constraints, WatchKit interface objects are laid out in a grid according to their margins and respective ordering, which is reminiscent to working with a CSS framework like Bootstrap (or for you Rubyists out there, remember Shoes?).

Another departure from Cocoa Touch is that Target-Action methods use a fixed-format specific for each type of control, rather than passing a dynamic sender and UIEvent.

ObjectAction Method
Button- (IBAction)doButtonAction
Switch- (IBAction)doSwitchAction:(BOOL)on
Slider- (IBAction)doSliderAction:(float)value
Table- (IBAction)doTableRowTapAction:(NSInteger)rowIndex
Menu Item- (IBAction)doMenuItemAction

For the small, closed set of controls, this approach is really appealing—much better than typing UIControlEventTouchUpInside.

WKInterfaceButton

WKInterfaceButton is an interface object that can be tapped to trigger an action. Its contents can be either a single text label or a group.

That latter part—being able to contain a group—is huge. This avoids one of the biggest gripes folks have with UIButton, the difficulty of adding subviews and having things position and interact correctly, which would otherwise drive people to give up and use UITapGestureRecognizer.

WKInterfaceTable

Of all of the concepts carried over from iOS, tables are perhaps the most-changed.

UITableView is the backbone of iPhone applications. As such, they've evolved significant complexity to handle the demands of applications with a lot of data that needs to be displayed in many different ways. WKInterfaceTable, by comparison, seems marginal by comparison.

WatchKit tables do not have sections or headers, or footers, or editing, or searching, or data sources, or delegates. Rows are pre-populated at WKInterfaceController -willActivate, with each row specifying its own row controller (an NSObject subclass with IBOutlets). WKInterfaceController can respond to table interactions either with the table:didSelectrowAtIndex: delegate method, or using the aforementioned Target-Action method.

It may not seem like much, but this approach is well-suited for the watch, and is a lot more straightforward than on iOS.

WKInterfaceLabel

By contrast, WKInterface is perhaps the least-changed thing carried over from iOS. With support for NSAttributedString, custom fonts, and font scaling, it's pretty much everything you could ask for.

WKInterfaceDate & WKInterfaceTimer

Let us not forget that time is a pretty important concept for watch. As such, WatchKit introduces two new interface objects that are unprecedented in Cocoa or Cocoa Touch: WKInterfaceDate& WKInterfaceTimer.

WKInterfaceDate is a special kind of label that displays the current date or time. WKInterfaceTimer is similar, except that it displays a countdown until a specified date.

Including these two classes does as much to ensure app quality as much as anything else in WatchKit. Given how these essential these tasks are for a watch, and how notoriously bad programmers are at using NSDateFormatter and NSTimer, one can only imagine where we'd be without these.

WKInterfaceSlider & WKInterfaceSwitch

Sliders and Switches are due for a comeback on the watch. Without the benefit of touch gestures, interfaces are resigned to go back to basics. Tap, Tap, On / Off. Tap, Tap, + / -.

WKInterfaceSlider& WKInterfaceSwitch both appear to be well-crafted and rather customizable.


This is, of course, but a superficial reflection into the capabilities of WatchKit. Like I said at the beginning of the article, Apple's official resources for WatchKit have everything you could ever need.

Pay

$
0
0

There's a unique brand of modern angst that manifests the moment you decide to buy something online. While there's no English word for it, it translates roughly to "Where is my credit card? What is its number? How badly do I actually want this thing, anyway?".

When you're on an iOS device, this ennui intensifies: there's a good chance you don't have your card with you, and holding your credit card and typing on your phone simultaneously is a feat best left to gymnasts and astronauts. (I'm sort of joking, but also willing to bet Apple has measured this in a lab somewhere.)

And if you're a developer accepting credit card payments in your app, this unfortunate phenomenon is directly correlated to lost revenue.

Apple Pay changes all this. Though a lot of the attention around its launch has focused on physical payments in stores (where customers can use their iPhone to pay at NFC-enabled payment terminals), there's an equally large opportunity for iOS developers to improve the checkout experience in their apps.

Just a reminder: if you're selling digital goods or virtual currencies in your app, you should use In-App Purchases—not Apple Pay—to sell your content (See section 11.2 of the App Store Review Guidelines). You can use Apple Pay for selling physical goods and services.


Obtaining an Apple Merchant ID

You'll have to register an Apple Merchant ID before testing anything out. Before doing this, you should choose a payment provider to actually handle your credit card processing. Apple provides a list of recommended ones at their Apple Pay Developer Page (disclosure: I work for Stripe, one of the recommended companies, but the code in this article doesn't depend on you choosing any specific provider). While your provider should have a specific guide for how to get set up using Apple Pay with their platform, the process looks like this:

  • Head to the Certificates, Identifiers, and Profiles section of the Apple Developer center and create a new merchant ID.
  • Next, head to the Certificates section and create a new Apple Pay Certificate. This involves uploading a Certificate Signing Request (CSR) to Apple. When you're signing up for a payment processor, they'll typically give you a CSR to use. You can use a CSR that you generate yourself to follow along with this guide, but your payment processor won't be able to decrypt payments made with it and you'll need to redo it later.
  • In Xcode, go to the "Capabilities" section of your project settings and turn "Apple Pay" to on. You may have to select the merchant ID you created earlier from the list provided.

Making Your First Charge

Apple Pay will only work on an iOS device capable of using Apple Pay (e.g. iPhone 6/6+, iPad Mini 3, iPad Air 2). In addition, you have to have successfully added the Apple Pay entitlement (described in "Obtaining an Apple Merchant ID") in order to test it in your app. If you'd like to approximate its behavior on the simulator, you can find a testing library that mimics its functionality (with test credit card details) at https://github.com/stripe/ApplePayStubs.

Once you're up and running with a merchant account, getting started with Apple Pay is really straightforward. When it's time to check out, you'll first need to see if Apple Pay is supported on the device you're running and your customer has added any cards to Passbook:

letpaymentNetworks=[PKPaymentNetworkAmex,PKPaymentNetworkMasterCard,PKPaymentNetworkVisa]ifPKPaymentAuthorizationViewController.canMakePaymentsUsingNetworks(paymentNetworks){// Pay is available!}else{// Show your own credit card form.}

Assuming Apple Pay is available, the next step is to assemble a PKPaymentRequest. This object describes the charge you're requesting from your customer. If you're requesting payment in the U.S. (reasonable, as Apple Pay is currently US-only), here's some default options you'll need to set that'll likely stay constant:

letrequest=PKPaymentRequest()request.supportedNetworks=[PKPaymentNetworkAmex,PKPaymentNetworkMasterCard,PKPaymentNetworkVisa]request.countryCode="US"request.currencyCode="USD"request.merchantIdentifier="<#Replace me with your Apple Merchant ID#>"request.merchantCapabilities=.Capability3DS

Next, describe the things the customer is actually buying with the paymentSummaryItems property. This takes an array of PKPaymentSummaryItems, which have a label and amount. They're analogous to line items on a receipt (which we'll see momentarily).

Payment Authorization

letwax=PKPaymentSummaryItem(label:"Mustache Wax",amount:NSDecimalNumber(string:"10.00"))letdiscount=PKPaymentSummaryItem(label:"Discount",amount:NSDecimalNumber(string:"-1.00"))lettotalAmount=wax.amount.decimalNumberByAdding(discount.amount).decimalNumberByAdding(shipping.amount)lettotal=PKPaymentSummaryItem(label:"NSHipster",amount:totalAmount)request.paymentSummaryItems=[wax,discount,shipping,total]

Note that you can specify zero or negative amounts here to apply coupons or communicate other information. However, the total amount requested must be greater than zero. You'll note we use a PKShippingMethod (inherits from PKPaymentSummaryItem) to describe our shipping item. More on this later.

Next, to display the actual payment sheet to the customer, we create an instance of PKPaymentAuthorizationViewController with our PKPaymentRequest and present it. (Assume for this example that all this code is inside a UIViewController that will sit behind the payment screen).

letviewController=PKPaymentAuthorizationViewController(paymentRequest:request)viewController.delegate=selfpresentViewController(viewController,animated:true,completion:nil)

A few style nits to be aware of:

  • The view controller doesn't fully obscure the screen (in this case the blue background is part of our application). You can update the background view controller while the PKPaymentAuthorizationViewController is visible if you want.
  • All text is automatically capitalized.
  • The final line item is separated from the rest, and is intended to display the total amount you're charging. The label will be prepended with the word "PAY", so it usually makes sense to put your company name for the payment summary item's label.
  • The entire UI is presented via a Remote View Controller. This means that outside the PKPaymentRequest you give it, it's impossible to otherwise style or modify the contents of this view.

PKPaymentAuthorizationViewControllerDelegate

In order to actually handle the payment information returned by the PKPaymentAuthorizationViewController, you need to implement the PKPaymentAuthorizationViewControllerDelegate protocol. This has 2 required methods, -(void)paymentAuthorizationViewController:didAuthorizePayment:completion: and -(void)paymentAuthorizationViewControllerDidFinish:.

To understand how each of these components work, let's check out a timeline of how an Apple Pay purchase works:

  • You present a PKPaymentAuthorizationViewController as described above.
  • The customer approves the purchase using Touch ID (or, if that fails 3 times, by entering their passcode).
  • The thumbprint icon turns into a spinner, with the label "Processing"
  • Your delegate receives the paymentAuthorizationViewController:didAuthorizePayment:completion: callback.
  • Your application communicates asynchronously with your payment processor and website backend to actually make a charge with those payment details. Once this complete, you invoke the completion handler that you're given as a parameter with either PKPaymentAuthorizationStatus.Success or PKPaymentAuthorizationStatus.Failure depending on the result.
  • The PKPaymentAuthorizationViewController spinner animates into a success or failure icon. If successful, a notification will arrive from PassBook indicating a charge on the customer's credit card.
  • Your delegate receives the paymentAuthorizationViewControllerDidFinish: callback. It is then responsible for calling dismissViewControllerAnimated:completion to dismiss the payment screen.

Status Indicator

Concretely, this comes out looking like this:

// MARK: - PKPaymentAuthorizationViewControllerDelegatefuncpaymentAuthorizationViewController(controller:PKPaymentAuthorizationViewController!,didAuthorizePaymentpayment:PKPayment!,completion:((PKPaymentAuthorizationStatus)->Void)!){// Use your payment processor's SDK to finish charging your customer.// When this is done, call completion(PKPaymentAuthorizationStatus.Success)}funcpaymentAuthorizationViewControllerDidFinish(controller:PKPaymentAuthorizationViewController!){dismissViewControllerAnimated(true,completion:nil)}

Here, the processPayment:payment completion: method is your own code, and would leverage your payment processor's SDK to finish the charge.

Dynamic Shipping Methods and Pricing

If you're using Apple Pay to let your customer buy physical goods, you might want to offer them different shipping options. You can do this by setting the shippingMethods property on PKPaymentRequest. Then, you can respond to your customer's selection by implementing the optional PKPaymentAuthorizationViewControllerDelegate method, paymentAuthorizationViewController:didSelectShippingMethod:completion:. This method follows a similar pattern to the didAuthorizePayment method described above, where you're allowed to do asynchronous work and then call a callback with an updated array of PKPaymentSummaryItems that includes the customer's desired shipping method. (Remember from earlier that PKShippingMethod inherits from PKPaymentSummaryItem? This is really helpful here!)

Here's a modified version of our earlier example, implemented as a computed property on the view controller and helper function:

varpaymentRequest:PKPaymentRequest{letrequest=...// initialize as beforeletfreeShipping=PKShippingMethod(label:"Free Shipping",amount:NSDecimalNumber(string:"0"))freeShipping.identifier="freeshipping"freeShipping.detail="Arrives in 6-8 weeks"letexpressShipping=PKShippingMethod(label:"Express Shipping",amount:NSDecimalNumber(string:"10.00"))expressShipping.identifier="expressshipping"expressShipping.detail="Arrives in 2-3 days"request.shippingMethods=[freeShipping,expressShipping]request.paymentSummaryItems=paymentSummaryItemsForShippingMethod(freeShipping)returnrequest}funcpaymentSummaryItemsForShippingMethod(shipping:PKShippingMethod)->([PKPaymentSummaryItem]){letwax=PKPaymentSummaryItem(label:"Mustache Wax",amount:NSDecimalNumber(string:"10.00"))letdiscount=PKPaymentSummaryItem(label:"Discount",amount:NSDecimalNumber(string:"-1.00"))lettotalAmount=wax.amount.decimalNumberByAdding(discount.amount).decimalNumberByAdding(shipping.amount)lettotal=PKPaymentSummaryItem(label:"NSHipster",amount:totalAmount)return[wax,discount,shipping,total]}// MARK: - PKPaymentAuthorizationViewControllerDelegatefuncpaymentAuthorizationViewController(controller:PKPaymentAuthorizationViewController!,didSelectShippingMethodshippingMethod:PKShippingMethod!,completion:((PKPaymentAuthorizationStatus,[AnyObject]!)->Void)!){completion(PKPaymentAuthorizationStatus.Success,paymentSummaryItemsForShippingMethod(shippingMethod))}

In this example, the customer will get the option to choose either free or express shipping—and the price they're quoted will adjust accordingly as they change their selection.

But wait, there's more!

Instead of having to provide a bunch of flat-rate shipping options, you can let your customer choose their shipping address and then calculate shipping rates dynamically based on that. To do that, you'll first need to set the requiredShippingAddressFields property on your PKPaymentRequest. This can represent any combination of PKAddressField.Email , .PhoneNumber, and .PostalAddress.

Alternatively, if you don't need the user's full mailing address but need to collect some contact information (like an email address to send receipts to), this is a good way to do it.

When this field is set, a new "Shipping Address" row appears in the payment UI that allows the customer to choose one of their saved addresses. Every time they choose one, the (aptly named) paymentAuthorizationViewController:didSelectShippingAddress:completion: message will be sent to your PKPaymentAuthorizationViewControllerDelegate.

Here, you should calculate the shipping rates for the selected address and then call the completion callback with 3 arguments:

  1. The result of the call
    • PKPaymentAuthorizationStatus.Success if successful
    • `PKPaymentAuthorizationStatus.Failure` if a connection error occurs
    • .InvalidShippingPostalAddress if the API returns an empty array (i.e. shipping to that address is impossible).
  2. An array of PKShippingMethods representing the customer's available shipping options
  3. A new array of PKPaymentSummaryItems that contains one of the shipping methods

I've set up a really simple web backend that queries the EasyPost API for shipping rates to a given address. The source is available at https://github.com/jflinter/example-shipping-api.

Here's a function to query it, using Alamofire:

importAddressBookimportPassKitimportAlamofirefuncaddressesForRecord(record:ABRecord)->[[String:String]]{varaddresses:[[String:String]]=[]letvalues:ABMultiValue=ABRecordCopyValue(record,kABPersonAddressProperty).takeRetainedValue()forindexin0..<ABMultiValueGetCount(values){ifletaddress=ABMultiValueCopyValueAtIndex(values,index).takeRetainedValue()as?[String:String]{addresses.append(address)}}returnaddresses}funcfetchShippingMethodsForAddress(address:[String:String],completion:([PKShippingMethod]?)->Void){letparameters=["street":address[kABPersonAddressStreetKey]??"","city":address[kABPersonAddressCityKey]??"","state":address[kABPersonAddressStateKey]??"","zip":address[kABPersonAddressZIPKey]??"","country":address[kABPersonAddressCountryKey]??""]Alamofire.request(.GET,"http://example.com",parameters:parameters).responseJSON{(_,_,JSON,_)inifletrates=JSONas?[[String:String]]{letshippingMethods=map(rates){(rate)->PKShippingMethodinletidentifier=rate["id"]letcarrier=rate["carrier"]??"Unknown Carrier"letservice=rate["service"]??"Unknown Service"letamount=NSDecimalNumber(string:rate["amount"])letarrival=rate["formatted_arrival_date"]??"Unknown Arrival"letshippingMethod=PKShippingMethod(label:"\(carrier) \(service)",amount:amount)shippingMethod.identifier=identifiershippingMethod.detail=arrivalreturnshippingMethod}}}}

With this, it's simple to implement PKPaymentAuthorizationViewControllerDelegate:

funcpaymentAuthorizationViewController(controller:PKPaymentAuthorizationViewController!,didSelectShippingAddressrecord:ABRecord!,completion:((PKPaymentAuthorizationStatus,[AnyObject]!,[AnyObject]!)->Void)!){ifletaddress=addressesForRecord(record).first{fetchShippingMethodsForAddress(address){(shippingMethods)inswitchshippingMethods?.count{case.None:completion(PKPaymentAuthorizationStatus.Failure,nil,nil)case.Some(0):completion(PKPaymentAuthorizationStatus.InvalidShippingPostalAddress,nil,nil)default:completion(PKPaymentAuthorizationStatus.Success,shippingMethods,self.paymentSummaryItemsForShippingMethod(shippingMethods!.first!))}}}else{completion(PKPaymentAuthorizationStatus.Failure,nil,nil)}}

Select a Shipping Method

Now, the customer can select an address and receive a different set of shipping options depending on where they live. Both the shippingAddress and shippingMethod they ultimately select will be available as properties on the PKPayment that is given to your delegate in the paymentAuthorizationViewController:didAuthorizePayment:completion: method.

You can find all of the source code in this article at https://github.com/jflinter/ApplePayExample.


Even though Apple Pay only exposes a small number of public APIs, its possible applications are wide-ranging and you can customize your checkout flow to fit your app. It even enables you to build new types of flows, such as letting your customers buy stuff without having to create an account first.

As more apps start using Apple Pay (and as more customers own devices that support it), it'll become a ubiquitous way of paying for things in iOS apps. I'm excited to hear what you build with Apple Pay— if you have any questions, or want to show anything off, please get in touch!

NSUndoManager

$
0
0

We all make mistakes. Thankfully, Foundation comes to our rescue for more than just our misspellings. Cocoa includes a simple yet robust API for undoing or redoing actions through NSUndoManager.

By default, each application window has an undo manager, and any object in the responder chain can manage a custom undo manager for performing undo and redo operations local to their respective view. UITextField and UITextArea use this functionality to automatically provide support for undoing text edits while first responder. However, indicating whether other actions can be undone is an exercise left for the app developer.

Creating an undoable action requires three steps: performing a change, registering an "undo operation" which can reverse the change, and responding to a request to undo the change.

Undo Operations

To show an action can be undone, register an "undo operation" while performing the action. The Undo Architecture documentation defines an "undo operation" as:

A method for reverting a change to an object, along with the arguments needed to revert the change.

The operation specifies:

  • The object to receive a message if an undo is requested
  • The message to send and
  • The arguments to pass with the message

If the method invoked by the undo operation also registers an undo operation, the undo manager provides redo support without extra work, as it is "undoing the undo".

There are two types of undo operations, "simple" selector-based undo and complex "NSInvocation-based undo".

Registering a Simple Undo Operation

To register a simple undo operation, invoke NSUndoManger -registerUndoWithTarget:selector:object: on a target which can undo the action. The target is not necessarily the modified object, and is often a utility or container which manages the object's state. Specify the name of the undo action at the same time, using NSUndoManager -setActionName:. The undo dialog shows the name of the action, so it should be localized.

funcupdateScore(score:NSNumber){undoManager.registerUndoWithTarget(self,selector:Selector("updateScore:"),object:score)undoManager.setActionName(NSLocalizedString("actions.update",comment:"Update Score"))myMovie.score=score}
-(void)updateScore:(NSNumber*)score{[undoManagerregisterUndoWithTarget:selfselector:@selector(updateScore:)object:score];[undoManagersetActionName:NSLocalizedString(@"actions.update",@"Update Score")];myMovie.score=score;}

Registering a Complex Undo Operation with NSInvocation

Simple undo operations may be too rigid for some uses, as undoing an action may require more than one argument. In these cases, we can leverage NSInvocation to record the selector and arguments required. Calling prepareWithInvocationTarget: records which object will receive the message which will make the change.

funcmovePiece(piece:ChessPiece,row:UInt,column:UInt){letundoController:ViewController=undoManager?.prepareWithInvocationTarget(self)asViewControllerundoController.movePiece(piece,row:piece.row,column:piece.column)undoManager?.setActionName(NSLocalizedString("actions.move-piece","Move Piece"))piece.row=rowpiece.column=columnupdateChessboard()}
-(void)movePiece:(ChessPiece*)piecetoRow:(NSUInteger)rowcolumn:(NSUInteger)column{[[undoManagerprepareWithInvocationTarget:self]movePiece:pieceToRow:piece.rowcolumn:piece.column];[undoManagersetActionName:NSLocalizedString(@"actions.move-piece",@"Move Piece")];piece.row=row;piece.column=column;[selfupdateChessboard];}

The magic here is that NSUndoManager implements forwardInvocation:. When the undo manager receives the message to undo -movePiece:row:column:, it forwards the message to the target since NSUndoManager does not implement this method.

Performing an Undo

Once undo operations are registered, actions can be undone and redone as needed, using NSUndoManager -undo and NSUndoManager -redo.

Responding to the Shake Gesture on iOS

By default, users trigger an undo operation by shaking the device. If a view controller should handle an undo request, the view controller must:

  1. Be able to become first responder
  2. Become first responder once its view appears,
  3. Resign first responder when its view disappears

When the view controller then receives the motion event, the operating system presents a dialog to the user when undo or redo actions are available. The undoManager property of the view controller will handle the user's choice without further involvement.

classViewController:UIViewController{overridefuncviewDidAppear(animated:Bool){super.viewDidAppear(animated)becomeFirstResponder()}overridefuncviewWillDisappear(animated:Bool){super.viewWillDisappear(animated)resignFirstResponder()}overridefunccanBecomeFirstResponder()->Bool{returntrue}// ...}
@implementationViewController-(void)viewDidAppear:(BOOL)animated{[superviewDidAppear:animated];[selfbecomeFirstResponder];}-(void)viewWillDisappear:(BOOL)animated{[superviewWillDisappear:animated];[selfresignFirstResponder];}-(BOOL)canBecomeFirstResponder{returnYES;}// ...@end

Customizing the Undo Stack

Grouping Actions Together

All undo operations registered during a single run loop will be undone together, unless "undo groups" are otherwise specified. Grouping allows undoing or redoing many actions at once. Although each action can be performed and undone individually, if the user performs two at once, undoing both at once preserves a consistent user experience.

funcreadAndArchiveEmail(email:Email){undoManager?.beginUndoGrouping()markEmail(email,read:true)archiveEmail(email)undoManager?.setActionName(NSLocalizedString("actions.read-archive",comment:"Mark as Read and Archive"))undoManager?.endUndoGrouping()}funcmarkEmail(email:Email,read:Bool){letundoController:ViewController=undoManager?.prepareWithInvocationTarget(self)asViewControllerundoController.markEmail(email,read:email.read)undoManager?.setActionName(NSLocalizedString("actions.read",comment:"Mark as Read"))email.read=read}funcarchiveEmail(email:Email){letundoController:ViewController=undoManager?.prepareWithInvocationTarget(self)asViewControllerundoController.moveEmail(email,toFolder:"Inbox")undoManager?.setActionName(NSLocalizedString("actions.archive",comment:"Archive"))moveEmail(email,toFolder:"All Mail")}
-(void)readAndArchiveEmail:(Email*)email{[undoManagerbeginUndoGrouping];[selfmarkEmail:emailasRead:YES];[selfarchiveEmail:email];[undoManagersetActionName:NSLocalizedString(@"actions.read-archive",@"Mark as Read and Archive")];[undoManagerendUndoGrouping];}-(void)markEmail:(Email*)emailasRead:(BOOL)isRead{[[undoManagerprepareWithInvocationTarget:self]markEmail:emailasRead:[emailisRead]];[undoManagersetActionName:NSLocalizedString(@"actions.read",@"Mark as Read")];email.read=isRead;}-(void)archiveEmail:(Email*)email{[[undoManagerprepareWithInvocationTarget:self]moveEmail:emailtoFolder:@"Inbox"];[undoManagersetActionName:NSLocalizedString(@"actions.archive",@"Archive")];[selfmoveEmail:emailtoFolder:@"All Mail"];}

Clearing the Stack

Sometimes the undo manager's list of actions should be cleared to avoid confusing the user with unexpected results. The most common cases are when the context changes dramatically, like changing the visible view controller on iOS or externally made changes occurring on an open document. When that time comes, the undo manager's stack can be cleared using NSUndoManager -removeAllActions or NSUndoManager -removeAllActionsWithTarget: if finer granularity is needed.

Caveats

If an action has different names for undo versus redo, check whether an undo operation is occurring before setting the action name to ensure the title of the undo dialog reflects which action will be undone. An example would be a pair of opposing operations, like adding and removing an object:

funcaddItem(item:NSObject){undoManager?.registerUndoWithTarget(self,selector:Selector("removeItem:"),object:item)ifundoManager?.undoing==false{undoManager?.setActionName(NSLocalizedString("action.add-item",comment:"Add Item"))}myArray.append(item)}funcremoveItem(item:NSObject){ifletindex=find(myArray,item){undoManager?.registerUndoWithTarget(self,selector:Selector("addItem:"),object:item)ifundoManager?.undoing==false{undoManager?.setActionName(NSLocalizedString("action.remove-item",comment:"Remove Item"))}myArray.removeAtIndex(index)}}
-(void)addItem:(id)item{[undoManagerregisterUndoWithTarget:selfselector:@selector(removeItem:)object:item];if(![undoManagerisUndoing]){[undoManagersetActionName:NSLocalizedString(@"actions.add-item",@"Add Item")];}[myArrayaddObject:item];}-(void)removeItem:(id)item{[undoManagerregisterUndoWithTarget:selfselector:@selector(addItem:)object:item];if(![undoManagerisUndoing]){[undoManagersetActionName:NSLocalizedString(@"actions.remove-item",@"Remove Item")];}[myArrayremoveObject:item];}

If your test framework runs many tests as a part of one run loop (like Kiwi), clear the undo stack between tests in teardown. Otherwise tests will share undo state and invoking NSUndoManager -undo during a test may lead to unexpected results.


There are even more ways to refine behavior with NSUndoManager, particularly for grouping actions and managing scope. Apple also provides usability guidelines for making undo and redo accessible in an expected and delightful way.

We all may wish to live without mistakes, but Cocoa gives us a way to let our users live with fewer regrets as it makes some actions easily changeable.

The Death of Cocoa

$
0
0

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

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

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

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

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

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

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


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

We owe all of our productivity to standard libraries.

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

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

Standard libraries should implement standards.

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

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

Numbers

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

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

Strings

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

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

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

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

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

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

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

URI, URL, and URN

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

Data Structures

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

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

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

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

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

Dates & Times

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

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

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

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

Interchange Formats

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

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

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

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

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

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

Regular Expressions

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

Errors

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

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

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

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

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

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

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

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

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

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

AppKit & UIKit

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


Thinking Further

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

Reader Submissions - New Year's 2015

$
0
0

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

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

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


The Secret Lives of Member Functions

From Robert Widmann:

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

Object -> (Args) -> Thing

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

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

@( ) for Boxing C-Strings

From Samuel Defago:

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

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

AmIBeingDebugged

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

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

Use Lazy Variables

From Colin Rofls:

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

lazyvarsomeModelStructure=ExpensiveClass()

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

Accessing Child Controllers Inserted Into Storyboard Container Views

From Vadim Shpakovski:

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

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

Re-Run without Re-Building

From Heath Borders:

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

Quick Access to Playground Resources

From Jon Friskics:

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

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

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

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

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

...or pulling out a local image:

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

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

CocoaPods, Exposed!

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

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

CREATE_INFOPLIST_SECTION_IN_BINARY

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

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

Stop dylib Hooking

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

Add this one line to your "Other Linker Flags":

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

NSBundle -preferredLocalizations

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

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

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

From the documentation:

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

From a comment in NSBundle.h:

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

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

Preserve SDK Headers

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

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

Inspecting void * Instance Variables

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

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

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

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

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

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

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

NSDateFormatter +dateFormatFromTemplate:options:locale:

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

From the documentation:

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

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

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

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

Deriving Internal Constants with the Debugger

Recently, Matthias Tretter asked on Twitter:

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

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

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

(lldb) finish

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

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

Answer: the default duration is 0.4s.

DIY Weak Associated Objects

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

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

Then, associate the WeakObjectContainter with OBJC_ASSOCIATION_RETAIN(_NONATOMIC):

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

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

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

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

May your code continue to compile and inspire.

Changing of the Guard

$
0
0

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

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

The best is yet to come for NSHipster.

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

May your code continue to compile and inspire.

Long Live Cocoa

$
0
0

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

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


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

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

Hand in Hand

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

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

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

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

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

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

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

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

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


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

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

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

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

Getting Better All The Time

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

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

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

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


JavaScriptCore

$
0
0

An updated ranking of programming language popularity is out this week, showing Swift leaping upward through the ranks from 68th to 22nd, while Objective-C holds a strong lead up ahead at #10. Both, however, are blown away by the only other language allowed to run natively on iOS: the current champion, JavaScript.

Introduced with OS X Mavericks and iOS 7, the JavaScriptCore framework puts an Objective-C wrapper around WebKit's JavaScript engine, providing easy, fast, and safe access to the world's most prevalent language. Love it or hate it, JavaScript's ubiquity has led to an explosion of developers, tools, and resources along with ultra-fast virtual machines like the one built into OS X and iOS.

So come, lay aside bitter debates about dynamism and type safety, and join me for a tour of JavaScriptCore.


JSContext / JSValue

JSContext is an environment for running JavaScript code. A JSContext instance represents the global object in the environment—if you've written JavaScript that runs in a browser, JSContext is analogous to window. After creating a JSContext, it's easy to run JavaScript code that creates variables, does calculations, or even defines functions:

letcontext=JSContext()context.evaluateScript("var num = 5 + 5")context.evaluateScript("var names = ['Grace', 'Ada', 'Margaret']")context.evaluateScript("var triple = function(value) { return value * 3 }")lettripleNum:JSValue=context.evaluateScript("triple(num)")
JSContext*context=[[JSContextalloc]init];[contextevaluateScript:@"var num = 5 + 5"];[contextevaluateScript:@"var names = ['Grace', 'Ada', 'Margaret']"];[contextevaluateScript:@"var triple = function(value) { return value * 3 }"];JSValue*tripleNum=[contextevaluateScript:@"triple(num)"];

As that last line shows, any value that comes out of a JSContext is wrapped in a JSValue object. A language as dynamic as JavaScript requires a dynamic type, so JSValue wraps every possible kind of JavaScript value: strings and numbers; arrays, objects, and functions; even errors and the special JavaScript values null and undefined.

JSValue includes a host of methods for accessing its underlying value as the correct Foundation type, including:

JavaScript TypeJSValue methodObjective-C TypeSwift Type
stringtoStringNSStringString!
booleantoBoolBOOLBool
numbertoNumber
toDouble
toInt32
toUInt32
NSNumber
double
int32_t
uint32_t
NSNumber!
Double
Int32
UInt32
DatetoDateNSDateNSDate!
ArraytoArrayNSArray[AnyObject]!
ObjecttoDictionaryNSDictionary[NSObject : AnyObject]!
ObjecttoObject
toObjectOfClass:
custom typecustom type

To retrieve the value of tripleNum from the above example, simply use the appropriate method:

println("Tripled: \(tripleNum.toInt32())")// Tripled: 30
NSLog(@"Tripled: %d",[tripleNumtoInt32]);// Tripled: 30

Subscripting Values

We can easily access any values we've created in our context using subscript notation on both JSContext and JSValue instances. JSContext requires a string subscript, while JSValue allows either string or integer subscripts for delving down into objects and arrays:

letnames=context.objectForKeyedSubscript("names")letinitialName=names.objectAtIndexedSubscript(0)println("The first name: \(initialName.toString())")// The first name: Grace
JSValue*names=context[@"names"];JSValue*initialName=names[0];NSLog(@"The first name: %@",[initialNametoString]);// The first name: Grace

Swift shows its youth, here—while Objective-C code can take advantage of subscript notation, Swift currently only exposes the raw methods that should make such subscripting possible: objectAtKeyedSubscript() and objectAtIndexedSubscript().

Calling Functions

With a JSValue that wraps a JavaScript function, we can call that function directly from our Objective-C/Swift code using Foundation types as parameters. Once again, JavaScriptCore handles the bridging without any trouble:

lettripleFunction=context.objectForKeyedSubscript("triple")letresult=tripleFunction.callWithArguments([5])println("Five tripled: \(result.toInt32())")
JSValue*tripleFunction=context[@"triple"];JSValue*result=[tripleFunctioncallWithArguments:@[@5]];NSLog(@"Five tripled: %d",[resulttoInt32]);

Exception Handling

JSContext has another useful trick up its sleeve: by setting the context's exceptionHandler property, you can observe and log syntax, type, and runtime errors as they happen. exceptionHandler is a callback handler that receives a reference to the JSContext and the exception itself:

context.exceptionHandler={context,exceptioninprintln("JS Error: \(exception)")}context.evaluateScript("function multiply(value1, value2) { return value1 * value2 ")// JS Error: SyntaxError: Unexpected end of script
context.exceptionHandler=^(JSContext*context,JSValue*exception){NSLog(@"JS Error: %@",exception);};[contextevaluateScript:@"function multiply(value1, value2) { return value1 * value2 "];// JS Error: SyntaxError: Unexpected end of script

JavaScript Calling

Now we know how to extract values from a JavaScript environment and call functions defined therein. What about the reverse? How can we get access our custom objects and methods, defined in Objective-C or Swift, from within the JavaScript realm?

There are two main ways of giving a JSContext access to our native client code: blocks and the JSExport protocol.

Blocks

When an Objective-C block is assigned to an identifier in a JSContext, JavaScriptCore automatically wraps the block in a JavaScript function. This makes it simple to use Foundation and Cocoa classes from within JavaScript—again, all the bridging happens for you. Witness the full power of CFStringTransform, now accessible to JavaScript:

letsimplifyString:@objc_blockString->String={inputinvarmutableString=NSMutableString(string:input)asCFMutableStringRefCFStringTransform(mutableString,nil,kCFStringTransformToLatin,Boolean(0))CFStringTransform(mutableString,nil,kCFStringTransformStripCombiningMarks,Boolean(0))returnmutableString}context.setObject(unsafeBitCast(simplifyString,AnyObject.self),forKeyedSubscript:"simplifyString")println(context.evaluateScript("simplifyString('안녕하새요!')"))// annyeonghasaeyo!
context[@"simplifyString"]=^(NSString*input){NSMutableString*mutableString=[inputmutableCopy];CFStringTransform((__bridgeCFMutableStringRef)mutableString,NULL,kCFStringTransformToLatin,NO);CFStringTransform((__bridgeCFMutableStringRef)mutableString,NULL,kCFStringTransformStripCombiningMarks,NO);returnmutableString;};NSLog(@"%@",[contextevaluateScript:@"simplifyString('안녕하새요!')"]);

There's another speedbump for Swift here—note that this only works for Objective-C blocks, not Swift closures. To use a Swift closure in a JSContext, it needs to be (a) declared with the @objc_block attribute, and (b) cast to AnyObject using Swift's knuckle-whitening unsafeBitCast() function.

Memory Management

Since blocks can capture references to variables and JSContexts maintain strong references to all their variables, some care needs to be taken to avoid strong reference cycles. Avoid capturing your JSContext or any JSValues inside a block. Instead, use [JSContext currentContext] to get the current context and pass any values you need as parameters.

JSExport Protocol

Another way to use our custom objects from within JavaScript code is to add conformance to the JSExport protocol. Whatever properties, instance methods, and class methods we declare in our JSExport-inherited protocol will automatically be available to any JavaScript code. We'll see how in the following section.

JavaScriptCore in Practice

Let's build out an example that will use all these different techniques—we'll define a Person model that conforms to the JSExport sub-protocol PersonJSExports, then use JavaScript to create and populate instances from a JSON file. Who needs NSJSONSerialization when there's an entire JVM lying around?

1) PersonJSExports and Person

Our Person class implements the PersonJSExports protocol, which specifies what properties should be available in JavaScript.

The create... class method is necessary because JavaScriptCore does not bridge initializers—we can't simply say var person = new Person() the way we would with a native JavaScript type.

// Custom protocol must be declared with `@objc`@objcprotocolPersonJSExports:JSExport{varfirstName:String{getset}varlastName:String{getset}varbirthYear:NSNumber?{getset}funcgetFullName()->String/// create and return a new Person instance with `firstName` and `lastName`classfunccreateWithFirstName(firstName:String,lastName:String)->Person}// Custom class must inherit from `NSObject`@objcclassPerson:NSObject,PersonJSExports{// properties must be declared as `dynamic`dynamicvarfirstName:StringdynamicvarlastName:StringdynamicvarbirthYear:NSNumber?init(firstName:String,lastName:String){self.firstName=firstNameself.lastName=lastName}classfunccreateWithFirstName(firstName:String,lastName:String)->Person{returnPerson(firstName:firstName,lastName:lastName)}funcgetFullName()->String{return"\(firstName) \(lastName)"}}
// in Person.h -----------------@classPerson;

@protocolPersonJSExports<JSExport>@property(nonatomic,copy)NSString*firstName;@property(nonatomic,copy)NSString*lastName;@propertyNSIntegerageToday;-(NSString*)getFullName;// create and return a new Person instance with `firstName` and `lastName`+(instancetype)createWithFirstName:(NSString*)firstNamelastName:(NSString*)lastName;@end@interfacePerson : NSObject<PersonJSExports>@property(nonatomic,copy)NSString*firstName;@property(nonatomic,copy)NSString*lastName;@propertyNSIntegerageToday;@end// in Person.m -----------------@implementationPerson-(NSString*)getFullName{return[NSStringstringWithFormat:@"%@ %@",self.firstName,self.lastName];}+(instancetype)createWithFirstName:(NSString*)firstNamelastName:(NSString*)lastName{Person*person=[[Personalloc]init];person.firstName=firstName;person.lastName=lastName;returnperson;}@end

2) JSContext Configuration

Before we can use the Person class we've created, we need to export it to the JavaScript environment. We'll also take this moment to import the Mustache JS library, which we'll use to apply templates to our Person objects later.

// export Person classcontext.setObject(Person.self,forKeyedSubscript:"Person")// load Mustache.jsifletmustacheJSString=String(contentsOfFile:...,encoding:NSUTF8StringEncoding,error:nil){context.evaluateScript(mustacheJSString)}
// export Person classcontext[@"Person"]=[Personclass];// load Mustache.jsNSString*mustacheJSString=[NSStringstringWithContentsOfFile:...encoding:NSUTF8StringEncodingerror:nil];[contextevaluateScript:mustacheJSString];

3) JavaScript Data & Processing

Here's a look at our simple JSON example and the code that will process it to create new Person instances.

Note: JavaScriptCore translates Objective-C/Swift method names to be JavaScript-compatible. Since JavaScript doesn't have named parameters, any external parameter names are converted to camel-case and appended to the function name. In this example, the Objective-C method createWithFirstName:lastName: becomes createWithFirstNameLastName() in JavaScript.

varloadPeopleFromJSON=function(jsonString){vardata=JSON.parse(jsonString);varpeople=[];for(i=0;i<data.length;i++){varperson=Person.createWithFirstNameLastName(data[i].first,data[i].last);person.birthYear=data[i].year;people.push(person);}returnpeople;}
[{"first":"Grace","last":"Hopper","year":1906},{"first":"Ada","last":"Lovelace","year":1815},{"first":"Margaret","last":"Hamilton","year":1936}]

4) Tying It All Together

All that remains is to load the JSON data, call into the JSContext to parse the data into an array of Person objects, and render each Person using a Mustache template:

// get JSON stringifletpeopleJSON=NSString(contentsOfFile:...,encoding:NSUTF8StringEncoding,error:nil){// get load functionletload=context.objectForKeyedSubscript("loadPeopleFromJSON")// call with JSON and convert to an array of `Person`ifletpeople=load.callWithArguments([peopleJSON]).toArray()as?[Person]{// get rendering function and create templateletmustacheRender=context.objectForKeyedSubscript("Mustache").objectForKeyedSubscript("render")lettemplate="{{getFullName}}, born {{birthYear}}"// loop through people and render Person object as stringforpersoninpeople{println(mustacheRender.callWithArguments([template,person]))}}}// Output:// Grace Hopper, born 1906// Ada Lovelace, born 1815// Margaret Hamilton, born 1936
// get JSON stringNSString*peopleJSON=[NSStringstringWithContentsOfFile:...encoding:NSUTF8StringEncodingerror:nil];// get load functionJSValue*load=context[@"loadPeopleFromJSON"];// call with JSON and convert to an NSArrayJSValue*loadResult=[loadcallWithArguments:@[peopleJSON]];NSArray*people=[loadResulttoArray];// get rendering function and create templateJSValue*mustacheRender=context[@"Mustache"][@"render"];NSString*template=@"{{getFullName}}, born {{birthYear}}";// loop through people and render Person object as stringfor(Person*personinpeople){NSLog(@"%@",[mustacheRendercallWithArguments:@[template,person]]);}// Output:// Grace Hopper, born 1906// Ada Lovelace, born 1815// Margaret Hamilton, born 1936

How can you use JavaScript in your apps? JavaScript snippets could be the basis for user-defined plugins that ship alongside yours. If your product started out on the web, you may have existing infrastructure that can be used with only minor changes. Or if you started out as a programmer on the web, you might relish the chance to get back to your scripty roots. Whatever the case, JavaScriptCore is too well-built and powerful to ignore.

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, the category. Namely, you can't add a property to an existing class via extension.

Happily, Objective-C associated objects come to the rescue. For example, to add a descriptiveName property to all the view controllers in a project, we simply 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?,UInt(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 subclass        ifself!==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{println("viewWillAppear: \(name)")}else{println("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 a 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 understated. 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:

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 succesful 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 sure 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?}

assume_nonnull

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 #pragma declaration can help streamline the process of annotating existing classes. Simply mark the beginning and end of a section of your header with #pragma clang 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 : NSObject#pragma clang assume_nonnull begin@property(nonatomic,readonly)NSArray*locations;@property(nullable,nonatomic,readonly)Location*latestLocation;-(void)addPhoto:(Photo*)photoforLocation:(Location*)location;-(nullablePhoto*)photoForLocation:(Location*)location;#pragma clang 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!

Viewing all 382 articles
Browse latest View live