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

guard & defer

$
0
0

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

Edsger W. Dijkstra, “Go To Considered Harmful”

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

Let’s defer defer and first take on guard.


guard

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

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

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

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

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

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

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

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

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

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

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

defer

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

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

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

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

Error prone? Yes. Frustratingly repetitive? Check.

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

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

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

Safe and clean. Swift at its best.

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

(Any Other) Defer Considered Harmful

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

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

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

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

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


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


Reader Submissions - New Year's 2016

$
0
0

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

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


Swift’s defer in Objective-C

From Nolan O’Brien:

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

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

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

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

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

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


Swift Protocol Extensions

From Juraj Hilje:

Keep inheritance trees shallow and use protocol composition:

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

Public Read-only Variables

From Stephen Celis:

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

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

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

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

Swift where Everywhere

From Taylor Franklin:

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

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

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


Improved Optional Binding

From Ursu Dan:

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

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

Unbuffered xcodebuild Output

From Cédric Luthi:

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

env NSUnbufferedIO=YES xcodebuild [flags] | xcpretty
    

Multiline Labels in a Table View

From Ray Fix:

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

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

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

Multiline demo


AmIRunningAsAnExtension

Another from Nolan O’Brien:

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

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

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

That frees you to do things like this:

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

Beyond Breakpoints

From Matthew Flint:

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

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

Breakpoint Options Popup

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


Fix Console po frame Printing

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

Broken po frame

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

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

Fixed po frame


Avoiding -DDEBUG in Swift

From GitHub user @vlat456:

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

// PreProcessorMacros.m:#include "PreProcessorMacros.h"
    #ifdef DEBUG
    BOOLconstDEBUG_BUILD=YES;#else
    BOOLconstDEBUG_BUILD=NO;#endif
    // PreProcessorMacros.h:#ifndef PreProcessorMacros_h
    #define PreProcessorMacros_h
    #include
    externBOOLconstDEBUG_BUILD;#endif /* PreProcessorMacros_h */// in Bridged header:#import "PreProcessorMacros.h"

And then from Swift:

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

Checking For Null Blocks

From Nicolás Jakubowski:

This macro for checking block nullability before executing them:

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

Old and busted:

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

New and shiny:

BLOCK_EXEC(completionBlock,arg1,arg2);

Swiftier GCD

From Luo Jie:

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

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

Downloading something then could be written like this:

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

_ObjectiveCBridgeable

From Mathew Huusko V:

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

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

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

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

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

Phantom Types

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

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

Easier Configuration

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

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

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

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

UITextChecker

$
0
0

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

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


Spell Checking

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

Autocorrecting 'hipstar'

We can find the same suggested substitution using UITextChecker:

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

The returned array of strings might look like this one:

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

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

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

Learning New Words

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

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

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

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

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

Suggesting Completions

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

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

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

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


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

NSHipster Quiz #8

$
0
0

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

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

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

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

Round 1: General Knowledge

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

    funcstringByReplacingCharactersInRange(_range:NSRange,        withStringreplacement:String)->String
  7. Write the Swift 3 code to execute an asynchronous “Hello, world!” using GCD.
  8. Swift went open source in November and the pace of community contributions has been amazing to see. Within 100, how many pull requests (open, closed, or merged) has the Swift project received on GitHub?
  9. Swift was released to the public just over two years ago, but was clearly under development long before that at Apple. What were the month and year of the first commit to the Swift repository?
  10. After Chris Lattner, who was the second contributor to Swift? When was their first commit?

Round 2: Name That Framework

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

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

Round 3: Who Is That?

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

Round 4: Easy as 1, 2, 3

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

1

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

2

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

3

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

4

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

5

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

6

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

7

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

8

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

9

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

10

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

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



.

.

.


Answers

Round 1: General Knowledge

  1. Mount Whitney
  2. San Francisco 49ers
  3. The Sugarhill Gang, 1979 (2 points for both)
  4. iPhoto ’09
  5. NSExpression
  6. One of:

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

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

Round 2: Name That Framework

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

Round 3: Who Is That?

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

Round 4: Easy as 1, 2, 3

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

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

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

NSRegularExpression

$
0
0

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

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

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

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

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

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

The prosecution rests.

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


NSString Methods

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

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

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

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

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

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

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

NSRange and Swift

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

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

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

Confusion. Despair.

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

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

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

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

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

NSRegularExpression& NSTextCheckingResult

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

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

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

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

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

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

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

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

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

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

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

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

Expression and Matching Options

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

NSRegularExpression.Options

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

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

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

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

Partial Matching

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

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

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

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

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


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

Swift GYB

$
0
0

The term “boilerplate” goes back to the early days of print media. Small regional newspapers had column inches to fill, but typically lacked the writing staff to make this happen, so many of them turned to large print syndicates for a steady flow of content that could be added verbatim into the back pages of their dailies. These stories would often be provided on pre-set plates, which resembled the rolled sheets of steel used to make boilers, hence the name.

Through a process of metonymy, the content itself came to be known as “boilerplate”, and the concept was appropriated to encompass standardized, formulaic text in contracts, form letters, and, most relevant to this week’s article on NSHipster, code.


Not all code can be glamorous. In fact, a lot of the low-level infrastructure that makes everything work is a slog of boilerplate.

This is true of the Swift standard library, which includes families of types like signed integers (Int8, Int16, Int32, Int64) whose implementation varies only in the size of the respective type.

Copy-pasting code may work as a one-off solution (assuming you manage to get it right the first time), but it’s not sustainable. Each time you want to make changes to these derived implementations, you risk introducing slight inconsistencies that cause the implementations to diverge over time — not unlike the random mutations responsible for the variation of life on Earth.

Languages have various techniques to cope with this, from C++ templates and Lisp macros to eval and C preprocessor statements.

Swift doesn’t have a macro system, and because the standard library is itself written in Swift, it can’t take advantage of C++ metaprogramming capabilities. Instead, the Swift maintainers use a Python script called gyb.py to generate source code using a small set of template tags.

GYB is an acronym for “Generate Your Boilerplate”, a reference to another Python tool, GYP, or “Generate Your Projects”.

How GYB Works

GYB is a lightweight templating system that allows you to use Python code for variable substitution and flow control:

  • The sequence %{ <#code#> } evaluates a block of Python code
  • The sequence % <#code#>: ... % end manages control flow
  • The sequence ${ <#code#> } substitutes the result of an expression

All other text is passed through unchanged.

A good example of GYB can be found in Codable.swift.gyb. At the top of the file, the base Codable types are assigned to an instance variable:

%{codable_types=['Bool','String','Double','Float','Int','Int8','Int16','Int32','Int64','UInt','UInt8','UInt16','UInt32','UInt64']}%

Later on, in the implementation of SingleValueEncodingContainer, these types are iterated over to generate the methods declarations for the protocol’s requirements:

%fortypeincodable_types:mutatingfuncencode(_value:${type},forKeykey:Key)throws%end

Evaluating the GYB template results in the following declarations:

mutatingfuncencode(_value:Bool)throwsmutatingfuncencode(_value:String)throwsmutatingfuncencode(_value:Double)throwsmutatingfuncencode(_value:Float)throwsmutatingfuncencode(_value:Int)throwsmutatingfuncencode(_value:Int8)throwsmutatingfuncencode(_value:Int16)throwsmutatingfuncencode(_value:Int32)throwsmutatingfuncencode(_value:Int64)throwsmutatingfuncencode(_value:UInt)throwsmutatingfuncencode(_value:UInt8)throwsmutatingfuncencode(_value:UInt16)throwsmutatingfuncencode(_value:UInt32)throwsmutatingfuncencode(_value:UInt64)throws

This pattern is used throughout the file to generate similarly formulaic declarations for methods like encode(_:forKey:), decode(_:forKey:), and decodeIfPresent(_:forKey:). In total, GYB reduces the amount of boilerplate code by a few thousand LOC:

$wc-l Codable.swift.gyb
          2183 Codable.swift.gyb
          $wc-l Codable.swift
          5790 Codable.swift
          

Important: A valid GYB template may not generate valid Swift code. If compilation errors occur in derived files, it may be difficult to determine the underlying cause.

Using GYB in Xcode

GYB isn’t part of the standard Xcode toolchain, so you won’t find it with xcrun. Instead, you can download the source code and then use the chmod command to make gyb executable (the default installation of Python on macOS should be able to run gyb):

$ wget https://github.com/apple/swift/raw/master/utils/gyb
          $ wget https://github.com/apple/swift/raw/master/utils/gyb.py
          $chmod +x gyb
          

Move these somewhere that can be accessed from your Xcode project, but keep them separate from your source files. For example, a Vendor directory at your project root.

In Xcode, click on the blue project file icon in the navigator, select the active target in your project, and navigate to the “Build Phases” panel. At the top, you’ll see a + symbol that you can click to add a new build phase. Select “Add New Run Script Phase”, and enter the following into the source editor:

find .-name'*.gyb' |                                               \while read file;do\
          ./path/to/gyb --line-directive''-o"${file%.gyb}""$file";\done

Make sure to order the GYB build phase before Compile Sources.

Now when you build your project any file with the .swift.gyb file extension is evaluated by GYB, which outputs a .swift file that’s compiled along with the rest of the code in the project.

When to Use GYB

As with any tool, knowing when to use it is just as important as knowing how. Here are some examples of when you might open your toolbox and reach for GYB.

Generating Formulaic Code

Are you copy-pasting the same code for elements in a set or items in a sequence? A for-in loop with variable substitution might be the solution.

As seen in the example with Codable from before, you can declare a collection at the top of your GYB template file and then iterate over that collection for type, property, or method declarations:

%{abilities=['strength','dexterity','constitution','intelligence','wisdom','charisma']}classCharacter{varname:String%forabilityinabilities:var${type}:Int%end}

Just be aware that a lot of repetition is a code smell, and may indicate that there’s a better way to accomplish your task. Built-in language feature like protocol extensions and generics can eliminate a lot of code duplication, so be on the lookout to use these instead of brute-forcing with GYB.

Generating Code Derived from Data

Are you writing code based on a data source? Try incorporating GYB into your development!

GYB files can import of Python packages like json, xml, and csv, so you can parse pretty much any kind of file you might encounter:

%{importcsv}%withopen('path/to/file.csv')asfile:%forrowincsv.DictReader(file):

If you want to see this in action, check out Currencies.swift.gyb which generates Swift enumerations for each of the currencies defined by the ISO 4217 specification.

Keep compilation fast and deterministic by downloading data to files that can be checked into source control rather than making HTTP requests or database queries in GYB files.

Code generation makes it trivial to keep your code in sync with the relevant standards. Simply update the data file and re-run GYB.


Swift has done a lot to cut down on boilerplate recently with the addition of compiler synthesis of Encodable and Decodable in 4.0, Equatable and Hashable in 4.1, and CaseIterable in 4.2. We hope that this momentum is carried in future updates to the language.

In the meantime, for everything else, GYB is a useful tool for code generation.

Another great tool from the community for this is Sourcery, which allows you to write templates in Swift (via Stencil) rather than Python.

“Don’t Repeat Yourself” may be a virtue in programming, but sometimes you have to say things a few times to make things work. When you do, you’ll be thankful to have a tool like GYB to say it for you.

Bug Reporting

$
0
0

“File a radar.” It’s a familiar refrain for those of us developing on Apple platforms.

It’s what you hear when you complain about a wandering UIKit component.
It’s the response you get when you share some hilariously outdated documentation.
It’s that voice inside your head when you reopen Xcode for the twelfth time today.

If you’ve ever been told to “file a Radar” and wondered what that meant, this week’s article has just the fix.


Radar is Apple’s bug tracking software. Any employee working in an engineering capacity interacts with it on a daily basis.

Radar is used to track features and bugs alike, in software, hardware, and everything else: documentation, localization, web properties — heck, even the responses you get from Siri.

When an Apple engineer hears the word “Radar”, one of the first things that come to mind is the iconic purple anteater of Radar.app. But more important than the app or even the database itself, Radar is a workflow that guides problems from report to verification across the entire organization.

When a Radar is created, it’s assigned a unique, permanent ID. Radar IDs are auto-incrementing integers, so you can get a general sense of when a bug was filed from the number alone. (At the time of writing, new Radars have 9-digit IDs starting with 4.)

Radar.app uses the rdar:// custom URL scheme. If an Apple employee clicks a rdar://xxxxxxxxx link with an ID, it’ll open directly to that Radar. Otherwise, clicking this link pops up a dialog box with the message “There is no application set to open the URL rdar://xxxxxxxxx.”

Reporting Bugs as External Developers

Unfortunately for all of us not working at Apple, we can’t access Radar directly. Instead, we have to file bugs through systems that interface with it indirectly.

Apple Bug Reporter

Apple Bug Reporter is the primary interface to Radar for external developers. To file a bug, you must be an Apple Developer Program member.

Bug Reporter was recently updated to a modern web app that resembles Mail and other apps on iCloud.com. For anyone who remembers its predecessor, I think you’ll agree that this is a huge improvement.

Choose the product related to the problem you’re reporting and enter a descriptive title. If it’s a bug, specify whether the kind of bug (Performance, Crash/Hang/Data Loss, UI/Usability, etc.) and how often you can produce it. Finally, write a description of the problem, including a summary, steps to reproduce, expected vs. actual results, and information about the configuration and version of your system.

This information is compiled into a Radar that is then triaged, assigned, prioritized, and scheduled by engineers.

Feedback Assistant

If you’re participating in the Apple Beta Software Program, and encounter a problem with your prerelease OS, you can alternatively report it using Feedback Assistant (find it on macOS and iOS with Spotlight).

Feedback Assistant offers a more streamlined experience that’s optimized for providing feedback about the platform you’re living on. It automatically captures a sysdiagnose and other information about your system in order to diagnose the problem you’re encountering more accurately.

Whereas Bug Reporter is your first choice for problems directly related to your work, Feedback Assistant is often a more convenient option for any bumps you encounter in your day-to-day.

Third-Party Bug Reporting Tools

When developers encounter a problem, they’re motivated to do something about it rather than merely complain. This is the reason they file bug reports in the first place.

This is, incidentally, the same motivation that compels us to create tools to fix problems that we find in the bug reporting process itself.

Here are some essential tools for bug reporting from the Apple developer community:

Open Radar

The fundamental problem with Radar as an external developer is lack of transparency. One of the ways this manifests itself is that there’s no way to know what anyone else has reported. All too often, you’ll invest a good deal of time writing up a detailed summary and creating a reproducible test case only to have the bug unceremoniously closed as a duplicate.

OpenRadar, created by Tim Burks, is a public database of bugs reported to Apple. Over the many years of its existence, OpenRadar has become the de facto way for us to coordinate our bug reports.

When you file a Radar with apple, you’re encouraged to also file it with OpenRadar (unless it’s something that shouldn’t be disclosed publicly). Your contribution can help anyone else who might have the same problem in the future.

Brisk

Although the recently-overhauled Bug Reporter web app is quite nice to use, there’s no replacement for a native app.

Brisk, created by Keith Smiley, is a macOS app for filing Radars through Apple’s Bug Reporter. It’s a fully-featured app, with support for two-factor authentication, saving radars as drafts, automatically duping radars by ID, and even opening rdar:// URLs. But it’s killer feature is the ability to cross-post bug reports to Open Radar.

To get started, download the latest release from GitHub or install via Homebrew with the following command:

$ brew cask install Brisk

Advice for Writing a Good Bug Report

So now that you know how to write a bug report let’s talk about how to write a good one.

One Problem, One Bug Report

You won’t be doing anyone any favors by reporting multiple bugs in the same report. Each additional concern you raise makes it both more difficult to understand and less actionable for the assigned engineer.

Instead, file multiple Radars and reference any related problems by ID in your summary.

Choose a Title Strategically

Before an issue can start to be resolved by an engineer, it needs to find its way to them. The best way to ensure things get to the right person is to surface the most important information in the title.

  • For problems about an API, put the fully-qualified symbol name in the title (for example, URLSession.dataTaskWithRequest(_:)).
  • For problems related to documentation, include the full list of navigation breadcrumbs in the title (for example, “Foundation > URL Loading System > URLSession > dataTaskWithRequest:”).
  • For problems with a particular app, reference the app name, version, and build number, which are found in the “About” info box (for example, “Xcode 10.0 beta (10L176w)”).

Don’t Be Antagonistic

Chances are, you’re not at your cheeriest when you’re writing a bug report.

It’s unacceptable that this doesn’t work as expected. You wasted hours trying to debug this problem. Apple doesn’t care about software quality anymore.

That sucks. We get it.

However, none of that is going to solve your problem any faster. If anything, hostility will make an engineer less likely to address your concern.

Remember that there’s a person on the other end of your bug report. Practice empathy.

Peter Steinberger has more great advice on the subject in this blog post.

How to Signal Boost Bug Reports

External developers may often liken the experience of filing a Radar to sending a message into a black hole. With thousands of new Radars coming in every hour, the odds of the right person seeing your bug report in a timely manner seem impossibly small.

Fortunately, there are a few things you can do to help your chances:

Duplicating Existing Radars

In Apple’s bug triage workflow, each problem is (ideally) tracked by a single Radar. If multiple Radars seem to report the same underlying problem, the oldest or most specific one is kept around while the others are closed as duplicates. This resolution can be frustrating for external developers, as this is often the last word they hear about a problem they’re having — particularly if the original Radar isn’t visible to them.

That said, having your bug closed as a duplicate isn’t always a bad thing. You can knowingly file a duplicate of an existing Radar as a way to say “I have this problem, too” and “Please fix this first”. However annoying this might be for the Apple engineer responsible for triage, part of me can’t help projecting a courage to those doomed bug reports, who courageously sacrifice themselves in the name of software quality. Semper fidelis, buggos.

Twitter

Some teams within Apple are quite responsive to feedback on Twitter. It’s hard to stay in-the-loop when you’re in The Loop, so engineers and higher-ups alike often tune in to channel the vox populi.

Due to the chilling nature of Apple’s social media policies, you’re unlikely ever to hear anything back. But rest assured that your Tweets are showing up on a saved Twitter search somewhere in Cupertino.

Blogging

Apple engineers are developers like you or me, and many of them pay attention to what we’re writing about. In addition to being helpful to fellow developers, a simple write-up may be just the thing that convinces that one engineer to take another look.


Speaking from my personal experience working at Apple, Radar is far and away the best bug tracking systems I’ve ever used. So it can be frustrating to be back on the outside looking in, knowing full well what we’re missing out on as external developers.

In contrast to open source software, which empowers anyone to fix whatever bugs they might encounter, the majority of Apple software is proprietary; there’s often very little that we can do. Our only option is to file a bug report and hope for the best.

Fortunately, things have gotten better. The new Bug Reporter site is excellent, and the process itself appears to be moving towards greater transparency:

Encouraging change on @apple’s bug reporter… I’ve been hearing of people now getting notified when an original is “awaiting verification”, and not just “closed”” Dave DeLong (@davedelong) via Twitter

The only way things continue to improve is if we communicate.

So the next time you find something amiss, remember: “file a radar”.

Password Rules / UITextInputPasswordRules

$
0
0

It’s no wonder why hipsters obsess over artisanal this and handcrafted that. Whether it’s a thick slice of avocado toast, a bottle of small batch (nondairy) golden milk, or a perfect cup of pour-over coffee — there’s no replacement for a human touch.

In stark contrast, good passwords are the opposite of artisanal. Unless it’s the title of a hacker movie from the 90’s or the solution to an escape room puzzle, a password should be utterly devoid of meaning.

With Safari in iOS 12 and macOS Mojave, it’ll be easier than ever to generate the strongest, most meaningless, most impossible-to-guess passwords imaginable — all thanks to a few new features.


An ideal password policy is simple: Enforce a minimum number of characters (at least 8) and allow for longer passwords (64 or more).

Anything more elaborate, be it pre-selected security questions, periodic password expiration, or arcane character requirements do little more than annoy the people these policies try to protect.

But don’t take my word for it — I’m not a security expert.

Instead, check out the latest Digital Identity Guidelines from NIST (published June 2017).

The good news is that more companies and organizations are starting to pay attention to security best practices. The bad news is that it took a series of massive data breaches affecting millions of people in order for things to change. And the ugly truth is that because corporations and governments take forever to do anything, many of the aforementioned security anti-patterns aren’t going away anytime soon.

Automatic Strong Passwords

Safari AutoFill has been able to generate passwords since iOS 8, but one of its shortcomings was that it couldn’t guarantee that a generated password satisfied the requirements of a particular service.

Apple aims to solve this problem with a new Automatic Strong Passwords feature in Safari, iOS 12, and macOS Mojave.

WebKit engineer Daniel Bates submitted this proposal for consideration to the WHATWG on March 1st. On June 6th, the WebKit team announced Safari Technology Preview Release 58, with support for strong password generation using the new passwordrules attribute. This announcement coincided with the release iOS 12 beta SDKs at WWDC, which included a new UITextInputPasswordRules API, along with a number of other password management features, including Security Code AutoFill and federated authentication.

Password Rules

Password rules are like a recipe for password generators. By following a few simple rules, the password generator can randomly generate new, secure passwords that comply with the particular requirements of the service provider.

Password rules consist of one or more key-value pairs in the following form:

required: lower; required: upper; required: digit; allowed: ascii-printable; max-consecutive: 3;

Keys

Each rule may specify one of the following keys:

  • required: The kinds of characters that are required
  • allowed: The kinds of characters that are allowed
  • max-consecutive: The maximum number of consecutive characters allowed
  • minlength: The minimum password length
  • maxlength: The maximum password length

The required and allowed keys have one of the character classes listed below as their value. The max-consecutive, minlength, and maxlength keys have a nonnegative integer as their value.

Character Classes

The required and allowed keys may have any one of the following named character classes as their value.

  • upper (A-Z)
  • lower (a-z)
  • digits (0-9)
  • special (-~!@#$%^&\*\_+=`|(){}[:;"'<>,.? ] and space)
  • ascii-printable (U+0020 — 007f)
  • unicode (U+0 — 10FFFF)

In addition to these presets, you may specify a custom character class with ASCII characters surrounded by square brackets (for example, [abc]).


Apple’s Password Rules Validation Tool allows you to experiment with different rules and get real-time feedback of their results. You can even generate and download passwords by the thousands to use during development and testing!

Password Rules Validation Tool

For more information about Password Rules syntax, check out Apple’s “Customizing Password AutoFill Rules”.


Specifying Password Rules

On iOS, you set the passwordRules property of a UITextField with a UITextInputPasswordRules object (you should also set the textContentType to .newPassword while you’re at it):

letnewPasswordTextField=UITextField()newPasswordTextField.textContentType=.newPasswordnewPasswordTextField.passwordRules=UITextInputPasswordRules(descriptor:"required: upper; required: lower; required: digit; max-consecutive: 2; minlength: 8;")

On the web, you set the passwordrules attribute to an <input> element with type="password":

<inputtype="password"passwordrules="required: upper; required: lower; required: special; max-consecutive: 3;"/>

If unspecified, the default password rule is allowed: ascii-printable. Though if your form has a password confirmation field, it will automatically follow the rules from the preceding field.

Generating Password Rules in Swift

If the thought of working with a string-based format without a proper abstraction gives you the heebie-jeebies, you’re not alone.

Here’s one way to encapsulate Password Rules in a Swift API:

enumPasswordRule{enumCharacterClass{caseupper,lower,digits,special,asciiPrintable,unicodecasecustom(Set<Character>)}caserequired(CharacterClass)caseallowed(CharacterClass)casemaxConsecutive(UInt)caseminLength(UInt)casemaxLength(UInt)}extensionPasswordRule:CustomStringConvertible{vardescription:String{switchself{case.required(letcharacterClass):return"required: \(characterClass)"case.allowed(letcharacterClass):return"allowed: \(characterClass)"case.maxConsecutive(letlength):return"max-consecutive: \(length)"case.minLength(letlength):return"minlength: \(length)"case.maxLength(letlength):return"maxlength: \(length)"}}}extensionPasswordRule.CharacterClass:CustomStringConvertible{vardescription:String{switchself{case.upper:return"upper"case.lower:return"lower"case.digits:return"digits"case.special:return"special"case.asciiPrintable:return"ascii-printable"case.unicode:return"unicode"case.custom(letcharacters):return"["+String(characters)+"]"}}}

With this in place, we can now specify a series of rules in code and use them to generate a string with valid password rules syntax:

letrules:[PasswordRule]=[.required(.upper),.required(.lower),.required(.special),.minLength(20)]letdescriptor=rules.map{"\($0.description);"}.joined(separator:"")// "required: upper; required: lower; required: special; max-consecutive: 3;"

If you feel so inclined, you could even extend UITextInputPasswordRules to provide a convenience initializer that takes an array of PasswordRule values:

extensionUITextInputPasswordRules{convenienceinit(rules:[PasswordRule]){letdescriptor=rules.map{$0.description}.joined(separator:"; ")self.init(descriptor:descriptor)}}

If you’re the sentimental type when it comes to personal credentials, and enjoy name dropping your college or dog or favorite sports team behind the anonymous bullets of password input fields, please consider reforming your ways.

Speaking personally, I can’t imagine going about my day-to-day without my password manager. It’s hard to overstate the peace of mind you get by knowing that any information you ever needed is accessible to you — and only you — whenever you need it.

By taking this step now, you’ll be able to take full advantage of these improvements coming to Safari when iOS 12 and macOS Mojave arrive later this year.


Never

$
0
0

“Never” is a proposition that an event doesn’t occur at any time in the past or future. It’s logical impossibility with a time axis; nothingness stretching out in all directions, forever.

…which is why it’s especially worrisome to encounter this comment in code:

// this will never happen

Every compiler textbook will tell you that a comment like this one can’t and won’t affect the behavior of compiled code. Murphy’s Law says otherwise.

How does Swift keep us safe in the unpredictable chaos that is programming? You’ll never believe the answer: nothing and crashing.


Never was proposed as a replacement for the @noreturn attribute in SE-0102: “Remove @noreturn attribute and introduce an empty Never type”, by Joe Groff.

Prior to Swift 3, functions that stop execution, like fatalError(_:file:line:), abort(), and exit(_:), were annotated with the @noreturn attribute, which told the compiler that there was no return to the caller site.

// Swift < 3.0@noreturnfuncfatalError(_message:()->String=String(),file:StaticString=#file,line:UInt=#line)

After the change, fatalError and its trapping cohorts were declared to return the Never type:

// Swift >= 3.0funcfatalError(_message:@autoclosure()->String=String(),file:StaticString=#file,line:UInt=#line)->Never

For a type to replace the functionality an annotation, it must be pretty complex, right? Nope! Actually, just the opposite — Never is arguably the simplest type in the entire Swift standard library:

enumNever{}

Uninhabited Types

Never is an uninhabited type, which means that it has no values. Or to put it another way, uninhabited types can’t be constructed.

Enumerations with no cases are the most common example of an uninhabited type in Swift. Unlike structures or classes, enumerations don’t receive an initializer. And unlike protocols, enumerations are concrete types that can have properties, methods, generic constraints, and nested types. Because of this, uninhabited enumeration types are used in the Swift standard library to namespace functionality and reason about generics in a clever way.

But Never isn’t like that. It doesn’t have any fancy bells or whistles. It’s special by virtue of it being what it is (or rather, isn’t).

Consider a function declared to return an uninhabited type: Because uninhabited types don’t have any values, the function can’t return normally. Instead, the function must either stop execution or run indefinitely.

Eliminating Impossible States in Generic Types

Sure, this is interesting from a theoretical perspective, but what practical use does Never have for us?

Not much — or at least not before the acceptance SE-0215: Conform Never to Equatable and Hashable.

In his proposal, Matt Diephouse explains the motivation behind conforming this obscure type to Equatable and other protocols this way:

Never is very useful for representing impossible code. Most people are familiar with it as the return type of functions like fatalError, but Never is also useful when working with generic classes. For example, a Result type might use Never for its Value to represent something that always errors or use Never for its Error to represent something that never errors.

Swift doesn’t have a standard Result type, but most of them look something like this:

enumResult<Value,Error:Swift.Error>{casesuccess(Value)casefailure(Error)}

Result types are used to encapsulate values and errors produced by functions that execute asynchronously (whereas synchronous functions can use throws to communicate errors).

For example, a function that makes an asynchronous HTTP request might use a Result type to store either a response or an error:

funcfetch(_request:Request,completion:(Result<Response,Error>)->Void){// ...}

When calling that method, you’d switch over result to handle .success and .failure separately:

fetch(request){resultinswitchresult{case.success(letvalue):print("Success: \(value)")case.failure(leterror):print("Failure: \(error)")}}

Now consider a function that’s guaranteed to always return a successful result in its completion handler:

funcalwaysSucceeds(_completion:(Result<String,Never>)->Void){completion(.success("yes!"))}

By specifying Never as the result’s Error type, we’re using the type system to signal that failure is not an option. What’s really cool about this is that Swift is smart enough to know that you don’ need to handle .failure for the switch statement to be exhaustive:

alwaysSucceeds{(result)inswitchresult{case.success(letstring):print(string)}}

You can see this effect played out to its logical extreme in the implementation conforming Never to Comparable:

extensionNever:Comparable{publicstaticfunc<(lhs:Never,rhs:Never)->Bool{switch(lhs,rhs){}}}

Because Never is an uninhabited type, there aren’t any possible values of it. So when we switch over lhs and rhs, Swift understands that there aren’t any missing cases. And since all cases (of which there aren’t any) return Bool, the method compiles without a problem.

Neat!


Never as a Bottom Type

As a corollary, the original Swift Evolution proposal for Never hints at the theoretical usefulness of the type with further enhancement:

An uninhabited type can be seen as a subtype of any other type — if evaluating an expression never produces a value, it doesn’t matter what the type of that expression is. If this were supported by the compiler, it would enable some potentially useful things…

Unwrap or Die

The forced unwrap operator (!) is one of the most controversial parts of Swift. At best, it’s a necessary evil. At worst, it’s a code smell that suggests sloppiness. And without additional information, it can be really tough to tell the difference between the two.

For example, consider the following code that assumes array to not be empty:

letarray:[Int]letfirstIem=array.first!

To avoid force-unwrapping, you could use a guard statement with conditional assignment instead:

letarray:[Int]guardletfirstItem=array.firstelse{fatalError("array cannot be empty")}

In the future, if Never is implemented as a bottom type, it could be used in the right-hand side of nil-coalescing operator expression.

// Future Swift? 🔮letfirstItem=array.first??fatalError("array cannot be empty")

If you’re really motivated to adopt this pattern today, you can manually overload the ?? operator thusly (however…):

func??<T>(lhs:T?,rhs:@autoclosure()->Never)->T{switchlhs{caseletvalue?:returnvaluecasenil:rhs()}}

In the rationale for SE-0217: Introducing the !! “Unwrap or Die” operator to the Swift Standard Library, Joe Groff notes that “[…] We found that overloading [?? for Never] had unacceptable impact on type-checking performance…”. Therefore, it’s recommended that you don’t add this to your codebase.

Expressive Throw

Similarly, if throw is changed from being a statement to an expression that returns Never, you could use throw on the right-hand side of ??:

// Future Swift? 🔮letfirstItem=array.first??throwError.empty

Typed Throws

Looking even further down the road: If the throws keyword in function declarations added support for type constraints, then the Never type could be used to indicate that a function won’t throw (similar to the Result example before):

// Future Swift? 🔮funcneverThrows()throws<Never>{// ...}neverThrows()// `try` unnecessary because it's guaranteed to succeed (perhaps)

Making a claim that something will never be the case can feel like an invitation for the universe to prove otherwise. Whereas modal or doxastic logics allow for face-saving compromise (“it was true at the time, or so I believed!”), temporal logic seems to hold propositions to a higher standard.

Fortunately for us, Swift lives up to this higher standard thanks to the unlikeliest of types, Never.

NLLanguageRecognizer

$
0
0

One of my favorite activities, when I travel, is to listen to people as they pass and try to guess what language they’re speaking. I’d like to think that I’ve gotten pretty good at it over the years (though I rarely get to know if I guessed right).

If I’m lucky, I’ll recognize a word or phrase as a cognate of a language I’m familiar with, and narrow things down from there. Otherwise, I try to build up a phonetic inventory, listening for what kinds of sounds are present. For instance, is the speaker mostly using voiced alveolar trills ⟨r⟩, flaps ⟨ɾ⟩, or postalveolar approximants ⟨ɹ⟩? Are the vowels mostly open / close; front / back? Any unusual sounds, like ⟨ʇ⟩?

…or at least that’s what I think I do. To be honest, all of this happens unconsciously and automatically – for all of us, and for all manner of language recognition tasks. And have only the faintest idea of how we get from input to output.

Computers operate in a similar manner. After many hours of training, machine learning models can predict the language of text with accuracy far exceeding previous attempts from a formalized top-down approach.

Machine learning has been at the heart of natural language processing in Apple platforms for many years, but it’s only recently that external developers have been able to harness it directly.


New in iOS 12 and macOS 10.14, the Natural Language framework refines existing linguistic APIs and exposes new functionality to developers.

NLTagger is NSLinguisticTagger with a new attitude. NLTokenizer is a replacement for enumerateSubstrings(in:options:using:) (neé CFStringTokenizer). NLLanguageRecognizer offers an extension of the functionality previously exposted through the dominantLanguage in NSLinguisticTagger, with the ability to provide hints and get additional predictions.

Recognizing the Language of Natural Language Text

Here’s how to use NLLanguageRecognizer to guess the dominant language of natural language text:

importNaturalLanguageletstring="""私はガラスを食べられます。それは私を傷つけません。
          """letrecognizer=NLLanguageRecognizer()recognizer.processString(string)recognizer.dominantLanguage// ja

First, create an instance of NLLanguageRecognizer and call the method processString(_:) passing a string. From there, the dominantLanguage property returns an NLLanguage object containing the BCP-47 language tag of the predicted language (for example "ja" for 日本語 / Japanese).

Getting Multiple Language Hypotheses

If you studied linguistics in college or joined the Latin club in high school, you may be familiar with some fun examples of polylingual homonymy between dialectic Latin and modern Italian.

For example, consider the readings of the following sentence:

CANE NERO MAGNA BELLA PERSICA!

LanguageTranslation
LatinSing, o Nero, the great Persian wars!
ItalianThe black dog eats a nice peach!

To the chagrin of Max Fisher, Latin isn’t one of the languages supported by NLLanguageRecognizer, so any examples of confusable languages won’t be nearly as entertaining.

With some experimentation, you’ll find that it’s quite difficult to get NLLanguageRecognizer to guess incorrectly, or even with low precision. Beyond giving it a single cognate shared across members of a language family, it’s often able to get past 2σ to 95% certainty with a handful of words.

After some trial and error, we were finally able to get NLLanguageRecognizer to guess incorrectly for a string of non-trivial length by passing the Article I of the Universal Declaration of Human Rights in Norsk, Bokmål:

letstring="""
          Alle mennesker er født frie og med samme menneskeverd og menneskerettigheter.
          De er utstyrt med fornuft og samvittighet og bør handle mot hverandre i brorskapets ånd.
          """letlanguageRecognizer=NLLanguageRecognizer()languageRecognizer.processString(string)recognizer.dominantLanguage// da (!)

The Universal Declaration of Human Rights, is the among the most widely-translated documents in the world, with translations in over 500 different languages. For this reason, it’s often used for natural language tasks.

Danish and Norwegian Bokmål are very similar languages to begin with, so it’s unsurprising that NLLanguageRecognizer guessed incorrectly. (For comparison, here’s the equivalent text in Danish)

We can use the languageHypotheses(withMaximum:) method to get a sense of how confident the dominantLanguage guess was:

languageRecognizer.languageHypotheses(withMaximum:2)
LanguageConfidence
Danish (da)56%
Norwegian Bokmål (nb)43%

At the time of writing, the languageHints property is undocumented, so it’s unclear how exactly it should be used. However, passing a weighted dictionary of probabilities seems to have the desired effect of bolstering the hypotheses with known priors:

languageRecognizer.languageHints=[.danish:0.25,.norwegian:0.75]
LanguageConfidence (with Hints)
Danish (da)30%
Norwegian Bokmål (nb)70%


So what can you do once you know the language of a string?

Here are a couple of use cases for your consideration:

Checking Misspelled Words

Combine NLLanguageRecognizer with UITextChecker to check the spelling of words in any string:

Start by creating an NLLanguageRecognizer and initializing it with a string by calling the processString(_:) method:

letstring="""
          Wenn ist das Nunstück git und Slotermeyer?
          Ja! Beiherhund das Oder die Flipperwaldt gersput!
          """letlanguageRecognizer=NLLanguageRecognizer()languageRecognizer.processString(string)letdominantLanguage=languageRecognizer.dominantLanguage!// de

Then, pass the rawValue of the NLLanguage object returned by the dominantLanguage property to the language parameter of rangeOfMisspelledWord(in:range:startingAt:wrap:language:):

lettextChecker=UITextChecker()letnsString=NSString(string:string)letstringRange=NSRange(location:0,length:nsString.length)varoffset=0repeat{letwordRange=textChecker.rangeOfMisspelledWord(in:string,range:stringRange,startingAt:offset,wrap:false,language:dominantLanguage.rawValue)guardwordRange.location!=NSNotFoundelse{break}print(nsString.substring(with:wordRange))offset=wordRange.upperBound}whiletrue

When passed the The Funniest Joke in the World, the following words are called out for being misspelled:

  • Nunstück
  • Slotermeyer
  • Beiherhund
  • Flipperwaldt
  • gersput

Synthesizing Speech

You can use NLLanguageRecognizer in concert with AVSpeechSynthesizer to hear any natural language text read aloud:

letstring="""
          Je m'baladais sur l'avenue le cœur ouvert à l'inconnu
          J'avais envie de dire bonjour à n'importe qui.
          N'importe qui et ce fut toi, je t'ai dit n'importe quoi
          Il suffisait de te parler, pour t'apprivoiser.
          """letlanguageRecognizer=NLLanguageRecognizer()languageRecognizer.processString(string)letlanguage=languageRecognizer.dominantLanguage!.rawValue// frletspeechSynthesizer=AVSpeechSynthesizer()letutterance=AVSpeechUtterance(string:string)utterance.voice=AVSpeechSynthesisVoice(language:language)speechSynthesizer.speak(utterance)

It doesn’t have the lyrical finesse of Joe Dassin, but ainsi va la vie.


In order to be understood, we first must seek to understand. And the first step to understanding natural language is to determine its language.

NLLanguageRecognizer offers a powerful new interface to functionality that’s been responsible for intelligent features throughout iOS and macOS. See how you might take advantage of it in your app to gain new understanding of your users.

Hashable / Hasher

$
0
0

When you make a Genius Bar reservation at an Apple Store, you’re instructed to show up at a particular time of day and check in with the concierge. After directing you to pull up a stool, the concierge adds you to the queue and makes a note about how to identify you.

According to anonymous reports from former retail employees, there are strict guidelines about how customers can be described. Nothing about their physical appearance is used: age, gender, ethnicity, height — not even hair color. Instead, all customers are described by their clothing, as in “Person with black turtleneck, jeans, and glasses”.

This practice of describing customers has a lot in common with a hashing function in programming. Like any good hashing function, it’s consistent and easy to compute, and can be used to quickly find what (or who) you’re looking for. Much better than a queue, I think you’ll agree!

Our topic this week is Hashable and its new related type, Hasher. Together, they comprise the functionality underlying two of Swift’s most beloved collection classes: Dictionary and Set


Let’s say you have a list of objects that can be compared for equality with one another. To find a particular object in that list, you iterate all the elements until you find a match As you add more elements to the array, the average amount of time necessary to find any one of them increases linearly (O(n)).

If you instead store those objects in a set, you can theoretically find any one of them in constant time (O(1)) — that is, a lookup on a set with 10 elements takes the same amount of time as a lookup on a set with 10,000*. How does this work? Instead of storing objects sequentially, a set computes a hash as an index based on the contents of the object. When you perform a lookup of an object in a set, you use the same function to compute a new hash and look for the object there.

* Two objects produce a hash collision when they have the same hash value but aren’t equal. When a collision occurs on insertion, they’re stored in a list at that address. The higher the rate of collision between objects, the more linear the performance of a hash collection becomes.

Hashable

In Swift, Array provides the standard interface for lists and Set for sets. In order for an object to be stored in a Set, its type must conform to Hashable (and by extension, Equatable). Swift’s standard map interface, Dictionary has a similar constraint on its associated Key type.

In previous versions of the language, it took quite a bit of boilerplate code to satisfy the requirements for storing a custom type in a Set or Dictionary.

Consider the following Color type, which represents a color using 8-bit values for red, green, and blue intensity:

structColor{letred:UInt8letgreen:UInt8letblue:UInt8}

To conform to Equatable, had to provide an implementation for the == operator. To conform to Hashable, had to provide an implementation of the computed hashValue property:

// Swift < 4.1extensionColor:Equatable{staticfunc==(lhs:Color,rhs:Color)->Bool{returnlhs.red==rhs.red&&lhs.green==rhs.green&&lhs.blue==rhs.blue}}extensionColor:Hashable{varhashValue:Int{returnself.red.hashValue^self.green.hashValue^self.blue.hashValue}}

For most developers, implementing Hashable was a speed bump on the way to getting real work done, so they’d simply XOR over all the stored properties and call it a day.

One downside to this approach is its high rate of hash collisions. Because XOR is commutative, colors as different as cyan and yellow produce a hash collision:

// Swift < 4.2letcyan=Color(red:0x00,green:0xFF,blue:0xFF)letyellow=Color(red:0xFF,green:0xFF,blue:0x00)cyan.hashValue==yellow.hashValue// false, no collision

Most of the time, this isn’t a problem; modern computers are so powerful that you have to get a lot of implementation details wrong in order to notice any decrease in performance.

But that’s not to say that details don’t matter — they often matter immensely. More on that later.

Automatic Synthesis of Hashable Conformance

As of Swift 4.1, the compiler automatically synthesizes conformance to the Equatable and Hashable protocols for types that adopt these protocols in their declaration if their members also conform to those protocols.

In addition to being a huge boost to developer productivity, this can drastically reduce the size of a codebase. For instance, our Color example from before — now ⅓ of its original size:

// Swift >= 4.1structColor:Hashable{letred:UInt8letgreen:UInt8letblue:UInt8}

Despite these unambiguous improvements to the language, there was still a lingering question about some of the implementation details.

In his Swift Evolution proposal SE-0185: Synthesizing Equatable and Hashable conformance, Tony Allevato offered this note about hashing functions:

The choice of hash function is left as an implementation detail, not a fixed part of the design; as such, users should not depend on specific characteristics of its behavior. The most likely implementation would call the standard library’s _mixInt function on each member’s hash value and then combine them with exclusive-or (^), which mirrors the way Collection types are hashed today.

Fortunately, it didn’t take long for Swift to settle on a hash function. We got our answer in the very next release:

Hasher

Swift 4.2 refines Hashable even further by introducing the Hasher type and adopting a new universal hashing function.

From the Swift Evolution proposal, SE-0206: Hashable Enhancements:

With a good hash function, simple lookups, insertions and removals take constant time on average. However, when the hash function isn’t carefully chosen to suit the data, the expected time of such operations can become proportional to the number of elements stored in the table.

As Karoy Lorentey and Vincent Esche note, the main draw of hash-based collections like Set and Dictionary is their ability to look up values in constant time. If the hash function doesn’t produce an even distribution of values, these collections effectively become linked lists.

Swift 4.2 implements hashing based on the SipHash family of pseudorandom functions, specifically SipHash-1-3 and SipHash-2-4, with 1 or 2 rounds of hashing per message block and 3 or 4 rounds of finalization, respectively.

Now if you want to customize how your type implements Hashable, you can override the hash(into:) method instead of hashValue. The hash(into:) method passes a Hasher object by reference, which you call combine(_:) on to add the essential state information of your type.

// Swift >= 4.2structColor:Hashable{letred:UInt8letgreen:UInt8letblue:UInt8// Synthesized by compilerfunchash(intohasher:inoutHasher){hasher.combine(self.red)hasher.combine(self.green)hasher.combine(self.blue)}// Default implementation from protocol extensionvarhashValue:Int{varhasher=Hasher()self.hash(into:&hasher)returnhasher.finalize()}}

By abstracting away low-level bit manipulation details, developers automatically take advantage of Swift’s built-in hashing function, which has the extra benefit of not reproducing the collisions we had with our original XOR-based implementation:

// Swift >= 4.2letcyan=Color(red:0x00,green:0xFF,blue:0xFF)letyellow=Color(red:0xFF,green:0xFF,blue:0x00)cyan.hashValue==yellow.hashValue// false, no collision

Customizing Hash Function

By default, Swift uses a universal hash function that reduces a sequence of bytes to a single integer.

However, you can improve on this by tailoring your hash function to your domain. For example, if you were writing a program to play a board game like chess or go, you might implement Zobrist hashing to quickly store the game state.

Guarding Against Hash-Flooding

Selecting a cryptographic algorithm like SipHash helps protect against hash-flooding DoS attacks, which deliberately try to generate hash collisions in an attempt to enforce the worst case of hashing data structures and cause a program to slow to a halt. This caused a bunch of problems for the web in the early 2010’s.

To make things even safer, Hasher generates random seed values each time an app is launched, to make hash values even less predictable.

You shouldn’t rely on specific hash values or save them across executions. On the rare occasion that you would need deterministic behavior, you can set the flag SWIFT_DETERMINISTIC_HASHING to disable random hash seeds,. You shouldn’t depend on consistent values, but you may want this to be deterministic for testing purposes.


The challenge of programming analogies is they normalize antisocial behavior by way of edge cases.

We excel as software engineers when they can think through all the ways that an attacker might leverage a particular behavior to some sinister end — as in the case of hash-flooding DoS attacks. But by doing so, we risk failing as humans when we apply that knowledge AFK.

That is to say… I’m not in any way encouraging you, dear reader, to coordinate outfits with your besties the next time you visit your local Apple retailer in an attempt to sow confusion and discord among Geniuses.

Please don’t.

Instead, please let your takeaway be this:

If you’re waiting at the Genius Bar, stay away from anyone wearing the same color shirt as you. It’ll make things a lot easier for everyone.

Swift Property Observers

$
0
0

By the 1930’s, Rube Goldberg had become a household name, synonymous with the fantastically complicated and whimsical inventions depicted in comic strips like “Self-Operating Napkin.” Around the same time, Albert Einstein popularized the phrase “spooky action at a distance” in his critique of the prevailing interpretation of quantum mechanics by Niels Bohr.

Nearly a century later, modern software development has become what might be seen as the quintessence of a Goldbergian contraption — sprawling ever closer into that spooky realm by way of quantum computers.

As software developers, we’re encouraged to reduce action-at-a-distance in our code whenever possible. This is codified in impressive-sounding guidelines like the Single Responsibility Principle, Principle of Least Astonishment, and Law of Demeter. Yet despite their misgivings about code that produces side effects, there are sometimes occasions where such techniques may clarify rather than confound.

Such is the focus of this week’s article about property observers in Swift, which offer a built-in, lightweight alternative to more formalized solutions like model-view-viewmodel (MVVM) functional reactive programming (FRP).


There are two kinds of properties in Swift: stored properties, which associate state with an object, and computed properties, which perform a calculation based on that state. For example,

structS{// Stored Propertyvarstored:String="stored"// Computed Propertyvarcomputed:String{return"computed"}}

When you declare a stored property, you have the option to define property observers with blocks of code to be executed when a property is set. The willSet observer runs before the new value is stored and the didSet observer runs after. And they run regardless of whether the old value is equal to the new value.

structS{varstored:String{willSet{print("willSet was called")print("stored is now equal to \(self.stored)")print("stored will be set to \(newValue)")}didSet{print("stored is now equal to \(self.stored)")print("stored was previously set to \(oldValue)")}}}

For example, running the following code prints the resulting text to the console:

vars=S(stored:"first")s.stored="second"
  • willSet was called
  • stored is now equal to first
  • stored will be set to second
  • didSet was called
  • stored is now equal to second
  • stored was previously set to first

An important caveat is that observers don’t run when you set a property in an initializer. As of Swift 4.2, you can work around that by wrapping the setter call in a defer block, but that’s a bug that will soon be fixed, so you shouldn’t depend on this behavior.


Swift property observers have been part of the language from the very beginning. To better understand why, let’s take a quick look at how things work in Objective-C:

Properties in Objective-C

In Objective-C, all properties are, in a sense, computed. Each time a property is accessed through dot notation, the call is translated into an equivalent getter or setter method invocation. This, in turn, is compiled into a message send that executes a function that reads or writes an instance variable.

// Dot accessorperson.name=@"Johnny";// ...is equivalent to[personsetName:@"Johnny"];// ...which gets compiled toobjc_msgSend(person,@selector(setName:),@"Johnny");// ...whose synthesized implementation yieldsperson->_name=@"Johnny";

Side effects are something you generally want to avoid in programming because they make it difficult to reason about program behavior. But many Objective-C developers had come to rely on the ability to inject additional behavior into getter or setter methods as needed.

Swift’s design for properties formalized these patterns and created a distinction between side effects that decorate state access (stored properties) and those that redirect state access (computed properties). For stored properties, the willSet and didSet observers replace the code that you’d otherwise include alongside ivar access. For computed properties, the get and set accessors replace code that you might implement for @dynamic properties in Objective-C.

As a result, we get more consistent semantics and better guarantees about mechanisms like Key-Value Observing (KVO) and Key-Value Coding (KVC) that interact with properties.


So what can you do with property observers in Swift? Here are a couple ideas for your consideration:


Validating / Normalizing Values

Sometimes you want to impose additional constraints on what values are acceptable for a type.

For example, if you were developing an app that interfaced with a government bureaucracy, you’d need to ensure that the user wouldn’t be able to submit a form if it was missing a required field, or contained an invalid value.

If, say, a form required that names use capital letters without accents, you could use the didSet property observer to automatically strip diacritics and uppercase the new value:

varname:String?{didSet{self.name=self.name?.applyingTransform(.stripDiacritics,reverse:false)?.uppercased()}}

Setting a property in the body of an observer (fortunately) doesn’t trigger additional callbacks, so we don’t create an infinite loop here. This is the same reason why this won’t work as a willSet observer; any value set in the callback is immediately overwritten when the property is set to its newValue.

While this approach can work for one-off problems, repeat use like this is a strong indicator of business logic that could be formalized in a type.

A better design would be to create a NormalizedText type that encapsulates the requirements of text to be entered in such a form:

structNormalizedText{enumError:Swift.Error{caseemptycaseexcessiveLengthcaseunsupportedCharacters}staticletmaximumLength=32varvalue:Stringinit(_string:String)throws{ifstring.isEmpty{throwError.empty}guardletvalue=string.applyingTransform(.stripDiacritics,reverse:false)?.uppercased(),value.canBeConverted(to:.ascii)else{throwError.unsupportedCharacters}guardvalue.count<NormalizedText.maximumLengthelse{throwError.excessiveLength}self.value=value}}

A failable or throwing initializer can surface errors to the caller in a way that a didSet observer can’t. Now, when a troublemaker like Jøhnny from Llanfair­pwllgwyngyll­gogery­chwyrn­drobwll­llan­tysilio­gogo­goch comes a’knocking, we can give him what’s for! (Which is to say, communicate errors to him in a reasonable manner rather than failing silently or allowing invalid data)

Propagating Dependent State

Another potential use case for property observers is propagating state to dependent components in a view controller.

Consider the following example of a Track model and a TrackViewController that presents it:

structTrack{vartitle:StringvaraudioURL:URL}classTrackViewController:UIViewController{varplayer:AVPlayer?vartrack:Track?{willSet{self.player?.pause()}didSet{self.title=self.track.titleletitem=AVPlayerItem(url:self.track.audioURL)self.player=AVPlayer(playerItem:item)self.player?.play()}}}

When the track property of the view controller is set, the following happens automatically:

  1. Any previous track’s audio is paused
  2. The title of the view controller is set to the new track title
  3. The new track’s audio is loaded and played

Pretty cool, right?

You could even cascade this behavior across multiple observed properties a la that one scene from Mousehunt.


As a general rule, side effects are something to avoid when programming, because they make it difficult to reason about complex behavior. Keep that in mind the next time you reach for this new tool.

And yet, from the tippy top of this teetering tower of abstraction, it can be tempting — and perhaps sometimes rewarding — to embrace the chaos of the system. Always following the rules is such a Bohr.

NSDataAsset

$
0
0

On the web, speed isn’t a luxury; it’s a matter of survival.

User studies in recent years suggest that any perceivable latency in page load time — that is, greater than 400 milliseconds (literally in the blink of an eye) — can negatively impact conversion and engagement rates. For every additional second that a webpage takes to load, one should expect 10% of users to swipe back or close the tab.

For large internet companies like Google, Amazon, and Netflix, an extra second here and there can mean billions of dollars in annual revenue. So it’s no surprise those very same companies have committed so much engineering effort into making the web fast.

There are many techniques for speeding up a network request: compressing and streaming, caching and prefetching, connection pooling and multiplexing, deferring and backgrounding. And yet there’s one optimization strategy that both predates and outperforms them all: not making the request in the first place.

Apps, by virtue of being downloaded ahead of time, have a unique advantage over conventional web pages in this respect. This week on NSHipster, we’ll show how to leverage the Asset Catalog in an unconventional way to improve the first launch experience for your app.


Asset Catalogs allow you to organize resources according to the characteristics of the current device. For a given image, you can provide different files depending on the device (iPhone, iPad, Apple Watch, Apple TV, Mac), screen resolution (@2x / @3x), or color gamut (sRGB / P3). For other kinds of assets, you might offer variations depending on the available memory or version of Metal. Just request an asset by name, and the most appropriate one is provided automatically.

Beyond offering a more convenient API, Asset Catalogs let apps take advantage of app thinning, resulting in smaller installations that are optimized for each user’s device.

Images are far and away the most common types of assets, but as of iOS 9 and macOS El Capitan, resources like JSON, XML and other data file can join in the fun by way of NSDataAsset.

How to Store and Retrieve Data with Asset Catalog

As an example, let’s imagine an iOS app for creating digital color palettes.

To distinguish between different shades of gray, we might load a list of colors and their corresponding names. Normally, we might download this from a server on first launch, but that could cause a bad user experience if adverse networking conditions block app functionality. Since this is a relatively static data set, why not include the data in the app bundle itself by way of an Asset Catalog?

Step 1. Add New Data Set to Asset Catalog

When you create a new app project in Xcode, it automatically generates an Asset Catalog. Select Assets.xcassets from the project navigator to open the Asset Catalog editor. Click the + icon at the bottom left and select “New Data Set”

Doing this creates a new subdirectory of Assets.xcassets with the .dataset extension.

By default, the Finder treats both of these bundles as directories, which makes it easy to inspect and modify their contents as needed.

Step 2. Add a Data File

Open the Finder, navigate to the data file and drag-and-drop it into the empty field for your data set asset in Xcode.

When you do this, Xcode copies the file to the the .dataset subdirectory and updates the contents.json metadata file with the filename and Universal Type Identifier. of the file.

{"info":{"version":1,"author":"xcode"},"data":[{"idiom":"universal","filename":"colors.json","universal-type-identifier":"public.json"}]}

Step 3. Access Data Using NSDataAsset

Now you can access the file’s data with the following code:

guardletasset=NSDataAsset(name:"NamedColors")else{fatalError("Missing data asset: NamedColors")}letdata=asset.data

In the case of our color app, we might call this from the viewDidLoad() method in a view controller and use the resulting data to decode an array of model objects to be displayed in a table view:

letdecoder=JSONDecoder()self.colors=try!decoder.decode([NamedColor].self,from:asset.data)

Mixing It Up

Data sets don’t typically benefit from app thinning features of Asset Catalogs (most JSON files, for example, couldn’t care less about what version of Metal is supported by the device).

But for our color palette app example, we might provide different color lists on devices with a wide-gamut display.

To do this, select the asset in the sidebar of the Asset Catalog editor and click on the drop-down control labeled Gamut in the Attributes Inspector.

After providing bespoke data files for each gamut, the contents.json metadata file should look something like this:

{"info":{"version":1,"author":"xcode"},"data":[{"idiom":"universal","filename":"colors-srgb.json","universal-type-identifier":"public.json","display-gamut":"sRGB"},{"idiom":"universal","filename":"colors-p3.json","universal-type-identifier":"public.json","display-gamut":"display-P3"}]}

Keeping It Fresh

Storing and retrieving data from the Asset Catalog is trivial. What’s actually difficult — and ultimately more important — is keeping that data up-to-date.

Refresh data using curl, rsync, sftp, Dropbox, BitTorrent, or Filecoin. Kick things off from a shell script (and call it in an Xcode Build Phase, if you like). Add it to your Makefile, Rakefile, Fastfile, or whatever is required of your build system of choice. Delegate the task to Jenkins or Travis or that bored-looking intern. Trigger it from a bespoke Slack integration or create a Siri Shortcut so you can wow your colleagues with a casual “Hey Siri, update that data asset before it gets too stale”.

However you decide to synchronize your data, just make sure it’s automated and part of your release process.

Here’s an example of a shell script you might run to download the latest data file using curl:

#!/bin/shCURL='/usr/bin/curl'URL='https://example.com/path/to/data.json'OUTPUT='./Assets.xcassets/Colors.dataset/data.json'$CURL-fsSL-o$OUTPUT$URL

Wrapping It Up

Although the Assets Catalog performs lossless compression of image assets, nothing from the documentation, Xcode Help, or WWDC sessions
indicate that any such optimization is done for data assets (at least not yet).

For data assets larger than, say, a few hundred kilobytes, you should consider using compression. This is especially true for text files like JSON, CSV, and XML, which typically compress down to 60% — 80% of their original size.

We can add compression to our previous shell script by piping the output of curl to gzip before writing to our file:

#!/bin/shCURL='/usr/bin/curl'GZIP='/usr/bin/gzip'URL='https://example.com/path/to/data.json'OUTPUT='./Assets.xcassets/Colors.dataset/data.json.gz'$CURL-fsSL$URL | $GZIP-c>$OUTPUT

If you do adopt compression, make sure that the "universal-type-identifier" field reflects this:

{"info":{"version":1,"author":"xcode"},"data":[{"idiom":"universal","filename":"colors.json.gz","universal-type-identifier":"org.gnu.gnu-zip-archive"}]}

On the client-side, it’s up to you to decompress data from the asset catalog before use. If you had a Gzip module, you might do the following:

do{letdata=tryGzip.decompress(data:asset.data)}catch{fatalError(error.localizedDescription)}

Or, if you do this multiple times in your app, you could create a convenience method in an extension to NSDataAsset:

extensionNSDataAsset{funcdecompressedData()throws->Data{returntryGzip.decompress(data:self.data)}}

You might also consider managing large data asset files in version control using Git Large File Storage (LFS).


Although it’s tempting to assume that all of your users enjoy fast, ubiquitous network access over WiFi and LTE, this isn’t true for everyone, and certainly not all the time.

Take a moment to see what networking calls your app makes at launch, and consider if any of these might benefit from being pre-loaded. Making a good first impression could mean the difference between a long-term active use and deletion after a few seconds.

CMMotionActivity

$
0
0

Humans perceive self-motion using a combination of sensory information from their visual, proprioceptive, and vestibular systems. Of these, the primary determination is made by the vestibular system, comprising the semicircular canals, which sense changes in rotation, and the otoliths, which are sensitive to horizontal and vertical forces.

Today’s iPhones are packed with a full complement of sensors that includes cameras, barometers, gyroscopes, magnetometers, and accelerometers. Like humans, they use permutations of different sensory information to make determinations about their position and orientation, often by means quite similar to our own biomechanical processes.

Making sense of sensory inputs — no matter their origin — is challenging. There’s just so much information to consider. (Heck, it took our species a few million years to get that right, and we’re still confused by newfangled inventions like elevators, planes, and roller coasters.)

After several major OS releases and hardware versions, Apple devices have become adroit at differentiating between different means of locomotion. But before you run off and try to write your own implementation, stop and consider using the built-in APIs discussed in this week’s article.


On iOS and watchOS, CMMotionActivityManager takes raw sensor data from the device and tells you (to what degree of certainty) whether the user is currently moving, and if they’re walking, running, biking, or driving in an automobile.

To use this API, you create an activity manager and start listening for activity updates using the startActivityUpdates method. Each time the device updates the motion activity, it executes the specified closure, passing a CMMotionActivity object.

letmanager=CMMotionActivityManager()manager.startActivityUpdates(to:.main){(activity)inguardletactivity=activityelse{return}varmodes:Set<String>=[]ifactivity.walking{modes.insert("🚶‍")}ifactivity.running{modes.insert("🏃‍")}ifactivity.cycling{modes.insert("🚴‍")}ifactivity.automotive{modes.insert("🚗")}print(modes.joined(separator:", "))}

CMMotionActivityManager is provided by the Core Motion framework. Devices that support Core Motion are equipped with a motion coprocessor. By using dedicated hardware, the system can offload all sensor processing from the CPU and minimize energy usage.

The first of the M-series coprocessors was the M7, which arrived in September 2013 with the iPhone 5S. This coincided with the release of iOS 7 and the Core Motion APIs.

Feature Drivers

Possibly the most well-known use of motion activities is “Do Not Disturb While Driving”, added in iOS 11. Automotive detection got much better with the introduction of this feature.

At low speeds, it’s hard to distinguish automobile travel from other means using accelerometer data alone. Although we can only speculate as to how this works, it’s possible that the iPhone uses magnetometer data from the device compass. Because cars and other vehicles are often enclosed in metal, electromagnetic flux is reduced.

Beyond safety concerns, some apps might change their behavior according to the current mode of transportation. For example, a delivery service app might relay changes in motion activity to a server to recalculate estimated ETA or change the UI to communicate that the courier has parked their vehicle and are now approaching by foot.

Traveling Without Moving

CMMotionActivity has Boolean properties for each of the different types of motion as well as one for whether the device is stationary. This seems counter-intuitive, as logic dictates that you can either be walking or driving a car at a given moment, but not both.

This point is clarified by the CMMotionActivity documentation:

The motion-related properties of this class are not mutually exclusive. In other words, it is possible for more than one of the motion-related properties to contain the value true. For example, if the user was driving in a car and the car stopped at a red light, the update event associated with that change in motion would have both the cycling and stationary properties set to true.

Wait, did I say clarified? I meant… whatever the opposite is. (I’m pretty sure this should be automotive) Fortunately, the header docs tell us more about what we might expect. Here are some concrete examples of how this API behaves in different situations:

Scenario 1: You’re in a car stopped at a red light

🚶‍ walking🏃‍ running🚴‍cycling🚗 automotive🛑 stationary
falsefalsefalsetruetrue

Scenario 2: You’re in a moving vehicle

🚶‍ walking🏃‍ running🚴‍cycling🚗 automotive🛑 stationary
falsefalsefalsetruefalse

Scenario 3: The device is in motion, but you’re neither walking nor in a moving vehicle

🚶‍ walking🏃‍ running🚴‍cycling🚗 automotive🛑 stationary
falsefalsefalsefalsefalse

Scenario 4: You’re a world-famous detective, who, in the process of chasing a suspect down the corridors of a moving train, has reached the last car and has stopped to look around to surmise where they’re hiding (perhaps that conspicuous, person-sized box in the corner?)

🚶‍ walking🏃‍ running🚴‍cycling🚗 automotive🛑 stationary🕵️‍🇧🇪 poirot
falsetruefalsetruetruetrue

(We’re actually not sure what would happen in that last scenario…)

The overall guidance from the documentation is that you should treat stationary to be orthogonal from all of the other CMMotionActivity properties and that you should be prepared to handle all other combinations.

Each CMMotionActivity object also includes a confidence property with possible values of .low, .medium, and .high. Unfortunately, not much information is provided by the documentation about what any of these values mean, or how they should be used. As is often the case, an empirical approach is recommended; test your app in the field to see when different confidence values are produced and use that information to determine the correct behavior for your app.

Combining with Location Queries

Depending on your use case, it might make sense to coordinate Core Motion readings with Core Location data.

You can combine changes in location over time with low-confidence motion activity readings to increase accuracy. Here are some general guidelines for typical ranges of speeds for each of the modes of transportation:

  • Walking speeds typically peak at 2.5 meters per second (5.6 mph, 9 km/h)
  • Running speeds range from 2.5 to 7.5 meters per second (5.6 – 16.8 mph, 9 – 27 km/h)
  • Cycling speeds range from 3 to 12 meters per second (6.7 – 26.8 mph, 10.8 – 43.2 km/h)
  • Automobile speeds can exceed 100 meters per second (220 mph, 360 km/h)

Alternatively, you might use location data to change the UI depending on whether the current location is in a body of water.

ifcurrentLocation.intersects(waterRegion){ifactivity.walking{print("🏊‍")}elseifactivity.automotive{print("🚢")}}

However, location data should only be consulted if absolutely necessary — and when you do, it should be done as sparingly as possible, such as by monitoring for only significant location changes. Reason being, location data requires turning on the GPS and/or cellular radio, which are both energy intensive.


CMMotionActivityManager is one of many great APIs in Core Motion that you can use to build immersive, responsive apps.

If you haven’t considered the potential of incorporating device motion into your app (or maybe haven’t looked at Core Motion for a while), you might be surprised at what’s possible.

iOS 12

$
0
0

If you tuned in to this year’s WWDC Keynote, you’ll know all about the big features in iOS 12: Siri Shortcuts, ARKit 2, and Core ML 2— not to mentioned the bombshell pre-announcement of the long-rumored iOS / Mac bridge, codenamed “Marzipan”.

And if you watched this year’s Platforms State of the Union session, you’ll be aware of the less glamorous, but equally exciting new technologies, like customizable user notification UI, and the new Network and Natural Language frameworks.

But if you’re looking for this year’s Release Notes… you’re out of luck. At the time of writing, we’re still waiting on those. There’s What’s New in iOS, which offers a nice top-level overview. But here at NSHipster, we’re interested in the nitty-gritty: the small (dare we say, obscure?) changes that add up to make a big impact to our day-to-day.

In celebration of this week’s release of iOS 12, we’re sharing what we found after trawling through the API diffs from iOS 11.4 to 12. (As it were, many of these are still undocumented, so proceed with caution).


Prioritizing Network Traffic for Important Requests

Have you heard of Fast Lane for iOS? No, not that fastlane. No, not that IOS, either.

Fast Lane (or is it Fastlane?) is a mechanism used to prioritize wireless traffic according to its type, such as audio, video, or background data. It’s a technology specific to Cisco routers, (which account for about half of all internet traffic), that encapsulates several Wi-Fi standards like 802.11r for fast roaming, 802.11k for assisted roaming, and 802.11v for wireless configuration.

Thanks to a partnership between Apple and Cisco announced in 2015, iOS developers can opt-in to this technology by providing a service type (QoS marking) to network connections (though many high-level APIs take care of this for you automatically).

New in iOS 12, URLRequest objects can now set networkServiceType to NSURLNetworkServiceTypeResponsiveData to prioritize time-sensitive requests:

importFoundationleturl=URL(string:"https://example.com/checkout")!varrequest=URLRequest(url:url)request.httpMethod="POST"request.networkServiceType=.responsiveData// PrioritizeURLSession.shared.dataTask(with:request){(data,response,error)in// ...}

This option is currently undocumented, but the guidance from the engineers presenting WWDC 2018 Session 714: “Optimizing Your App for Today’s Internet” is to use this feature judiciously, only when time is of the essence. The example they provide is “the checkout page for a shopping app”, but you can extrapolate other potential use cases.

Reading NFC Tags in the Background

One of the longstanding questions coming out of WWDC 2018 was the new ndefMessagePayload property added to NSUserActivity. At the time, the most that Apple engineers would offer during Lab sessions was “no comment”.

But all became clear with last week’s announcements of the iPhone Xs, iPhone Xs Max and iPhone XR. These devices support reading NFC tags in the background, and if you’re running iOS 12 on the latest generation of devices, you’ll be able to — among other things — launch apps, start calls, and open URLs in response to scanning compatible NFC tags. No additional setup required. To avoid inadvertent activation, this only works if the iPhone is unlocked and not currently in Airplane Mode or being used for Apple Pay or camera.

With this NFC integration, Apple hopes to fully realize past promises made about BLE iBeacons back in 2013, offering a sleeker interface to the real world than the depravity of scanning a QR code (a practice ubiquitous in China, but largely ignored to the rest of the world).

Perhaps the most commonly advertised use cases for both technologies NFC and iBeacon technologies have been visiting a museum and getting additional details about an exhibit by hovering your phone near a strategically-placed information placard.

Enabling this kind of functionality in your app requires entitlements, setting associated domains, and other configuration — not to mention the actual APIs you need to implement. Fortunately, Apple provides some extensive documentation for this process, including this sample code project and this article.

Matching Contacts on Phone Number and Email Address

The Contacts framework was introduced in iOS 9 and macOS El Capitan as a modern replacement for the AddressBook framework.

Until recently, you could only search for contacts by name and identifier. With iOS 12, you can now use the predicateForContacts(matching:), and predicateForContacts(matchingEmailAddress:) class methods on CNContact to construct predicates for matching on phone numbers and email addresses.

For example, if we wanted to retrieve the given and family name components for all contacts with a given phone number and email address, you create a CNContactFetchRequest, specify a compound “AND” predicate created from the individual subpredicates, and pass that to the enumerateContacts(with:) method called on the current CNContactStore:

importContactsletphoneNumber=CNPhoneNumber(stringValue:"+1 555 555 1234")letphoneNumberPredicate=CNContact.predicateForContacts(matching:phoneNumber)letemailPredicate=CNContact.predicateForContacts(matchingEmailAddress:"johnny@example.com")varfetchRequest=CNContactFetchRequest(keysToFetch:[CNContactGivenNameKeyasCNKeyDescriptor,CNContactFamilyNameKeyasCNKeyDescriptor])fetchRequest.predicate=NSCompoundPredicate(andPredicateWithSubpredicates:[phoneNumberPredicate,emailPredicate])letstore=CNContactStore()trystore.enumerateContacts(with:fetchRequest){(contact,_)in// ...}

Updating Location While Airborne

iPads are especially popular among pilots, who use them for navigation and flight planning. If you’re working on an app geared for folks up in the cockpit, you’ll be delighted to hear that CLLocationManager now has something just for you in iOS 12.

The activityType property has been around for a while, but remains a lesser-known configuration option for CLLocationManager. If you use a location manager to track changes in position over time, a quick “low-hanging fruit” optimization is to specify how you expect users to be perambulating. Until now, these modes of transportation have been strictly terrestrial: automotive, walking / running / biking, what have you. But in iOS 12, you can specify the airborne activity type and let your app’s motion tracking algorithms soar!

importCoreLocationletmanager=CLLocationManager()manager.activityType=.airborne// ✈️

Detecting Flat Device Orientation

Have you ever wanted to determine whether an iOS device was laying flat on a surface, but were loath to do two equality checks in the process? Good news! In iOS 12, there’s a new convenience property: isFlat.

importUIKit// iOS 12+UIDevice.current.orientation.isFlat// iOS <= 11.4UIDevice.current.orientation==.faceUp||UIDevice.current.orientation==.faceDown

Auto-filling New Passwords and One-Time Codes in Text Fields

Apple goes to heroic lengths to make user input pleasant on iOS devices. Yet despite their best efforts, the fact remains: the experience of typing on a featureless piece of smooth glass is always going to pale in comparison to a proper hardware keyboard (discontentment about the latest MacBook models notwithstanding).

To minimize the amount of text-entry drudgery, iOS 10 introduced the textContentType property for controls conforming to the UITextInputTraits protocol — namely UITextField and UITextView. By providing one of the enumeration values you declare the semantic value of the control, which allows for details like certain name and address components to be auto-filled based on the current user’s information.

iOS 12 and tvOS 12 expand on this by adding two new content types: UITextContentTypeNewPassword and UITextContentTypeOneTimeCode.

When you specify the .newPassword content type in conjunction with the passwordRules property , Password AutoFill can automatically generate new passwords according to the login requirements of the system.

textField.textContentType=.newPasswordtextField.passwordRules=.init(descriptor:"allowed: ascii-printable; minlength: 8;")

When you specify the .oneTimeCode content type, the text field can automatically forward two-factor authentication codes received via SMS.

textField.textContentType=.oneTimeCode

That wraps up this round of iOS 12 diff spelunking. Of course, this is an enormous release, so we look forward to cover many more new APIs in greater depth in the weeks to come.

Do you have any suggestions about what we should cover next? Please get in touch via Twitter!


UIFieldBehavior

$
0
0

The decade mark for iOS has come and gone. Once a nascent craft, iOS development today has a well-worn, broken-in feel to it.

And yet, when I step outside my comfort zone of table views, labels, buttons, and the like, I often find myself stumbling upon pieces of Cocoa Touch that I’d either overlooked or completely forgotten about. When I do, it’s like picking an old book from a shelf; the anticipation of what might be tucked away in its pages invariably swells up within you.

Recently, UIFieldBehavior has been my dust-covered tome sitting idly inside UIKit. An API built to model complex field physics for UI elements isn’t a typical use case, nor is it likely to be the hot topic among fellow engineers. But, when you need it, you need it, and not much else will do. And as purveyors of the oft-forgotten or seldom used, it serves as an excellent topic for this week’s NSHipster article.


With the design refresh of iOS in its 7th release, skeuomorphic design was famously sunset. In its place, a new paradigm emerged, in which UI controls were made to feel like physical objects rather than simply look like them. New APIs would be needed to usher in this new era of UI design, and so we were introduced to UIKit Dynamics.

Examples of this reach out across the entire OS: the bouncy lock screen, the flickable photos, those oh-so-bubbly message bubbles — these and many other interactions leverage some flavor of UIKit Dynamics (of which there are several).

  • UIAttachmentBehavior: Creates a relationship between two items, or an item and a given anchor point.
  • UICollisionBehavior: Causes one or more objects to bounce off of one another instead of overlapping without interaction.
  • UIFieldBehavior: Enables an area or item to participate in field-based physics.
  • UIGravityBehavior: Applies a gravitational force, or pull.
  • UIPushBehavior: Creates an instantaneous or continuous force.
  • UISnapBehavior: Produces a motion that dampens over time.

For this article, let’s take a look at UIFieldBehavior, which our good friends in Cupertino used to build the PiP functionality seen in FaceTime calls.

FaceTime

Understanding Field Behaviors

Apple mentions that UIFieldBehavior applies “field-based” physics, but what does that mean, exactly? Thankfully, it’s more relatable that one might think.

There are plenty of examples of field-based physics in the real world, whether it’s the pull of a magnet, the *sproing* of a spring, the force of gravity pulling you down to earth. Using UIFieldBehavior, we can designate areas of our view to apply certain physics effects whenever an item enters into them.

Its approachable API design allows us to complex physics without much more than a factory method:

letdrag=UIFieldBehavior.dragField()
UIFieldBehavior*drag=[UIFieldBehaviordragField];

Once we have a field force at our disposal, it’s a matter of placing it on the screen and defining its area of influence.

drag.position=view.centerdrag.region=UIRegion(size:bounds.size)
drag.position=self.view.center;drag.region=[[UIRegionalloc]initWithSize:self.view.bounds.size];

If you need more granular control over a field’s behavior, you can configure its strength and falloff, as well as any additional properties specific to that field type.


All UIKit Dynamics behaviors require some setup to take effect, and UIFieldBehavior is no exception. The flow looks generally something like this:

  • Create an instance of a UIDynamicAnimator to provide the context for any animations affecting its dynamic items.
  • Initialize the desired behaviors to use.
  • Add the views you wish to be involved with each behavior.
  • Add those behaviors to the dynamic animator from step one.
lazyvaranimator:UIDynamicAnimator={returnUIDynamicAnimator(referenceView:view)}()letdrag=UIFieldBehavior.dragField()// viewDidLoad:drag.addItem(anotherView)animator.addBehavior(drag)
@property(strong,nonatomic,nonnull)UIDynamicAnimator*animator;@property(strong,nonatomic,nonnull)UIFieldBehavior*drag;// viewDidLoad:self.animator=[[UIDynamicAnimatoralloc]initWithReferenceView:self.view];self.drag=[UIFieldBehaviordragField];[self.dragaddItem:self.anotherView];[self.animatoraddBehavior:self.drag];

Take care to keep a strong reference to you UIKitDynamicAnimator object. You don’t typically need to do this for behaviors because the animator takes ownership to a behavior once it’s added.

For a bona fide example of UIFieldBehavior, let’s take a look at how FaceTime leverages it to make the small rectangular view of the front-facing camera stick to each corner of the view’s bounds.

Face to Face with Spring Fields

During a FaceTime call, you can flick your picture-in-picture to one of the corners of the screen. How do we get it to move fluidly but still stick?

One approach might entail checking a gesture recognizer’s end state, calculating which corner to settle into, and animating as necessary. The problem here is that we likely would lose the “secret sauce” that Apple painstakingly applies to these little interactions, such as the interpolation and dampening that occurs as the avatar settles into a corner.

This is a textbook situation for UIFieldBehavior’s spring field. If we think about how a literal spring works, it exerts a linear force equal to the amount of strain that’s put on it. So, if we push down on a coiled spring we expect it to snap back into place once we let go.

This is also why spring fields can help contain items within a particular part of your UI. You could think of a boxing ring and how its elastic rope keeps contestants within the ring. With springs, though, the rope would originate from the center of the ring and be pulled back to each edge.

A spring field works a lot like this. Imagine if our view’s bounds were divided into four rectangles, and we had these springs hanging out around the edges of each one. The springs would be “pushed” down from the center of the rectangle to the edge of its corner. When the avatar enters any of the corners, the spring is “let go” and gives us that nice little push that we’re after.

The spring field is created by replicating Hooke’s Law to calculate how much force should be applied to the objects within the field.

To take care of the avatar settling into each corner, we can do something clever like this:

forverticalin[\UIEdgeInsets.left,\UIEdgeInsets.right]{forhorizontalin[\UIEdgeInsets.top,\UIEdgeInsets.bottom]{letspringField=UIFieldBehavior.springField()springField.position=CGPoint(x:layoutMargins[keyPath:horizontal],y:layoutMargins[keyPath:vertical])springField.region=UIRegion(size:view.bounds.size.applying(scale))animator.addBehavior(springField)springField.addItem(facetimeAvatar)}}
UIFieldBehavior*topLeftCornerField=[UIFieldBehaviorspringField];// Top left cornertopLeftCornerField.position=CGPointMake(self.layoutMargins.left,self.layoutMargins.top);topLeftCornerField.region=[[UIRegionalloc]initWithSize:CGSizeMake(self.bounds.size.width/2,self.bounds.size.height/2)];[self.animatoraddBehavior:topLeftCornerField];[self.topLeftCornerFieldaddItem:self.facetimeAvatar];// Continue to create a spring field for each corner...

Debugging Physics

It’s not easy to conceptualize the interactions of invisible field forces. Thankfully, Apple anticipated as much and provides a somewhat out-of-the-box way to solve this problem.

Tucked away inside of UIDynamicAnimator is a Boolean property, debugEnabled. Setting it to true paints the interface with red lines to visualize field-based effects and their influence. This can go quite a long way to help you make sense of how their dynamics are working.

This API isn’t exposed publicly, but you can unlock its potential through a category or using key-value coding:

@importUIKit;#if DEBUG
          @interfaceUIDynamicAnimator(Debugging)@property(nonatomic,getter=isDebugEnabled)BOOLdebugEnabled;@end#endif
          

or

animator.setValue(true,forKey:"debugEnabled")
[self.animatorsetValue:@1forKey:@"debugEnabled"];

Although creating a category involves a bit more legwork, it’s the safer option. The slippery slope of key-value coding can rear its exception-laden head with any iOS release in the future, as the price of convenience is typically anything but free.

With debugging enabled, it appears as though each corner has a spring effect attached to it. Running and using our fledgling app, however, reveals that it’s not enough to complete the effect we’re seeking.

Aggregating Behaviors

Let’s take stock of our current situation to deepen our understanding of field physics. Currently, we’ve got a few issues:

  1. The avatar could fly off the screen with nothing to keep it constrained aside from spring fields
  2. It has a knack for rotating in circles.
  3. Also, it’s a tad slow.

UIKit Dynamics simulates physics — perhaps too well.

Fortunately, we can mitigate all of these undesirable side effects. To wit, they are rather trivial fixes, but it’s the reason why they’re needed that’s key.

The first issue is solved in a rather trivial fashion with what is likely UIKit Dynamics most easily understood behavior: the collision. To better hone in on how the avatar view should react once it’s acted upon by a spring field, we need to describe its physical properties in a more intentional manner. Ideally, we’d want it to behave like it would in real life, with gravity and friction acting to slow down its momentum.

For such occasions, UIDynamicItemBehavior is ideal. It lets us attach physical properties to what would otherwise be mere abstract view instances interacting with a physics engine. Though UIKit does provide default values for each of these properties when interacting with the physics engine, they are likely not tuned to your specific use case. And UIKit Dynamics almost always falls into the “specific use case” bucket.

It’s not hard to foresee how the lack of such an API could quickly turn problematic. If we want to model things like a push, pull or velocity but have no way to specify the object’s mass or density, we’d be omitting a critical piece of the puzzle.

letavatarPhysicalProperties=UIDynamicItemBehavior(items:[facetimeAvatar])avatarPhysicalProperties.allowsRotation=falseavatarPhysicalProperties.resistance=8avatarPhysicalProperties.density=0.02
UIDynamicItemBehavior*avatarPhysicalProperties=[[UIDynamicItemBehavioralloc]initWithItems:@[self.facetimeAvatar]];avatarPhysicalProperties.allowsRotation=NO;avatarPhysicalProperties.resistance=8;avatarPhysicalProperties.density=0.02;

Now the avatar view more closely mirrors real-world physics in that it slows down a tinge after pushed by a spring field. The configurations available from UIDynamicItemBehavior are impressive, as support for elasticity, charge and anchoring are also available to ensure you can continuing tweaking things until they feel right.

Further, it also includes out-of-the-box support for attaching linear or angular velocity to an object. This serves as the perfect bookend to our journey with UIDynamicItemBehavior, as we probably want to give our FaceTime avatar a friendly nudge at the end of the gesture recognizer to send it off to its nearest corner, thus letting the relevant spring field take over:

// Inside a switch for a gesture recognizer...case.canceled,.ended:letvelocity=panGesture.velocity(in:view)facetimeAvatarBehavior.addLinearVelocity(velocity,for:facetimeAvatar)
// Inside a switch for a gesture recognizer...caseUIGestureRecognizerStateCancelled:caseUIGestureRecognizerStateEnded:{CGPointvelocity=[panGesturevelocityInView:self.view];[facetimeAvatarBehavioraddLinearVelocity:velocityforItem:self.facetimeAvatar];break;}

We’re almost finished creating our faux FaceTime UI.

To pull the entire experience together, we need to account for what our FaceTime avatar should do when it reaches the corners of the animator’s view. We want it to stay contained within it, and currently, nothing is keeping it from flying off the screen. UIKit Dynamics offers us such behavior to account for these situations by way of UICollisionBehavior.

Creating a collision follows a similar pattern as with using any other UIKit Dynamics behavior, thanks to consistent API design:

letparentViewBoundsCollision=UICollisionBehavior(items:[facetimeAvatar])parentViewBoundsCollision.translatesReferenceBoundsIntoBoundary=true
UICollisionBehavior*parentViewBoundsCollision=[[UICollisionBehavioralloc]initWithItems:@[self.facetimeAvatar]];parentViewBoundsCollision.translatesReferenceBoundsIntoBoundary=YES;

Take note of translatesReferenceBoundsIntoBoundary. When true, it treats our animator view’s bounds as its collision boundaries. Recall that this was our initial step in setting up our dynamics stack:

lazyvaranimator:UIDynamicAnimator={returnUIDynamicAnimator(referenceView:view)}()
self.animator=[[UIDynamicAnimatoralloc]initWithReferenceView:self.view];

By aggregating several behaviors to work as one, we can now bask in our work:


If you want to stray from the FaceTime “sticky” corners, you are in an ideal position to do so. UIFieldBehavior has many more field physics to offer other than just a spring. You could experiment by replacing it with a magnetism effect, or constantly have the avatar rotate around a given point.


iOS has largely parted ways with skeuomorphism, and user experience has come a long way as a result. We no longer necessarily require green felt to know that Game Center represents games and how we can manage them.

Instead, UIKit Dynamics introduces an entirely new way for users to interact and connect with iOS. Making UI components behave as they do in the real world instead of simply looking like them is a good illustration of how far user experience has evolved since 2007.

Stripping away this layer across the OS opened the door for UIKit Dynamics to connect our expectations of how visual elements should react to our actions. These little connections may seem inconsequential at first glance, but take them away, and you’ll likely start to realize that things would feel “off.”

UIKit Dynamics offers up many flavors of physical behaviors to leverage, and its field behaviors are perhaps some of the most interesting and versatile. The next time you see an opportunity to create a connection in your app, UIFieldBehavior might give you the start you need.

macOS Dynamic Desktop

$
0
0

Dark Mode is one of the most popular additions to macOS — especially among us developer types, who tend towards light-on-dark color themes in text editors and appreciate this new visual consistency across the system.

A couple of years back, there was similar fanfare for Night Shift, which helped to reduce eye strain from hacking late into the night (or early in the morning, as it were).

If you triangulate from those two macOS features, you get Dynamic Desktops, also new in Mojave. Now when you go to “System Preferences > Desktop & Screen Saver”, you have the option to select a “Dynamic” desktop picture that changes throughout the day, based on your location.

The result is subtle and delightful. Having a background that tracks the passage of time makes the desktop feel alive; in tune with the natural world. (If nothing else, it makes for a lovely visual effect when switching dark mode on and off)

But how does it work, exactly?
That’s the question for this week’s NSHipster article.

The answer involves a deep dive into image formats, a little bit of reverse-engineering and even some spherical trigonometry.


The first step to understanding how Dynamic Desktop works is to get hold of a dynamic image.

If you’re running macOS Mojave open Finder, select “Go > Go to Folder…” (G), and enter “/Library/Desktop Pictures/”.

In this directory, you should find a file named “Mojave.heic”. Double-click it to open it in Preview.

In Preview, the sidebar shows a list of thumbnails numbered 1 through 16, each showing a different view of the desert scene.

If we select “Tools > Show Inspector” (I), we get some general information about what we’re looking at:

Unfortunately, that’s about all Preview gives us (at least at the time of writing). If we click on the next panel over, “More Info Inspector”, we don’t learn a whole lot more about our subject:

  
Color ModelRGB
Depth:8
Pixel Height2,880
Pixel Width5,120
Profile NameDisplay P3

If we want to learn more, we’ll need to roll up our sleeves and get our hands dirty with some low-level APIs.

Digging Deeper with CoreGraphics

Let’s start our investigation by creating a new Xcode Playground. For simplicity, we can hard-code a URL to the “Mojave.heic” file on our system.

importFoundationimportCoreGraphics// macOS 10.14 Mojave Requiredleturl=URL(fileURLWithPath:"/Library/Desktop Pictures/Mojave.heic")

Next, create a CGImageSource, copy its metadata, and enumerate over each of its tags:

letsource=CGImageSourceCreateWithURL(urlasCFURL,nil)!letmetadata=CGImageSourceCopyMetadataAtIndex(source,0,nil)!lettags=CGImageMetadataCopyTags(metadata)as![CGImageMetadataTag]fortagintags{guardletname=CGImageMetadataTagCopyName(tag),letvalue=CGImageMetadataTagCopyValue(tag)else{continue}print(name,value)}

When we run this code, we get two results: hasXMP, which has a value of "True", and solar, which has a decidedly less understandable value:

YnBsaXN0MDDRAQJSc2mvEBADDBAUGBwgJCgsMDQ4PEFF1AQFBgcICQoLUWlRelFh
          UW8QACNAcO7vOubr3yO/1e+pmkOtXBAB1AQFBgcNDg8LEAEjQFRxqCKOFiAjwCR6
          waUkDgHUBAUGBxESEwsQAiNAVZV4BI4c+CPAEP2uFrMcrdQEBQYHFRYXCxADI0BW
          tALKmrjwIz/2ObLnx6l21AQFBgcZGhsLEAQjQFfTrJlEjnwjQByrLle1Q0rUBAUG
          Bx0eHwsQBSNAWPrrmI0ISCNAKiwhpSRpc9QEBQYHISIjCxAGI0BgJff9KDpyI0BE
          NTOsilht1AQFBgclJicLEAcjQGbHdYIVQKojQEq3fAg86lXUBAUGBykqKwsQCCNA
          bTGmpC2YRiNAQ2WFOZGjntQEBQYHLS4vCxAJI0BwXfII2B+SI0AmLcjfuC7g1AQF
          BgcxMjMLEAojQHCnF6YrsxcjQBS9AVBLTq3UBAUGBzU2NwsQCyNAcTcSnimmjCPA
          GP5E0ASXJtQEBQYHOTo7CxAMI0BxgSADjxK2I8AoalieOTyE1AQFBgc9Pj9AEA0j
          QHNWsnnMcWIjwEO+oq1pXr8QANQEBQYHQkNEQBAOI0ABZpkFpAcAI8BKYGg/VvMf
          1AQFBgdGR0hAEA8jQErBKblRzPgjwEMGElBIUO0ACAALAA4AIQAqACwALgAwADIA
          NAA9AEYASABRAFMAXABlAG4AcAB5AIIAiwCNAJYAnwCoAKoAswC8AMUAxwDQANkA
          4gDkAO0A9gD/AQEBCgETARwBHgEnATABOQE7AUQBTQFWAVgBYQFqAXMBdQF+AYcB
          kAGSAZsBpAGtAa8BuAHBAcMBzAHOAdcB4AHpAesB9AAAAAAAAAIBAAAAAAAAAEkA
          AAAAAAAAAAAAAAAAAAH9
          

Shining Light on Solar

Most of us would look at that wall of text and quietly close the lid of our MacBook Pro. But, as some of you surely noticed, this text looks an awful lot like it’s Base64-encoded.

Let’s test out our hypothesis in code:

ifname=="solar"{letdata=Data(base64Encoded:value)!print(String(data:data,encoding:.ascii))}

bplist00Ò\u{01}\u{02}\u{03}...

What’s that? bplist, followed by a bunch of garbled nonsense?

By golly, that’s the file signature for a binary property list.

Let’s see if PropertyListSerialization can make any sense of it…

ifname=="solar"{letdata=Data(base64Encoded:value)!letpropertyList=tryPropertyListSerialization.propertyList(from:data,options:[],format:nil)print(propertyList)}
(
          ap = {
          d = 15;
          l = 0;
          };
          si = (
          {
          a = "-0.3427528387535028";
          i = 0;
          z = "270.9334057827345";
          },
          ...
          {
          a = "-38.04743388682423";
          i = 15;
          z = "53.50908581251309";
          }
          )
          )
          

Now we’re talking!

We have two top-level keys:

The ap key corresponds to a dictionary containing integers for the d and l keys.

The si key corresponds to an array of dictionaries with integer and floating-point values. Of the nested dictionary keys, i is the easiest to understand — incrementing from 0 to 15, they’re the index of the image in the sequence. It’d be hard to guess a and z without any additional information, but they turn out to represent the altitude (a) and azimuth (z) of the sun in the corresponding pictures.

Calculating Solar Position

At the time of writing, those of us in the northern hemisphere are settling into the season of autumn and its shorter, colder days, whereas those of us in the southern hemisphere are gearing up for hotter and longer days.

And it’s not just a north/south divide, as anyone living the Arctic or Antarctic circle will tell you. The duration of a solar day depends where you are on the planet and where the planet is in its orbit around the sun.

The good news is that astronomers can tell you with perfect accuracy where the sun is in the sky for any location or time. The bad news is that the necessary calculations are complicated to say the least.

Honestly, we don’t really understand it ourselves, and are pretty much just porting whatever code we manage to find online. After some trial and error, we were able to arrive at something that seems to work (PRs welcome!):

importFoundationimportCoreLocation// Apple Park, Cupertino, CAletlocation=CLLocation(latitude:37.3327,longitude:-122.0053)lettime=Date()letposition=solarPosition(for:location,at:time)letformattedDate=DateFormatter.localizedString(from:time,dateStyle:.medium,timeStyle:.short)print("Solar Position on \(formattedDate)")print("\(position.azimuth)° Az / \(position.elevation)° El")

Solar Position on Oct 1, 2018 at 12:00 180.73470025840783° Az / 49.27482549913847° El

At noon on October 1, 2018, the sun shines on Apple Park from the south, about halfway between the horizon and directly overhead.

If track the position of the sun over an entire day, we get a sinusoidal shape reminiscent of the Apple Watch “Solar” face.

Extending Our Understanding of XMP

Alright, enough astronomy for the moment. Let’s ground ourselves in something much more banal: de facto XML metadata standards.

Remember the hasXMP metadata key from before? Yeah, that.

XMP, or Extensible Metadata Platform, is a standard format for tagging files with metadata. What does XMP look like? Brace yourself:

letxmpData=CGImageMetadataCreateXMPData(metadata,nil)letxmp=String(data:xmpDataas!Data,encoding:.utf8)!print(xmp)
<x:xmpmetaxmlns:x="adobe:ns:meta/"x:xmptk="XMP Core 5.4.0"><rdf:RDFxmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Descriptionrdf:about=""xmlns:apple_desktop="http://ns.apple.com/namespace/1.0/"><apple_desktop:solar><!-- (Base64-Encoded Metadata) --></apple_desktop:solar></rdf:Description></rdf:RDF></x:xmpmeta>

Yuck.

But it’s a good thing that we checked. We’ll need to honor that apple_desktop namespace to make our own Dynamic Desktop images work correctly.

Speaking of, let’s get started on that.

Creating Our Own Dynamic Desktop

Let’s create a data model to represent a Dynamic Desktop:

structDynamicDesktop{letimages:[Image]structImage{letcgImage:CGImageletmetadata:MetadatastructMetadata:Codable{letindex:Intletaltitude:Doubleletazimuth:DoubleprivateenumCodingKeys:String,CodingKey{caseindex="i"casealtitude="a"caseazimuth="z"}}}}

Each Dynamic Desktop comprises an ordered sequence of images, each of which has image data, stored in a CGImage object, and metadata, as discussed before. We adopt Codable in the Metadata declaration in order for the compiler to automatically synthesize conformance. We’ll take advantage of that when it comes time to generate the Base64-encoded binary property list.

Writing to an Image Destination

First, create a CGImageDestination with a specified output URL. The file type is heic and the source count is equal to the number of images to be included.

guardletimageDestination=CGImageDestinationCreateWithURL(outputURLasCFURL,AVFileType.heicasCFString,dynamicDesktop.images.count,nil)else{fatalError("Error creating image destination")}

Next, enumerate over each image in the dynamic desktop object. By using the enumerated() method, we also get the current index for each loop so that we can set the image metadata on the first image:

for(index,image)indynamicDesktop.images.enumerated(){ifindex==0{letimageMetadata=CGImageMetadataCreateMutable()guardlettag=CGImageMetadataTagCreate("http://ns.apple.com/namespace/1.0/"asCFString,"apple_desktop"asCFString,"solar"asCFString,.string,try!dynamicDesktop.base64EncodedMetadata()asCFString),CGImageMetadataSetTagWithPath(imageMetadata,nil,"xmp:solar"asCFString,tag)else{fatalError("Error creating image metadata")}CGImageDestinationAddImageAndMetadata(imageDestination,image.cgImage,imageMetadata,nil)}else{CGImageDestinationAddImage(imageDestination,image.cgImage,nil)}}

Aside from the unrefined nature of Core Graphics APIs, the code is pretty straightforward. The only part that requires further explanation is the call to CGImageMetadataTagCreate(_:_:_:_:_:).

Because of a mismatch between how image and container metadata are structured and how they’re represented in code, we have to implement Encodable for DynamicDesktop ourselves:

extensionDynamicDesktop:Encodable{privateenumCodingKeys:String,CodingKey{caseap,si}privateenumNestedCodingKeys:String,CodingKey{cased,l}funcencode(toencoder:Encoder)throws{varkeyedContainer=encoder.container(keyedBy:CodingKeys.self)varnestedKeyedContainer=keyedContainer.nestedContainer(keyedBy:NestedCodingKeys.self,forKey:.ap)// FIXME: Not sure what `l` and `d` keys indicatetrynestedKeyedContainer.encode(0,forKey:.l)trynestedKeyedContainer.encode(self.images.count,forKey:.d)varunkeyedContainer=keyedContainer.nestedUnkeyedContainer(forKey:.si)forimageinself.images{tryunkeyedContainer.encode(image.metadata)}}}

With that in place, we can implement the aforementioned base64EncodedMetadata() method like so:

extensionDynamicDesktop{funcbase64EncodedMetadata()throws->String{letencoder=PropertyListEncoder()encoder.outputFormat=.binaryletbinaryPropertyListData=tryencoder.encode(self)returnbinaryPropertyListData.base64EncodedString()}}

Once the for-in loop is exhausted, and all images and metadata are written, we call CGImageDestinationFinalize(_:) to finalize the image source and write the image to disk.

guardCGImageDestinationFinalize(imageDestination)else{fatalError("Error finalizing image")}

If everything worked as expected, you should now be the proud owner of a brand new Dynamic Desktop. Nice!


We love the Dynamic Desktop feature in Mojave, and are excited to see the same proliferation of them that we saw when wallpapers hit the mainstream with Windows 95.

If you’re so inclined, here are a few ideas for where to go from here:

Automatically Generating a Dynamic Desktop from Photos

It’s mind-blowing to think that something as transcendent as the movement of celestial bodies can be reduced to a system of equations with two inputs: time and place.

In the example before, this information is hard-coded, but you could ostensibly extract that information from images automatically.

By default, the camera on most phones captures Exif metadata each time a photo is snapped. This metadata can include the time which the photo was taken and the GPS coordinates of the device at the time.

By reading time and location information directly from image metadata, you can automatically determine solar position and simplify the process of generating a Dynamic Desktop from a series of photos.

Shooting a Time Lapse on Your iPhone

Want to put your new iPhone XS to good use? (Or more accurately, “Want to use your old iPhone for something productive while you procrastinate selling it?”)

Mount your phone against a window, plug it into a charger, set the Camera to Timelapse mode, and hit the “Record” button. By extracting key frames from the resulting video, you can make your very own bespoke Dynamic Desktop.

You might also want to check out Skyflow or similar apps that more easily allow you to take still photos at predefined intervals.

Generating Landscapes from GIS Data

If you can’t stand to be away from your phone for an entire day (sad) or don’t have anything remarkable to look (also sad), you could always create your own reality (sounds sadder than it is).

Using an app like Terragen, you can render photo-realistic 3D landscapes, with fine-tuned control over the earth, sun, and sky.

You can make it even easier for yourself by downloading an elevation map from the U.S. Geological Survey’s National Map website and using that as a template for your 3D rendering project.

Downloading Pre-Made Dynamic Desktops

Or if you have actual work to do and can’t be bothered to spend your time making pretty pictures, you can always just pay someone else to do it for you.

We’re personally fans of the the 24 Hour Wallpaper app. If you have any other recommendations, @ us on Twitter!.

Swift Literals

$
0
0

In 1911, linguist Franz Boas observed that speakers of Eskimo–Aleut languages used different words to distinguish falling snowflakes from snow on the ground. By comparison, English speakers typically refer to both as “snow,” but create a similar distinction between raindrops and puddles.

Over time, this simple empirical observation has warped into an awful cliché that “Eskimos [sic] have 50 different words for snow” — which is unfortunate, because Boas’ original observation was empirical, and the resulting weak claim of linguistic relativity is uncontroversial: languages divide semantic concepts into separate words in ways that may (and often do) differ from one another. Whether that’s more an accident of history or reflective of some deeper truth about a culture is unclear, and subject for further debate.

It’s in this framing that you’re invited to consider how the different kinds of literals in Swift shape the way we reason about code.


A literal is a representation of a value in source code, such as a number or a string.

Swift provides the following kinds of literals:

NameDefault Inferred TypeExamples
IntegerInt123, 0b1010, 0o644, 0xFF,
Floating-PointDouble3.14, 6.02e23, 0xAp-2
StringString"Hello", """ . . . """
Extended Grapheme ClusterCharacter"A", "é", "🇺🇸"
Unicode ScalarUnicode.Scalar"A", "´", "\u{1F1FA}"
BooleanBooltrue, false
NilOptionalnil
ArrayArray[1, 2, 3]
DictionaryDictionary["a": 1, "b": 2]

The most important thing to understand about literals in Swift is that they specify a value, but not a definite type.

When the compiler encounters a literal, it attempts to infer the type automatically. It does this by looking for each type that could be initialized by that kind of literal, and narrowing it down based on any other constraints.

If no type can be inferred, Swift initializes the default type for that kind of literal — Int for an integer literal, String for a string literal, and so on.

57// Integer literal"Hello"// String literal

In the case of nil literals, the type can never be inferred automatically and therefore must be declared.

nil// ! cannot infer typenilasString?// Optional<String>.none

For array and dictionary literals, the associated types for the collection are inferred based on its contents. However, inferring types for large or nested collections is a complex operation and may significantly increase the amount of time it takes to compile your code. You can keep things snappy by adding an explicit type in your declaration.

// Explicit type in the declaration// prevents expensive type inference during compilationletdictionary:[String:[Int]]=["a":[1,2],"b":[3,4],"c":[5,6],// ...]

Playground Literals

In addition to the standard literals listed above, there are a few additional literal types for code in Playgrounds:

NameDefault Inferred TypeExamples
ColorNSColor / UIColor#colorLiteral(red: 1, green: 0, blue: 1, alpha: 1)
ImageNSImage / UIImage#imageLiteral(resourceName: "icon")
FileURL#fileLiteral(resourceName: "articles.json")

In Xcode or Swift Playgrounds on the iPad, these octothorpe-prefixed literal expressions are automatically replaced by an interactive control that provides a visual representation of the referenced color, image, or file.

// Code#colorLiteral(red: 0.7477839589, green: 0.5598286986, blue: 0.4095913172, alpha: 1)// Rendering🏽

This control also makes it easy for new values to be chosen: instead of entering RGBA values or file paths, you’re presented with a color picker or file selector.


Most programming languages have literals for Boolean values, numbers, and strings, and many have literals for arrays, dictionaries, and regular expressions.

Literals are so ingrained in a developer’s mental model of programming that most of us don’t actively consider what the compiler is actually doing.

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

How Literals Work

Literals are like words: their meaning can change depending on the surrounding context.

["h","e","l","l","o"]// Array<String>["h"asCharacter,"e","l","l","o"]// Array<Character>["h","e","l","l","o"]asSet<Character>

In the example above, we see that an array literal containing string literals is initialized to an array of strings by default. However, if we explicitly cast the first array element as Character, the literal is initialized as an array of characters. Alternatively, we could cast the entire expression as Set<Character> to initialize a set of characters.

How does this work?

In Swift, the compiler decides how to initialize literals by looking at all the visible types that implement the corresponding literal expression protocol.

LiteralProtocol
IntegerExpressibleByIntegerLiteral
Floating-PointExpressibleByFloatLiteral
StringExpressibleByStringLiteral
Extended Grapheme ClusterExpressibleByExtendedGraphemeClusterLiteral
Unicode ScalarExpressibleByUnicodeScalarLiteral
BooleanExpressibleByBooleanLiteral
NilExpressibleByNilLiteral
ArrayExpressibleByArrayLiteral
DictionaryExpressibleByDictionaryLiteral

To conform to a protocol, a type must implement its required initializer. For example, the ExpressibleByIntegerLiteral protocol requires init(integerLiteral:).

What’s really great about this approach is that it lets you add literal initialization for your own custom types.

Supporting Literal Initialization for Custom Types

Supporting initialization by literals when appropriate can significantly improve the ergonomics of custom types, making them feel like they’re built-in.

For example, if you wanted to support fuzzy logic, in addition to standard Boolean fare, you might implement a Fuzzy type like the following:

structFuzzy:Equatable{varvalue:Doubleinit(_value:Double){precondition(value>=0.0&&value<=1.0)self.value=value}}

A Fuzzy value represents a truth value that ranges between completely true and completely false over the numeric range 0 to 1 (inclusive). That is, a value of 1 means completely true, 0.8 means mostly true, and 0.1 means mostly false.

In order to work more conveniently with standard Boolean logic, we can extend Fuzzy to adopt the ExpressibleByBooleanLiteral protocol.

extensionFuzzy:ExpressibleByBooleanLiteral{init(booleanLiteralvalue:Bool){self.init(value?1.0:0.0)}}

In practice, there aren’t many situations in which it’d be appropriate for a type to be initialized using Boolean literals. Support for string, integer, and floating-point literals are much more common.

Doing so doesn’t change the default meaning of true or false. We don’t have to worry about existing code breaking just because we introduced the concept of half-truths to our code base (“view did appear animated… maybe?”). The only situations in which true or false initialize a Fuzzy value would be when the compiler could infer the type to be Fuzzy:

trueisBool// truetrueisFuzzy// false(trueasFuzzy)isFuzzy// true(falseasFuzzy).value// 0.0

Because Fuzzy is initialized with a single Double value, it’s reasonable to allow values to be initialized with floating-point literals as well. It’s hard to think of any situations in which a type would support floating-point literals but not integer literals, so we should do that too (however, the converse isn’t true; there are plenty of types that work with integer but not floating point numbers).

extensionFuzzy:ExpressibleByIntegerLiteral{init(integerLiteralvalue:Int){self.init(Double(value))}}extensionFuzzy:ExpressibleByFloatLiteral{init(floatLiteralvalue:Double){self.init(value)}}

With these protocols adopted, the Fuzzy type now looks and feels like a bona fide member of Swift standard library.

letcompletelyTrue:Fuzzy=trueletmostlyTrue:Fuzzy=0.8letmostlyFalse:Fuzzy=0.1

(Now the only thing left to do is implement the standard logical operators!)

If convenience and developer productivity is something you want to optimize for, you should consider implementing whichever literal protocols are appropriate for your custom types.

Future Developments

Literals are an active topic of discussion for the future of the language. Looking forward to Swift 5, there are a number of current proposals that could have terrific implications for how we write code.

Raw String Literals

At the time of writing, Swift Evolution proposal 0200 is in active review. If it’s accepted, future versions of Swift will support “raw” strings, or string literals that ignores escape sequences.

From the proposal:

Our design adds customizable string delimiters. You may pad a string literal with one or more # (pound, Number Sign, U+0023) characters […] The number of pound signs at the start of the string (in these examples, zero, one, and four) must match the number of pound signs at the end of the string.

"This is a Swift string literal"#"This is also a Swift string literal"#####"So is this"####

This proposal comes as a natural extension of the new multi-line string literals added in Swift 4 (SE-0165), and would make it even easier to do work with data formats like JSON and XML.

If nothing else, adoption of this proposal could remove the largest obstacle to using Swift on Windows: dealing with file paths like C:\Windows\All Users\Application Data.

Literal Initialization Via Coercion

Another recent proposal, SE-0213: Literal initialization via coercion is already implemented for Swift 5.

From the proposal:

T(literal) should construct T using the appropriate literal protocol if possible.

Currently types conforming to literal protocols are type-checked using regular initializer rules, which means that for expressions like UInt32(42) the type-checker is going to look up a set of available initializer choices and attempt them one-by-one trying to deduce the best solution.

In Swift 4.2, initializing a UInt64 with its maximum value results in a compile-time overflow because the compiler first tries to initialize an Int with the literal value.

UInt64(0xffff_ffff_ffff_ffff)// overflows in Swift 4.2

Starting in Swift 5, not only will this expression compile successfully, but it’ll do so a little bit faster, too.


The words available to a language speaker influence not only what they say, but how they think as well. In the same way, the individual parts of a programming language hold considerable influence over how a developer works.

The way Swift carves up the semantic space of values makes it different from languages that don’t, for example, distinguish between integers and floating points or have separate concepts for strings, characters, and Unicode scalars. So it’s no coincidence that when we write Swift code, we often think about numbers and strings at a lower level than if we were hacking away in, say, JavaScript.

Along the same lines, Swift’s current lack of distinction between string literals and regular expressions contributes to the relative lack of regex usage compared to other scripting languages.

That’s not to say that having or lacking certain words makes it impossible to express certain ideas — just a bit fuzzier. We can understand “untranslatable” words like “Saudade” in Portuguese, “Han” in Korean, or “Weltschmerz” in German.

We’re all human. We all understand pain.

By allowing any type to support literal initialization, Swift invites us to be part of the greater conversation. Take advantage of this and make your own code feel like a natural extension of the standard library.

NSDataDetector

$
0
0

Text means nothing without context.

What gives weight to our words is their relation to one another, to ourselves, and to our location space-time.

Consider endophoric expressions whose meaning depends on the surrounding text, or deictic expressions, whose meaning is dependent on who the speaker is, where they are, and when they said it. Now consider how difficult it would be for a computer to make sense of an utterance like “I’ll be home in 5 minutes”? (And that’s to say nothing of the challenges of ambiguity and variation in representations of dates, addresses, and other information.)

For better or worse, that’s how we communicate. And until humanity embraces RDF for our daily interactions, computers will have to work overtime to figure out what the heck we’re all talking about.


There’s immense value in transforming natural language into structured data that’s compatible with our calendars, address books, maps, and reminders. Manual data entry, however, amounts to drudgery, and is the last thing you want to force on users.

On other platforms, you might delegate this task to a web service or hack something together that works well enough. Fortunately for us Cocoa developers, Foundation us covered with NSDataDetector.

You can use NSDataDetector to extract dates, links, phone numbers, addresses, and transit information from natural language text.

First, create a detector, by specifying the result types that you’re interested in. Then call the enumerateMatches(in:options:range:using:) method, passing the text to be processed. The provided closure is executed once for each result.

letstring="123 Main St. / (555) 555-1234"lettypes:NSTextCheckingResult.CheckingType=[.phoneNumber,.address]letdetector=tryNSDataDetector(types:types.rawValue)detector.enumerateMatches(in:string,options:[],range:range){(result,_,_)inprint(result)}
NSString*string=@"123 Main St. / (555) 555-1234";NSError*error=nil;NSDataDetector*detector=[NSDataDetectordataDetectorWithTypes:NSTextCheckingTypeAddress|NSTextCheckingTypePhoneNumbererror:&error];[detectorenumerateMatchesInString:stringoptions:kNilOptionsrange:NSMakeRange(0,[stringlength])usingBlock:^(NSTextCheckingResult*result,NSMatchingFlagsflags,BOOL*stop){NSLog(@"%@",result);}];

As you might expect, running this code produces two results: the address “123 Main St.” and the phone number “(555) 555-1234”.

When initializing NSDataDetector, specify only the types you’re interested in because any unused types will only slow you down.

Discerning Information from Results

NSDataDetector produces NSTextCheckingResult objects.

On the one hand, this makes sense because NSDataDetector is actually a subclass of NSRegularExpression. On the other hand, there’s not much overlap between a pattern match and detected data other than the range and type. So what you get is an API that’s polluted and offers no strong guarantees about what information is present under which circumstances.

To make matters worse, NSTextCheckingResult is also used by NSSpellServer. Gross.

To get information about data detector results, you need to first check its resultType; depending on that, you might access information directly through properties, (in the case of links, phone numbers, and dates), or indirectly by keyed values on the components property (for addresses and transit information).

Here’s a rundown of the various NSDataDetector result types and their associated properties:

TypeProperties
.link
  • .url
.phoneNumber
  • .phoneNumber
.date
  • .date
  • .duration
  • .timeZone
.address
  • .components
    • .name
    • .jobTitle
    • .organization
    • .street
    • .city
    • .state
    • .zip
    • .country
    • .phone
.transitInformation
  • .components
    • .airline
    • .flight

Data Detector Data Points

Let’s put NSDataDetector through its paces. That way, we’ll not only have a complete example of how to use it to its full capacity but see what it’s actually capable of.

The following text contains one of each of the type of data that NSDataDetector should be able to detect:

letstring="""
        My flight (AA10) is scheduled for tomorrow night from 9 PM PST to 5 AM EST.
        I'll be staying at The Plaza Hotel, 768 5th Ave, New York, NY 10019.
        You can reach me at 555-555-1234 or me@example.com
        """

We can have NSDataDetector check for everything by passing NSTextCheckingAllTypes to its initializer. The rest is a matter of switching over each resultType and extracting their respective details:

letdetector=tryNSDataDetector(types:NSTextCheckingAllTypes)letrange=NSRange(string.startIndex..<string.endIndex,in:string)detector.enumerateMatches(in:string,options:[],range:range){(match,flags,_)inguardletmatch=matchelse{return}switchmatch.resultType{case.date:letdate=match.datelettimeZone=match.timeZoneletduration=match.durationprint(date,timeZone,duration)case.address:ifletcomponents=match.components{letname=components[.name]letjobTitle=components[.jobTitle]letorganization=components[.organization]letstreet=components[.street]letlocality=components[.city]letregion=components[.state]letpostalCode=components[.zip]letcountry=components[.country]letphoneNumber=components[.phone]print(name,jobTitle,organization,street,locality,region,postalCode,country,phoneNumber)}case.link:leturl=match.urlprint(url)case.phoneNumber:letphoneNumber=match.phoneNumberprint(phoneNumber)case.transitInformation:ifletcomponents=match.components{letairline=components[.airline]letflight=components[.flight]print(airline,flight)}default:return}}

When we run this code, we see that NSDataDetector is able to identify each of the types.

TypeOutput
Date“2018-08-31 04:00:00 +0000”, “America/Los_Angeles”, 18000.0
Addressnil, nil, nil“768 5th Ave”, “New York”, “NY”, “10019”, nil, nil
Link“mailto:me@example.com”
Phone Number“555-555-1234”
Transit Informationnil, “10”

Impressively, the date result correctly calculates the 6-hour duration of the flight, accommodating for the time zone change. However, some information is missing, like the name of The Plaza Hotel in the address, and the airline in the transit information.

Even after trying a handful of different representations (“American Airlines 10”, “AA 10”, “AA #10”, “American Airlines (AA) #10”) and airlines (“Delta 1226”, “DL 1226”) I still wasn’t able to find an example that populated the airline property. If anyone knows what’s up, @ us.

Detect (Rough) Edges

Useful as NSDataDetector is, it’s not a particularly nice API to use.

With all of the charms of its parent class, NSRegularExpression, the same, cumbersome initialization pattern of NSLinguisticTagger, and an incomplete Swift interface, NSDataDetector has an interface that only a mother could love.

But that’s only the API itself.

In a broader context, you might be surprised to learn that a nearly identical API can be found in the dataDetectorTypes properties of UITextView and WKWebView. Nearly identical.

UIDataDetectorTypes and WKDataDetectorTypes are distinct from and incompatible with NSTextCheckingTypes, which is inconvenient but not super conspicuous. But what’s utterly inexplicable is that these APIs can detect shipment tracking numbers and lookup suggestions, neither of which are supported by NSDataDetector. It’s hard to imagine why shipment tracking numbers wouldn’t be supported, which leads one to believe that it’s an oversight.


Humans have an innate ability to derive meaning from language. We can stitch together linguistic, situational and cultural information into a coherent interpretation at a subconscious level. Ironically, it’s difficult to put this process into words — or code as the case may be. Computers aren’t hard-wired for understanding like we are.

Despite its shortcomings, NSDataDetector can prove invaluable for certain use cases. Until something better comes along, take advantage of it in your app to unlock the structured information hiding in plain sight.

CMDeviceMotion

$
0
0

Beneath the smooth glass of each iPhone an array of sensors sits nestled on the logic board, sending a steady stream of data to a motion coprocessor.

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.


Core Motion lets you observe and respond to changes in the position and orientation of an iOS or watchOS device. Thanks to their dedicated motion coprocessor, iPhones, iPads, and Apple Watches can continuously read and process inputs from built-in sensors without taxing the CPU or draining the battery.

Accelerometer and gyroscope data is projected into a 3D coordinate space, with the center of the device at the origin.

Device X-, Y-, and Z-axes

For an iPhone held in portrait orientation:

  • The X-axis runs the width of the device from left (negative values) to right (positive values),
  • The Y-axis runs the height of the device from bottom (-) to top (+),
  • The Z-axis runs perpendicularly through the screen from the back (-) to the front (+).

CMMotionManager

The CMMotionManager class is responsible for providing data about the motion of the current device. To keep performance at the highest level, create and use a single shared CMMotionManager instance throughout your app.

CMMotionManager provides four different interfaces for sensor information, each with corresponding properties and methods to check hardware availability and access measurements.

  • The accelerometer measures acceleration, or changes in velocity over time.
  • The gyroscope measures attitude, or the orientation of the device.
  • The magnetometer is essentially a compass, and measures the Earth’s magnetic field relative to the device.

In addition to these individual readings, CMMotionManager also provides a unified “device motion” interface, which uses sensor fusion algorithms to combine readings from each of these sensors into a unified view of the device in space.

Checking for Availability

Although most Apple devices these days come with a standard set of sensors, it’s still a good idea to check for the capabilities of the current device before attempting to read motion data.

The following examples involve the accelerometer, but you could replace the word “accelerometer” for the type of motion data that you’re interested in (such as “gyro”, “magnetometer”, or “deviceMotion”):

letmanager=CMMotionManager()guardmanager.isAccelerometerAvailableelse{return}

Push vs. Pull

Core Motion provides both “pull” and “push” access to motion data.

To “pull” motion data, you access the current reading from using one of the read-only properties of CMMotionManager.

To receive “pushed” data, you start the collection of your desired data with a closure that receives updates at a specified interval.

Starting Updates to “pull” Data

manager.startAccelerometerUpdates()

After this call, manager.accelerometerData is accessible at any time with the device’s current accelerometer data.

manager.accelerometerData

You can also check whether motion data is available by reading the corresponding “is active” property.

manager.isAccelerometerActive

Starting Updates to “push” Data

manager.startAccelerometerUpdates(to:.main){(data,error)inguardletdata=data,error!=nilelse{return}// ...}

The passed closure is called at the frequency provided by the update interval. (Actually, Core Motion enforces a minimum and maximum frequency, so specifying a value outside of that range causes that value to be normalized; you can determine the effective interval rate of the current device by checking the timestamps of motion events over time.)

Stopping Updates

manager.stopAccelerometerUpdates()

Accelerometer in Action

Let’s say we want to give the splash page of our app a fun effect, such that the background image remains level no matter how the phone is tilted.

Consider the following code:

ifmanager.isAccelerometerAvailable{manager.accelerometerUpdateInterval=0.01manager.startAccelerometerUpdates(to:.main){[weakself](data,error)inguardletdata=data,error!=nilelse{return}letrotation=atan2(data.acceleration.x,data.acceleration.y)-.piself?.imageView.transform=CGAffineTransform(rotationAngle:CGFloat(rotation))}}

First, we check to make sure our device makes accelerometer data available. Next we specify a high update frequency. And then finally, we begin updates to a closure that will rotate a UIImageView property:

Each CMAccelerometerData object includes an x, y, and z value — each of these shows the amount of acceleration in G-forces (where 1G = the force of gravity on Earth) for that axis. If your device were stationary and standing straight up in portrait orientation, it would have acceleration (0, -1, 0); laying flat on its back on the table, it would be (0, 0, -1); tilted forty-five degrees to the right, it would be something like (0.707, -0.707, 0)(dat √2 tho).

We calculate the rotation with the two-argument arctangent function (atan2) using the x and y components from the accelerometer data. We then initialize a CGAffineTransform using that calculate rotation. Our image should stay right-side-up, no matter how the phone is turned — here, it is in a hypothetical app for the National Air & Space Museum (my favorite museum as a kid):

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 by calling the startGyroUpdates... method, let’s get composited gyroscope and accelerometer data by requesting the unified”device motion” data. Using the gyroscope, Core Motion separates user movement from gravitational acceleration and presents each as its own property of the CMDeviceMotion object. The code is very similar to our first example:

ifmanager.isDeviceMotionActive{manager.deviceMotionUpdateInterval=0.01manager.startDeviceMotionUpdates(to:.main){[weakself](data,error)inguardletdata=data,error!=nilelse{return}letrotation=atan2(data.acceleration.x,data.acceleration.y)-.piself?.imageView.transform=CGAffineTransform(rotationAngle:CGFloat(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 CMDeviceMotion to navigate backward whenever the user taps the left side of the device against their 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’s our cue to pop the view controller from the stack. The implementation is only a couple lines different from our previous example:

ifmanager.isDeviceMotionActive{manager.deviceMotionUpdateInterval=0.01manager.startDeviceMotionUpdates(to:.main){[weakself](data,error)inguardletdata=data,error!=nilelse{return}ifdata.userAcceleration.x<-2.5{self?.navigationController?.popViewControllerAnimated(true)}}}

Works like a charm!

Tapping the device in a detail view immediately takes us back to the list of exhibits:

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. This data is accessed via the attitude property of a CMDeviceMotion object and encapsulated in a CMAttitude object. CMAttitude contains three different representations of the device’s orientation:

  • Euler angles,
  • A quaternion,
  • 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.
  • CMAttitudeReferenceFrameXMagneticNorthZVertical describes a device laying flat, with its X-axis (that is, the right side of the device in portrait mode when it’s facing you) pointed toward magnetic north. This setting may require the user to perform that figure-eight motion with their device to calibrate the magnetometer.
  • CMAttitudeReferenceFrameXTrueNorthZVertical is the same as the last, but it adjusts for magnetic / true north discrepancy and therefore requires location data in addition to 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
  • 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 flip the view as the device turns around, so the quizzer sees the answer while the person being quizzed sees only the prompt.

Figuring out this switch from the reference frame would be tricky. To know which angles to monitor, we would somehow need to account for the starting orientation of the device and then determine which direction the device is pointing. Instead, we can save a CMAttitude instance and use it as the “zero point” for an adjusted set of Euler angles, calling the multiply(byInverseOf:) 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 theoremfuncmagnitude(fromattitude: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

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.isDeviceMotionActive{manager.startDeviceMotionUpdates(to:.main){// translate the attitudedata.attitude.multiply(byInverseOf:initialAttitude)// calculate magnitude of the change from our initial attitudeletmagnitude=magnitude(from:data.attitude)??0// show the promptif!showingPrompt&&magnitude>showPromptTrigger{ifletpromptViewController=self?.storyboard?.instantiateViewController(withIdentifier:"PromptViewController")as?PromptViewController{showingPrompt=truepromptViewController.modalTransitionStyle=.crossDissolveself?.present(promptViewController,animated:true,completion:nil)}}// hide the promptifshowingPrompt&&magnitude<showAnswerTrigger{showingPrompt=falseself?.dismiss(animated:true,completion: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 of our motion updates to the main queue. A better approach would be to schedule these updates on their own queue and dispatch back to main to update the UI.

letqueue=DispatchQueue(label:"motion")manager.startDeviceMotionUpdates(to:queue){[weakself](data,error)in// motion processing hereDispatchQueue.main.async{// update UI here}}

Remember that not all interactions made possible by Core Motion are good ones. Navigation through motion can be fun, but it can also be hard to discover, easy to accidentally trigger, and may not be accessible to all users. Similar to purposeless animations, overuse of fancy gestures 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 apps and delight users.

Viewing all 382 articles
Browse latest View live