NSHipster An imprint of Read Evaluate Press, LLC Portland, Oregon https://readeval.press Copyright © 2019 Mattt Zmuda All rights reserved ISBN 978-1-949080-24-7 Printed in Minion, by Carol Twombly and Robert Slimbach, and Avenir, by Adrian Frutiger. Code excerpts set in Menlo, by Jim Lyles. Twemoji graphics made by Twitter and other contributors. Cover type set in Trajan, by Carol Twombly and Robert Slimbach, in an homage to The C Programming Language by Brian Kernighan and Dennis Ritchie. Table of Contents Objective-C #pragma ..................................................................................................................................................3 @ ..............................................................................................................................................................7 nil / Nil / NULL / NSNull ...................................................................................................................17 BOOL / bool / Boolean / NSCFBoolean ...........................................................................................21 NS_ENUM & NS_OPTIONS .............................................................................................................25 Associated Objects ..............................................................................................................................29 Method Swizzling ................................................................................................................................35 Objective-C Direct Methods ..............................................................................................................41 Swift Equatable and Comparable ................................................................................................................51 Hashable / Hasher ...............................................................................................................................59 Identifiable ...........................................................................................................................................65 Void .......................................................................................................................................................77 Never ....................................................................................................................................................87 OptionSet .............................................................................................................................................95 numericCast(_:) .................................................................................................................................101 guard & defer .....................................................................................................................................107 Mirror / CustomReflectable / CustomLeafReflectable ..................................................................117 KeyValuePairs ....................................................................................................................................129 Swift Property Wrappers ..................................................................................................................137 Swift API Availability ........................................................................................................................155 Swift Import Declarations ................................................................................................................167 API Pollution in Swift Modules .......................................................................................................175 Swift Operators ..................................................................................................................................183 Swift Literals ......................................................................................................................................193 ExpressibleByStringInterpolation ....................................................................................................203 TextOutputStream .............................................................................................................................211 Regular Expressions in Swift ............................................................................................................219 Swift GYB ...........................................................................................................................................229 SwiftSyntax .........................................................................................................................................235 Swift Documentation ........................................................................................................................245 Swift Property Observers .................................................................................................................257 Cocoa TimeInterval, Date, and DateInterval .............................................................................................265 CharacterSet .......................................................................................................................................271 NLLanguageRecognizer ...................................................................................................................279 Locale ..................................................................................................................................................285 Formatter ............................................................................................................................................293 FileManager .......................................................................................................................................315 Temporary Files .................................................................................................................................325 CoreGraphics Geometry Primitives ................................................................................................333 Bundles and Packages .......................................................................................................................345 Miscellaneous Empathy .............................................................................................................................................355 Objective-C #pragma #pragma declarations are a mark of craftsmanship in Objective-C. Although originally used to make source code compatible across different compilers, Xcode-savvy programmers use #pragma declarations to very different ends. Whereas other preprocessor directives allow you to define behavior when code is executed, #pragma is unique in that it gives you control at the time code is being written — specifically, by organizing code under named headings and inhibiting warnings. As we’ll see in this week’s article, Good developer habits start with #pragma mark. Organizing Your Code Code organization is a matter of hygiene. How you structure your code is a reflection of you and your work. A lack of convention and internal consistency indicates either carelessness or incompetence — and worse, it makes a project more challenging to maintain and collaborate on. We here at NSHipster believe that code should be clean enough to eat off of. That’s why se use #pragma mark to divide code into logical sections. If your class overrides any inherited methods, organize them under common headings according to their superclass. This has the added benefit of describing the responsibilities for each ancestor in the class hierarchies; for example, an NSInputStream subclass might have a group marked NSInputStream, followed by NSStream, and then finally NSObject. If your class adopts any protocols, it makes sense to group each of their respective methods with a #pragma mark header with the name of that protocol (bonus points for following the same order as the original declarations). Additional concerns that naturally align under their own headings include initializers, @dynamic properties, helper methods, Interface Builder outlets or actions, and handlers for notification or KeyValue Observing (KVO) selectors. For example, the @implementation for a ViewController class that inherits from UITableView Controller might organize Interface Builder actions together at the top, followed by overridden view controller life-cycle methods, and then methods for each adopted protocol, each under their respective heading. @implementation ViewController - (instancetype)init { … } #pragma mark - IBAction - (IBAction)confirm:(id)sender { … } - (IBAction)cancel:(id)sender { … } #pragma mark - UIViewController - (void)viewDidLoad { … } - (void)viewDidAppear:(BOOL)animated { … } #pragma mark - UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { … } #pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { … } @end Not only do these sections make for easier reading in the code itself, but they also create visual cues to the Xcode source navigator and minimap. #pragma mark declarations beginning with a dash (-) are preceded with a horizontal divider in Xcode. Inhibiting Warnings Do you know what’s even more annoying than poorly-formatted code? Code that generates warnings — especially 3rd-party code. Is there anything more irksome than a vendor SDK that generates dozens of warnings each time you hit ⌘ R ? Heck, even a single warning is one too many for many developers. Warnings are almost always warranted, but sometimes they’re unavoidable. Under those rare circumstances where you are absolutely certain that a particular warning from the compiler or static analyzer should be inhibited, #pragma comes to the rescue. Let’s say you’ve deprecated a class: @interface DeprecatedClass __attribute__ ((deprecated)) … @end Now, deprecation is something to be celebrated. It’s a way to responsibly communicate future intent to API consumers that provides a transition period to migrate to an alternative solution. But you might not feel so appreciated if you have CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS enabled in your project. Rather than being praised for your consideration, you’d be admonished for ““implementing a deprecated class””. One way to silence the compiler would be to disable the warnings by setting -Wno-deprecated-implementations. However, doing this for the entire project is too coarse, and doing this for this file only is too much work. A better option would be to use #pragma to ignore the unhelpful warning around the problematic code. Using #pragma clang diagnostic push/pop, you can tell the compiler to ignore certain warnings for a particular section of code: #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-implementations" @implementation DeprecatedClass … @end #pragma clang diagnostic pop Before you start copy-pasting this across your project in a rush to zero out your warnings, remember that, in the overwhelming majority of cases, Clang is going to be right. Actually fixing an analyzer warning is strongly preferred to ignoring it, so you should use #pragma clang diagnostic ignored as a method of last resort. Finding app development to be too easy for you? Enable all diagnostics with the -Weverything flag and enable “Treat Warnings as Errors”. This turns on “Hard Mode” in Xcode. Though it skirts the line between comment and code, #pragma remains a vital tool in the modern app developer’s toolbox. Whether it’s for grouping methods or suppressing spurious warnings, judicious use of #pragma can go a long way to make projects easier to understand and maintain. Like the thrift store 8-track player you turned into that lamp in the foyer, #pragma remains a curious vestige of the past: Once the secret language of compilers, now re-purposed to better-communicate intent to other programmers. How delightfully vintage! @ Birdwatchers refer to it as (and I swear I’m not making this up) “Jizz”: Those indefinable characteristics unique to a particular kind of thing. This term can be appropriated to describe how seasoned developers might distinguish Rust from Go, or Ruby from Elixir at a glance. Some just stick out like sore thumbs: Perl, with all of its short variable names with special characters, reads like Q*bert swearing. Lisp, whose profusion of parentheses is best captured by that old joke about Russians in the 1980s proving that they had stolen the source code of some SDI missile interceptor by showing the last page: ))) ) ) ))) ) )) ))))) ))) ) )) )))) )) )))) )) ))) So if we were to go code-watching for the elusive Objective-C species, what would we look for? That’s right: • Square brackets • Ridiculously-long method names • @’s @, or “at” sign compiler directives, are as central to understanding Objective-C’s gestalt as its ancestry and underlying mechanisms. It’s the sugary glue that allows Objective-C to be such an expressive language, and yet still compile all the way down to C. Its uses are varied and disparate, to the point that the only accurate way to describe what @ means by itself is “shorthand for something to do with Objective-C”. They cover a broad range in usefulness and obscurity, from staples like @interface and @implementation to ones you could go your whole career without running into, like @defs and @compatibility_alias. But to anyone aspiring to be an NSHipster, intimate familiarity with @ directives is tantamount to a music lover’s ability to enumerate the entire Beatles catalog in chronological order (and most importantly, having unreasonably strong opinions about each of them). Interface & Implementation @interface and @implementation are the first things you encounter when you start learning Objective-C: // MyObject.h @interface MyObject … @end // MyObject.m @implementation MyObject … @end What you don’t learn about until later on are categories and class extensions. Categories allow you to extend the behavior of existing classes by adding new class or instance methods. As a convention, categories are defined in their own .{h,m} files: // MyObject+CategoryName.h @interface MyObject (CategoryName) - (void)foo; - (BOOL)barWithBaz:(NSInteger)baz; @end // MyObject+CategoryName.m @implementation MyObject (CategoryName) - (void)foo { … } - (BOOL)barWithBaz:(NSInteger)baz { return YES; } @end Categories are particularly useful for convenience methods on standard framework classes (just don’t go overboard with your utility functions). Extensions look like categories, but omit the category name. These’re typically declared before an @implementation to specify a private interface or override properties declared in the public interface: // MyObject.m @interface MyObject () @property (readwrite, nonatomic, strong) NSString *name; - (void)somePrivateMethod; @end @implementation MyObject … @end Properties Property directives are likewise concepts learned early on: • @property • @synthesize • @dynamic Though, since Xcode introduced automatic property synthesis in version 4.4, @synthesize has become less relevant to Objective-C code bases. Forward Class Declarations Occasionally, @interface declarations will reference an external type in a property or as a parameter. Rather than adding an #import statement in the interface, you can use a forward class declaration in the header and import the necessary in the implementation. • @class Shorter compile times, less chance of cyclical references; you should definitely get in the habit of doing this if you aren’t already. Instance Variable Visibility It’s a matter of general convention that classes provide state and mutating interfaces through properties and methods, rather than directly exposing ivars. Although ARC makes working with ivars much safer by taking care of memory management, the aforementioned automatic property synthesis removes the one place where ivars would otherwise be declared. Nonetheless, in cases where ivars are directly manipulated, there are the following visibility directives: • @public: instance variable can be read and written to directly, using the notation person>age = 32" • @package: instance variable is public, except outside of the framework in which it is specified (64-bit architectures only) • @protected: instance variable is only accessible to its class and derived classes • @private: instance variable is only accessible to its class @interface Person : NSObject { @public NSString *name; int age; @private int salary; } @end Protocols There’s a distinct point early in an Objective-C programmer’s evolution when they realize that they can define their own protocols. The beauty of protocols is that they let you design contracts that can be adopted outside of a class hierarchy. It’s the egalitarian mantra at the heart of the American Dream: It doesn’t matter who you are or where you come from; anyone can achieve anything if they work hard enough. @protocol…@end defines a set of methods to be implemented by any conforming class, as if they were added to the interface of that class directly. Architectural stability and expressiveness without the burden of coupling? Protocols are awesome. Requirement Options You can further tailor a protocol by specifying methods as required or optional. Optional methods are stubbed in the interface, so as to be auto-completed in Xcode, but do not generate a warning if the method is not implemented. Protocol methods are required by default. The syntax for @required and @optional follows that of the visibility macros: @protocol CustomControlDelegate - (void)control:(CustomControl *)control didSucceedWithResult:(id)result; @optional - (void)control:(CustomControl *)control didFailWithError:(NSError *)error; @end Exception Handling Objective-C communicates unexpected state primarily through NSError. Whereas other languages would use exception handling for this, Objective-C relegates exceptions to truly exceptional behavior. @ directives are used for the traditional convention of try/catch/finally blocks: @try{ // attempt to execute the following statements [self getValue:&value error:&error]; // if an exception is raised, or explicitly thrown... if (error) { @throw exception; } } @catch(NSException *e) { … } @finally { // always executed after @try or @catch [self cleanup]; } Literals Literals are shorthand notation for specifying fixed values. Literals are more -or-less directly correlated with programmer happiness. By this measure, Objective-C has long been a language of programmer misery. Object Literals Until recently, Objective-C only had literals for NSString. But with the release of the Apple LLVM 4.0 compiler, literals for NSNumber, NSArray and NSDictionary were added, with much rejoicing. • @"": Returns an NSString object initialized with the Unicode content inside the quotation marks. • @42, @3.14, @YES, @'Z': Returns an NSNumber object initialized with pertinent class constructor, such that @42 → [NSNumber numberWithInteger:42], or @YES → [NSNumber numberWithBool:YES]. Supports the use of suffixes to further specify type, like @42U → [NSNumber numberWithUnsignedInt:42U]. • @[]: Returns an NSArray object initialized with the comma-delimited list of objects as its contents. It uses +arrayWithObjects:count: class constructor method, which is a more precise alternative to the more familiar +arrayWithObjects:. For example, @[@"A", @NO, @2.718] → id objects[] = {@"A", @NO, @2.718}; [NSArray arrayWithObjects:objects count:3]. • @{}: Returns an NSDictionary object initialized with the specified key-value pairs as its contents, in the format: @{@"someKey" : @"theValue"}. • @(): Dynamically evaluates the boxed expression and returns the appropriate object literal based on its value (i.e. NSString for const char*, NSNumber for int, etc.). This is also the designated way to use number literals with enum values. Objective-C Literals Selectors and protocols can be passed as method parameters. @selector() and @protocol() serve as pseudo-literal directives that return a pointer to a particular selector (SEL) or protocol (Protocol *). • @selector(): Returns an SEL pointer to a selector with the specified name. Used in methods like -performSelector:withObject:. • @protocol(): Returns a Protocol * pointer to the protocol with the specified name. Used in methods like -conformsToProtocol:. C Literals Literals can also work the other way around, transforming Objective-C objects into C values. These directives in particular allow us to peek underneath the Objective-C veil, to begin to understand what’s really going on. Did you know that all Objective-C classes and objects are just glorified structs? Or that the entire identity of an object hinges on a single isa field in that struct? For most of us, at least most of the time, coming into this knowledge is but an academic exercise. But for anyone venturing into low-level optimizations, this is simply the jumping-off point. • @encode(): Returns the type encoding of a type. This type value can be used as the first argument encode in NSCoder -encodeValueOfObjCType:at:. • @defs(): Returns the layout of an Objective-C class. For example, to declare a struct with the same fields as an NSObject, you would simply do: struct { @defs(NSObject) } @defs is unavailable in the modern Objective-C runtime. Optimizations There are some @ compiler directives specifically purposed for providing shortcuts for common optimizations. • @autoreleasepool{}: If your code contains a tight loop that creates lots of temporary objects, you can use the @autoreleasepool directive to optimize for these short-lived, locally-scoped objects by being more aggressive about how they’re deallocated. @autoreleasepool replaces and improves upon the old NSAutoreleasePool, which is significantly slower, and unavailable with ARC. • @synchronized(){}: This directive offers a convenient way to guarantee the safe execution of a particular block within a specified context (usually self). Locking in this way is expensive, however, so for classes aiming for a particular level of thread safety, a dedicated NSLock property or the use of low-level locking functions like OSAtomicCompareAndSwap32(3) are recommended. Compatibility In case all of the previous directives were old hat for you, there’s a strong likelihood that you didn’t know about this one: • @compatibility_alias: Allows existing classes to be aliased by a different name. For example PSTCollectionView uses @compatibility_alias to significantly improve the experience of using the backwards-compatible, drop-in replacement for UICollectionView: // Allows code to just use UICollectionView as if it would be available on iOS SDK 5. // http://developer.apple. com/legacy/mac/library/#documentation/ DeveloperTools/gcc-3. 3/gcc/compatibility_005falias.html #if __IPHONE_OS_VERSION_MAX_ALLOWED < 60000 @compatibility_alias UICollectionViewController PSTCollectionViewController; @compatibility_alias UICollectionView PSTCollectionView; @compatibility_alias UICollectionReusableView PSTCollectionReusableView; @compatibility_alias UICollectionViewCell PSTCollectionViewCell; @compatibility_alias UICollectionViewLayout PSTCollectionViewLayout; @compatibility_alias UICollectionViewFlowLayout PSTCollectionViewFlowLayout; @compatibility_alias UICollectionViewLayoutAttributes PSTCollectionViewLayoutAttributes; @protocol UICollectionViewDataSource <PSTCollectionViewDataSource> @end @protocol UICollectionViewDelegate <PSTCollectionViewDelegate> @end #endif Using this clever combination of macros, a developer can develop with UICollectionView by including PSTCollectionView–without worrying about the deployment target of the final project. As a drop-in replacement, the same code works more-or-less identically on iOS 6 as it does on iOS 4.3. Thus concludes this exhaustive rundown of the many faces of @. It’s a versatile, power-packed character that embodies the underlying design and mechanisms of the language. nil / Nil / NULL / NSNull Understanding the concept of nothingness is as much a philosophical issue as it is a pragmatic one. We are inhabitants of a universe of somethings, yet reason in a logical universe of existential uncertainties. As a physical manifestation of a logical system, computers are faced with the intractable problem of how to represent nothing with something. In Objective-C, there are several different varieties of nothing. The reason for this goes back to a common NSHipster refrain, of how Objective-C bridges the procedural paradigm of C with Smalltalkinspired object-oriented paradigm. C represents nothing as 0 for primitive values, and NULL for pointers (which is equivalent to 0 in a pointer context). Objective-C builds on C’s representation of nothing by adding nil. nil is an object pointer to nothing. Although semantically distinct from NULL, they are technically equivalent to one another. On the framework level, Foundation defines NSNull, which defines a class method, +null, which returns the singleton NSNull object. NSNull is different from nil or NULL, in that it is an actual object, rather than a zero value. Additionally, in Foundation/NSObjCRuntime.h, Nil is defined as a class pointer to nothing. This lesserknown title-case cousin of nil doesn’t show up much very often, but it’s at least worth noting. There’s Something About nil Newly-alloc‘d NSObjects start life with their contents set to 0. This means that all pointers that object has to other objects begin as nil, so it’s unnecessary to, for instance, set self.(association) = nil in init methods. Perhaps the most notable behavior of nil, though, is that it can have messages sent to it. In other languages, like C++, this would crash your program, but in Objective-C, invoking a method on nil returns a zero value. This greatly simplifies expressions, as it obviates the need to check for nil before doing anything: // For example, this expression... if (name != nil && [name isEqualToString:@"Steve"]) { ... } // ...can be simplified to: if ([name isEqualToString:@"Steve"]) { ... } Being aware of how nil works in Objective-C allows this convenience to be a feature, and not a lurking bug in your application. Make sure to guard against cases where nil values are unwanted, either by checking and returning early to fail silently, or adding a NSParameterAssert to throw an exception. NSNull: Something for Nothing NSNull is used throughout Foundation and other frameworks to skirt around the limitations of collections like NSArray and NSDictionary not being able to contain nil values. You can think of NSNull as effectively boxing the NULL or nil value so that it can be used in collections: NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionary]; mutableDictionary[@"someKey"] = [NSNull null]; // Sets value of NSNull singleton for `someKey` NSLog(@"Keys: %@", [mutableDictionary allKeys]); // @[@"someKey"] So to recap, here are the four values representing nothing that every Objective-C programmer should know about: Symbol Value Meaning NULL (void *)0 literal null value for C pointers nil (id)0 literal null value for Objective-C objects Nil (Class)0 literal null value for Objective-C classes NSNull [NSNull null] singleton object used to represent null BOOL / bool / Boolean / NSCFBoolean We’ve talked before about the philosophical and technical concerns of nothingness in programming. This week, our attention turns to another fundamental matter: Truth. Truth. Vēritās. The entire charter of Philosophy is founded upon the pursuit of it, and yet its exact meaning and implications still elude us. Does truth exist independently, or is it defined contingently against falsity? Can a proposition be at once both true and false? Is there absolute truth in anything, or is everything relative? Once again, encoding our logical universe into the cold, calculating bytecode of computers forces us to deal with these questions one way or another. And as you’ll see from our discussion of boolean types in Objective-C and its kin, truth is indeed stranger than fiction. Objective-C defines BOOL to encode truth value. It is a typedef of a signed char, with the macros YES and NO to represent true and false, respectively. Boolean values are used in conditionals, such as if or while statements, to conditionally perform logic or repeat execution. When evaluating a conditional statement, the value 0 is considered “false”, while any other value is considered “true”. Because NULL and nil are defined as 0, conditional statements on these nonexistent values are also evaluated as “false”. In Objective-C, use the BOOL type for parameters, properties, and instance variables dealing with truth values. When assigning literal values, use the YES and NO macros. The Wrong Answer to the Wrong Question Novice programmers often include an equality operator when evaluating conditionals: if ([a isEqual:b] == YES) { ... } Not only is this unnecessary, but depending on the left-hand value, it may also cause unexpected results, as described in the Big Nerd Ranch blog post, “BOOL’s Sharp Edges”: static BOOL different (int a, int b) { return a - b; } An overly clever C programmer might take some satisfaction in the simplicity of this approach: indeed, two integers are equal if and only if their difference is 0. However, because of the reality of BOOL being typedef‘d as a signed char, this will not behave as expected: if (different(11, 10) == YES) { printf ("11 != 10\n"); } else { printf ("11 == 10\n"); } if (different(10, 11) == YES) { printf ("10 != 11\n"); } else { printf ("10 == 11\n"); } if (different(512, 256) == YES) { printf ("512 != 256\n"); } else { printf ("512 == 256\n"); } This evaluates to: 11 != 10 10 == 11 512 == 256 Now, this might be acceptable for JavaScript, but Objective-C don’t suffer fools gladly. Deriving truth value directly from an arithmetic operation is never a good idea. Like the sentence “Colorless green ideas sleep furiously”, it may be grammatical (after all, BOOL is a signed char like any other, so it could be treated as a number), but it doesn’t make sense semantically. Instead, use the result of the == operator, or cast values into booleans with the ! (or !!) operator. The Truth About NSNumber and BOOL Pop quiz: what is the output of the following expression? NSLog(@"%@", [@(YES) class]); The answer: __NSCFBoolean Wait, what? All this time, we’ve been led to believe that NSNumber boxes primitives into an object representation. Any other integer- or float-derived NSNumber object shows its class to be __NSCFNumber. What gives? NSCFBoolean is a private class in the NSNumber class cluster. It is a bridge to the CFBooleanRef type, which is used to wrap boolean values for Core Foundation property lists and collections. CFBoolean defines the constants kCFBooleanTrue and kCFBooleanFalse. Because CFNumberRef and CFBoolean Ref are different types in Core Foundation, it makes sense that they are represented by different bridging classes in NSNumber. For most people, boolean values and boxed objects “just work”, and don’t really care what goes into making the sausage. But here at NSHipster, we’re all about sausages. So, to recap, here is a table of all of the truth types and values in Objective-C: Name Typedef Header True Value False Value BOOL signed char objc.h YES NO bool _Bool (int) stdbool.h true false Boolean unsigned char MacTypes.h TRUE FALSE NSNumber __NSCFBoolean Foundation.h @(YES) @(NO) CFBooleanRef struct CoreFoundation.h kCFBooleanTrue kCFBooleanFalse NS_ENUM & NS_OPTIONS When everything is an object, nothing is. So, there are a few ways you could parse that, but for the purposes of this article, this is all to say: sometimes it’s nice to be able to drop down to the C layer of things. Yes–that non-objective part of our favorite Smalltalk-inspired hybrid language, C can be a great asset. It’s fast, it’s battle-tested, it’s the very foundation of modern computing. But more than that, C is the escape hatch for when the Object-Oriented paradigm cracks under its own cognitive weight. Static functions are nicer than shoe-horned class methods. Enums are nicer than string constants. Bitmasks are nicer than arrays of string constants. Preprocessor directives are nicer than runtime hacks. A skilled Objective-C developer is able to gracefully switch between Objective and Procedural paradigms, and use each to their own advantage. And on that note, this week’s topic has to do with two simple-but-handy macros: NS_ENUM and NS_OPTIONS. Introduced in Foundation with iOS 6 / OS X Mountain Lion, the NS_ENUM and NS_OPTIONS macros are the new, preferred way to declare enum types. enum, or enumerated value types, are the C way to define constants for fixed values, like days of the week, or available styles of table view cells. In an enum declaration, constants without explicit values will automatically be assigned values sequentially, starting from 0. There are several legal ways that enums can be defined. What’s confusing is that there are subtle functional differences between each approach, and without knowing any better, someone is just as likely to use them interchangeably. For instance: enum { UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, UITableViewCellStyleSubtitle }; …declares integer values, but no type. Whereas: typedef enum { UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, UITableViewCellStyleSubtitle } UITableViewCellStyle; …defines the UITableViewCellStyle type, suitable for specifying the type of method parameters. However, Apple had previously defined all of their enum types as: typedef enum { UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, UITableViewCellStyleSubtitle }; typedef NSInteger UITableViewCellStyle; …which ensures a fixed size for UITableViewCellStyle, but does nothing to hint the relation between the aforementioned enum and the new type to the compiler. Thankfully, Apple has decided on “One Macro To Rule Them All” with NS_ENUM. NS_ENUM Now, UITableViewCellStyle is declared with: typedef NS_ENUM(NSInteger, UITableViewCellStyle) { UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, UITableViewCellStyleSubtitle }; The first argument for NS_ENUM is the type used to store the new type. In a 64-bit environment, UITable ViewCellStyle will be 8 bytes long–same as NSInteger. Make sure that the specified size can fit all of the defined values, or else an error will be generated. The second argument is the name of the new type (as you probably guessed). Inside the block, the values are defined as usual. This approach combines the best of all of the aforementioned approaches, and even provides hints to the compiler for type-checking and switch statement completeness. NS_OPTIONS enum can also be used to define a bitmask. Using a convenient property of binary math, a single integer value can encode a combination of values all at once using the bitwise OR (|), and decoded with bitwise AND (&). Each subsequent value, rather than automatically being incremented by 1 from 0, are manually given a value with a bit offset: 1 << 0, 1 << 1, 1 << 2, and so on. If you imagine the binary representation of a number, like 10110 for 22, each bit can be though to represent a single boolean. In UIKit, for example, UIViewAutoresizing is a bitmask that can represent any combination of flexible top, bottom, left, and right margins, or width and height. Rather than NS_ENUM, bitmasks should now use the NS_OPTIONS macro. The syntax is exactly the same as NS_ENUM, but this macro alerts the compiler to how values can be combined with bitmask |. Again, you must be careful that all of the enumerated values fit within the specified type. NS_ENUM and NS_OPTIONS are handy additions to the Objective-C development experience, and reaffirm the healthy dialectic between its objective and procedural nature. Keep this in mind as you move forward in your own journey to understand the logical tensions that underpin everything around us. Associated Objects #import <objc/runtime.h> Objective-C developers are conditioned to be wary of whatever follows this ominous incantation. And for good reason: messing with the Objective-C runtime changes the very fabric of reality for all of the code that runs on it. In the right hands, the functions of <objc/runtime.h> have the potential to add powerful new behavior to an application or framework, in ways that would otherwise not be possible. In the wrong hands, it drains the proverbial sanity meter of the code, and everything it may interact with (with terrifying side-effects). Therefore, it is with great trepidation that we consider this Faustian bargain, and look at one of the subjects most-often requested by NSHipster readers: associated objects. Associated Objects—or Associative References, as they were originally known—are a feature of the Objective-C 2.0 runtime, introduced in OS X Snow Leopard (available in iOS 4). The term refers to the following three C functions declared in <objc/runtime.h>, which allow objects to associate arbitrary values for keys at runtime: • objc_setAssociatedObject • objc_getAssociatedObject • objc_removeAssociatedObjects Why is this useful? It allows developers to add custom properties to existing classes in categories, which is an otherwise notable shortcoming for Objective-C. @interface NSObject (AssociatedObject) @property (nonatomic, strong) id associatedObject; @end @implementation NSObject (AssociatedObject) @dynamic associatedObject; - (void)setAssociatedObject:(id)object { objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (id)associatedObject { return objc_getAssociatedObject(self, @selector(associatedObject)); } It is often recommended that they key be a static char—or better yet, the pointer to one. Basically, an arbitrary value that is guaranteed to be constant, unique, and scoped for use within getters and setters: static char kAssociatedObjectKey; objc_getAssociatedObject(self, &kAssociatedObjectKey); However, a much simpler solution exists: just use a selector. Since SELs are guaranteed to be unique and constant, you can use _cmd as the key for objc_set AssociatedObject(). #objective-c #snowleopard — Bill Bumgarner (@bbum) August 28, 2009 Associative Object Behaviors Values can be associated onto objects according to the behaviors defined by the enumerated type objc_AssociationPolicy: Behavior @property Equivalent @property (assign) OBJC_ASSOCIATION_ASSIGN Description or @property Specifies a weak reference to the associated object. (unsafe_unretained) @property (nonatomic, OBJC_ASSOCIATION_RETAIN_NONATOMIC strong) Specifies a strong reference to the associated object, and that the association is not made atomically. OBJC_ASSOCIATION_COPY_NONATOMIC @property (nonatomic, copy) Specifies that the associated object is copied, and that the association is not made atomically. OBJC_ASSOCIATION_RETAIN @property (atomic, strong) Specifies a strong reference to the associated object, and that the association is made atomically. OBJC_ASSOCIATION_COPY @property (atomic, copy) Specifies that the associated object is copied, and that the association is made atomically. Weak associations to objects made with OBJC_ASSOCIATION_ASSIGN are not zero weak references, but rather follow a behavior similar to unsafe_unretained, which means that one should be cautious when accessing weakly associated objects within an implementation. According to the deallocation timeline described in WWDC 2011, Session 322 (~36:00), associated objects are erased surprisingly late in the object lifecycle — object_dispose(), which is invoked by NSObject -dealloc. Removing Values One may be tempted to call objc_removeAssociatedObjects() at some point in their foray into associated objects. However, as described in the documentation, it’s unlikely that you would have an occasion to invoke it yourself: The main purpose of this function is to make it easy to return an object to a “pristine state”. You should not use this function for general removal of associations from objects, since it also removes associations that other clients may have added to the object. Typically you should use objc_setAssociatedObject with a nil value to clear an association. Patterns Adding private variables to facilitate implementation details When extending the behavior of a built-in class, it may be necessary to keep track of additional state. This is the textbook use case for associated objects. Adding public properties to configure category behavior. Sometimes, it makes more sense to make category behavior more flexible with a property, than in a method parameter. In these situations, a public-facing property is an acceptable situation to use associated objects. Creating an associated observer for KVO When using KVO in a category implementation, it is recommended that a custom associated-object be used as an observer, rather than the object observing itself. Anti-Patterns Storing an associated object, when the value is not needed A common pattern for views is to create a convenience method that populates fields and attributes based on a model object or compound value. If that value does not need to be recalled later, it is acceptable, and indeed preferable, not to associate with that object. Storing an associated object, when the value can be inferred For example, one might be tempted to store a reference to a custom accessory view’s containing UITableViewCell, for use in tableView:accessoryButtonTappedForRowWithIndexPath:, when this can retrieved by calling cellForRowAtIndexPath:. Using associated objects instead of X …where X is any one the following: • Subclassing for when inheritance is a more reasonable fit than composition. • Target-Action for adding interaction events to responders. • Gesture Recognizers for any situations when target-action doesn’t suffice. • Delegation when behavior can be delegated to another object. • NSNotification & NSNotificationCenter for communicating events across a system in a looselycoupled way. Associated objects should be seen as a method of last resort, rather than a solution in search of a problem (and really, categories themselves really shouldn’t be at the top of the toolchain to begin with). Like any clever trick, hack, or workaround, there is a natural tendency for one to actively seek out occasions to use it—especially just after learning about it. Do your best to understand and appreciate when it’s the right solution, and save yourself the embarrassment of being scornfully asked “why in the name of $DEITY” you decided to go with that solution. Method Swizzling If you could blow up the world with the flick of a switch Would you do it? If you could make everybody poor just so you could be rich Would you do it? If you could watch everybody work while you just lay on your back Would you do it? If you could take all the love without giving any back Would you do it? And so we cannot know ourselves or what we’d really do… With all your power … What would you do? ― The Flaming Lips, “The Yeah Yeah Yeah Song (With All Your Power)” In last week’s article about associated objects, we began to explore the dark arts of the Objective-C runtime. This week, we venture further, to discuss what is perhaps the most contentious of runtime hackery techniques: method swizzling. Method swizzling is the process of changing the implementation of an existing selector. It’s a technique made possible by the fact that method invocations in Objective-C can be changed at runtime, by changing how selectors are mapped to underlying functions in a class’s dispatch table. For example, let’s say we wanted to track how many times each view controller is presented to a user in an iOS app: Each view controller could add tracking code to its own implementation of viewDidAppear:, but that would make for a ton of duplicated boilerplate code. Subclassing would be another possibility, but it would require subclassing UIViewController, UITableViewController, UINavigationController, and every other view controller class—an approach that would also suffer from code duplication. Fortunately, there is another way: method swizzling from a category. Here’s how to do it: #import <objc/runtime.h> @implementation UIViewController (Tracking) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL originalSelector = @selector(viewWillAppear:); SEL swizzledSelector = @selector(xxx_viewWillAppear:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); // When swizzling a class method, use the following: // Class class = object_getClass((id)self); … // Method originalMethod = class_getClassMethod(class, originalSelector); // Method swizzledMethod = class_getClassMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); } }); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } #pragma mark - Method Swizzling - (void)xxx_viewWillAppear:(BOOL)animated { [self xxx_viewWillAppear:animated]; NSLog(@"viewWillAppear: %@", self); } @end Now, when any instance of UIViewController, or one of its subclasses invokes viewWillAppear:, a log statement will print out. Injecting behavior into the view controller lifecycle, responder events, view drawing, or the Foundation networking stack are all good examples of how method swizzling can be used to great effect. There are a number of other occasions when swizzling would be an appropriate technique, and they become increasingly apparent the more seasoned an Objective-C developer becomes. Regardless of why or where one chooses to use swizzling, the how remains absolute: In computer science, pointer swizzling is the conversion of references based on name or position to direct pointer references. While the origins of Objective-C’s usage of the term are not entirely known, it’s understandable why it was co-opted, since method swizzling involves changing the reference of a function pointer by its selector. +load vs. +initialize Swizzling should always be done in +load. There are two methods that are automatically invoked by the Objective-C runtime for each class. +load is sent when the class is initially loaded, while +initialize is called just before the application calls its first method on that class or an instance of that class. Both are optional, and are executed only if the method is implemented. Because method swizzling affects global state, it is important to minimize the possibility of race conditions. +load is guaranteed to be loaded during class initialization, which provides a modicum of consistency for changing system-wide behavior. By contrast, +initialize provides no such guarantee of when it will be executed—in fact, it may never be called, if that class is never messaged directly by the app. dispatch_once Swizzling should always be done in a dispatch_once. Again, because swizzling changes global state, we need to take every precaution available to us in the runtime. Atomicity is one such precaution, as is a guarantee that code will be executed exactly once, even across different threads. Grand Central Dispatch’s dispatch_once provides both of these desirable behaviors, and should be considered as much a standard practice for swizzling as they are for initializing singletons. Selectors, Methods, & Implementations In Objective-C, selectors, methods, and implementations refer to particular aspects of the runtime, although in normal conversation, these terms are often used interchangeably to generally refer to the process of message sending. Here is how each is described in Apple’s Objective-C Runtime Reference: • Selector (typedef struct objc_selector *SEL): Selectors are used to represent the name of a method at runtime. A method selector is a C string that has been registered (or “mapped”) with the Objective-C runtime. Selectors generated by the compiler are automatically mapped by the runtime when the class is loaded . • Method (typedef struct objc_method *Method ): An opaque type that represents a method in a class definition. • Implementation (typedef id (*IMP)(id, SEL, ...)): This data type is a pointer to the start of the function that implements the method. This function uses standard C calling conventions as implemented for the current CPU architecture. The first argument is a pointer to self (that is, the memory for the particular instance of this class, or, for a class method, a pointer to the metaclass). The second argument is the method selector. The method arguments follow. The best way to understand the relationship between these concepts is as follows: a class (Class) maintains a dispatch table to resolve messages sent at runtime; each entry in the table is a method (Method), which keys a particular name, the selector (SEL), to an implementation (IMP), which is a pointer to an underlying C function. To swizzle a method is to change a class’s dispatch table in order to resolve messages from an existing selector to a different implementation, while aliasing the original method implementation to a new selector. Invoking _cmd It may appear that the following code will result in an infinite loop: - (void)xxx_viewWillAppear:(BOOL)animated { [self xxx_viewWillAppear:animated]; NSLog(@"viewWillAppear: %@", NSStringFromClass([self class])); } Surprisingly, it won’t. In the process of swizzling, xxx_viewWillAppear: has been reassigned to the original implementation of UIViewController -viewWillAppear:. It’s good programmer instinct for calling a method on self in its own implementation to raise a red flag, but in this case, it makes sense if we remember what’s really going on. However, if we were to call viewWillAppear: in this method, it would cause an infinite loop, since the implementation of this method will be swizzled to the viewWill Appear: selector at runtime. Remember to prefix your swizzled method name as you would any other contentious category method. Considerations Swizzling is widely considered a voodoo technique, prone to unpredictable behavior and unforeseen consequences. While it is not the safest thing to do, method swizzling is reasonably safe, when the following precautions are taken: • Always invoke the original implementation of a method (unless you have a good reason not to): APIs provide a contract for input and output, but the implementation in-between is a black box. Swizzling a method and not calling the original implementation may cause underlying assumptions about private state to break, along with the rest of your application. • Avoid collisions: Prefix category methods, and make damn well sure that nothing else in your code base (or any of your dependencies) are monkeying around with the same piece of functionality as you are. • Understand what’s going on: Simply copy-pasting swizzling code without understanding how it works is not only dangerous, but is a wasted opportunity to learn a lot about the Objective-C runtime. Read through the Objective-C Runtime Reference and browse <objc/runtime.h> to get a good sense of how and why things happen. Always endeavor to replace magical thinking with understanding. • Proceed with caution: No matter how confident you are about swizzling Foundation, UIKit, or any other built-in framework, know that everything could break in the next release. Be ready for that, and go the extra mile to ensure that in playing with fire, you don’t get NSBurned. Like associated objects, method swizzling is a powerful technique when you need it, but should be used sparingly. Objective-C Direct Methods It’s hard to get excited when new features come to Objective-C. These days, any such improvements are in service of Swift interoperability rather than an investment in the language itself (see nullability and lightweight generics). So it was surprising to learn about this recently merged patch to Clang, which adds a new direct dispatch mechanism to Objective-C methods. The genesis of this new language feature is unclear; the most we have to go on is an Apple-internal Radar number (2684889), which doesn’t tell us much beyond its relative age (sometime in the early ’00s, by our estimation). Fortunately, the feature landed with enough documentation and test coverage to get a good idea of how it works. (Kudos to implementor Pierre Habouzit, review manager John McCall, and the other LLVM contributors). This week on NSHipster, we’re taking this occasion to review Objective-C method dispatching and try to understand the potential impact of this new language feature on future codebases. (Direct methods could show up as early as Xcode 11.x, but most likely, it’ll be announced at WWDC 2020.) To understand the significance of direct methods, you need to know a few things about the Objective-C runtime. But let’s start our discussion one step before that, to the origin of OOP itself: Object-Oriented Programming Alan Kay coined the term “object-oriented programming in the late 1960s. With the help of Adele Goldberg, Dan Ingalls, and his other colleagues at Xerox PARC, Kay put this idea into practice in the ’70s with the creation of the Smalltalk programming language. (During this time, researches at Xerox PARC also developed the Xerox Alto, which would become the inspiration for Apple’s Macintosh and all other GUI computers.) In the 1980s, Brad Cox and Tom Love started work the first version of Objective-C, a language that sought to take the object-oriented paradigm of Smalltalk and implement it on solid fundamentals of C. Through a series of fortuitous events in the ’90s, the language would come to be the official language of NeXT, and later, Apple. For those of us who started learning Objective-C in the iPhone era, the language was often seen as yet another proprietary Apple technology — one of a myriad, obscure byproducts of the company’s “Not invented here” (NIH) culture. However, Objective-C isn’t just “an object-oriented C”, it’s one of the original object-oriented languages, with as strong a claim to OOP credentials as any other. Now, what does OOP mean? That’s a good question. ’90s era hype cycles have rendered the term almost meaningless. However, for our purposes today, let’s focus on something Alan Kay wrote in 1998: I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “messaging” […] > ― Alan Kay Dynamic Dispatch and the Objective-C Runtime In Objective-C, a program consists of a collection of objects that interact with each other by passing messages that, in turn, invoke methods, or functions. This act of message passing is denoted by square bracket syntax: [someObject aMethod:withAnArgument]; When Objective-C code is compiled, message sends are transformed into calls to a function called objc_msgSend (literally “send a message to some object with an argument”). objc_msgSend(object, @selector(message), withAnArgument); • The first argument is the receiver (self for instance methods) • The second argument is _cmd: the selector, or name of the method • Any method parameters are passed as additional function arguments objc_msgSend is responsible for determining which underlying implementation to call in response to this message, a process known as method dispatch. In Objective-C, each class (Class) maintains a dispatch table to resolve messages sent at runtime. Each entry in the dispatch table is a method (Method) that keys a selector (SEL) to a corresponding implementation (IMP), which is a pointer to a C function. When an object receives a message, it consults the dispatch table of its class. If it can find an implementation for the selector, the associated function is called. Otherwise, the object consults the dispatch table of its superclass. This continues up the inheritance chain until a match is found or the root class (NSObject) deems the selector to be unrecognized. (And that’s to say nothing of how Objective-C lets you do things like replace method implementations and create new classes dynamically at runtime. It’s absolutely wild what you can do.) If you think all of this indirection sounds like a lot of work… in a way, you’d be right! If you have a hot path in your code, an expensive method that’s called frequently, you could imagine some benefit to avoiding all of this indirection. To that end, some developers have used C functions as a way around dynamic dispatch. Direct Dispatch with a C Function As we saw with objc_msgSend, any method invocation can be represented by an equivalent function by passing implicit self as the first argument. For example, consider the following declaration of an Objective-C class with a conventional, dynamically-dispatched method. @interface MyClass: NSObject - (void)dynamicMethod; @end If a developer wanted to implement some functionality on MyClass without going through the whole message sending shebang, they could declare a static C function that took an instance of MyClass as an argument. static void directFunction(MyClass *__unsafe_unretained object); Here’s how each of these approaches translates to the call site: MyClass *object = [[[MyClass] alloc] init]; // Dynamic Dispatch [object dynamicMethod]; // Direct Dispatch directFunction(object); Direct Methods A direct method has the look and feel of a conventional method, but has the behavior of a C function. When a direct method is called, it directly calls its underlying implementation rather than going through objc_msgSend. With this new LLVM patch, you now have a way to annotate Objective-C methods to avoid participation in dynamic dispatch selectively. objc_direct, @property(direct), and objc_direct_members To make an instance or class method direct, you can mark it with the objc_direct Clang attribute. Likewise, the methods for an Objective-C property can be made direct by declaring it with the direct property attribute. @interface MyClass: NSObject @property(nonatomic) BOOL dynamicProperty; @property(nonatomic, direct) BOOL directProperty; - (void)dynamicMethod; - (void)directMethod __attribute__((objc_direct)); @end By our count, the addition of direct brings the total number of @property attributes to 16: • getter and setter • readwrite and readonly, • atomic and nonatomic • weak, strong, copy, retain, and unsafe_unretained • nullable, nonnullable, and null_resettable • class When an @interface for a category or class extension is annotated with the objc_direct_members attribute, all method and property declarations contained within it are considered to be direct, unless previously declared by that class. You can’t annotate the primary class interface with the objc_direct_members attribute. __attribute__((objc_direct_members)) @interface MyClass () @property (nonatomic) BOOL directExtensionProperty; - (void)directExtensionMethod; @end Annotating an @implementation with objc_direct_members has a similar effect, causing nonpreviously declared members to be deemed direct, including any implicit methods resulting from property synthesis. __attribute__((objc_direct_members)) @implementation MyClass - (BOOL)directProperty { … } - (void)dynamicMethod { … } - (void)directMethod { … } - (void)directExtensionMethod { … } - (void)directImplementationMethod { … } @end A dynamic method can’t be overridden in a subclass by a direct method, and a direct method can’t be overridden at all. Protocols can’t declare direct method requirements, and a class can’t implement a protocol requirement with a direct method. Applying these annotations to our example from before, we can see how direct and dynamic methods are indistinguishable at the call site: MyClass *object = [[[MyClass] alloc] init]; // Dynamic Dispatch [object dynamicMethod]; // Direct Dispatch [object directMethod]; Direct methods seem like a slam dunk feature for the performance-minded developers among us. But here’s the twist: In most cases, making a method direct probably won’t have a noticeable performance advantage. As it turns out, objc_msgSend is surprisingly fast. Thanks to aggressive caching, extensive low-level optimization, and intrinsic performance characteristics of modern processors, objc_msgSend has an extremely low overhead. We’re long past the days when iPhone hardware could reasonably be described as a resource-constrained environment. So unless Apple is preparing for a new embedded platform (AR glasses, anyone?), the most reasonable explanation we have for Apple implementing Objective-C direct methods in 2019 stems from something other than performance. Mike Ash is the Internet’s foremost expert on objc_msgSend. Over the years, his posts have provided the deepest and most complete understanding to the Objective-C runtime you’ll find outside of Cupertino. For the curious, “Dissecting objc_msgSend on ARM64” is a great place to start. Hidden Motives When an Objective-C method is marked as direct, its implementation has hidden visibility. That is, direct methods can only be called within the same module (or to be pedantic, linkage unit). It won’t even show up in the Objective-C runtime. Hidden visibility has two direct advantages: • Smaller binary size • No external invocation Without external visibility or a way to invoke them dynamically from the Objective-C runtime, direct methods are effectively private methods. If you want to participate in direct dispatch, but still want to make your API accessible externally, you can wrap it in a C function. static inline void performDirectMethod(MyClass *__unsafe_unretained object) { [object directMethod]; } While hidden visibility can be used by Apple to prevent swizzling and private API use, that doesn’t seem to be the primary motivation. According to Pierre, who implemented this feature, the main benefit of this optimization is code size reduction. Reportedly, the weight of unused Objective-C metadata can account for 5 – 10% of the __text section in the compiled binary. You could imagine that, from now until next year’s developer conference, a few engineers could go through each of the SDK frameworks, annotating private methods with objc_direct and private classes with objc_direct_members as a lightweight way to progressively tighten its SDK. If that’s true, then perhaps it’s just as well that we’ve become skeptical of new Objective-C features. When they’re not in service of Swift, they’re in service of Apple. Despite its important place in the history of programming and Apple itself, it’s hard not to see Objective-C as just that — history. Swift Equatable and Comparable Objective-C required us to wax philosophic about the nature of equality and identity. To the relief of any developer less inclined towards discursive treatises, this is not as much the case for Swift. In Swift, there’s the Equatable protocol, which explicitly defines the semantics of equality and inequality in a manner entirely separate from the question of identity. There’s also the Comparable protocol, which builds on Equatable to refine inequality semantics to creating an ordering of values. Together, the Equatable and Comparable protocols form the central point of comparison throughout the language. Equatable Values conforming to the Equatable protocol can be evaluated for equality and inequality. Conformance to Equatable requires the implementation of the equality operator (==). As an example, consider the following Binomen structure: struct Binomen { let genus: String let species: String } let let = Binomen(genus: "Canis", species: "lupus") = Binomen(genus: "Ursus", species: "arctos") We can add Equatable conformance through an extension, implementing the required type method for the == operator like so: extension Binomen: Equatable { static func == (lhs: Binomen, rhs: Binomen) -> Bool { return lhs.genus == rhs.genus && lhs.species == rhs.species } } == == // true // false Easy enough, right? Well actually, it’s even easier than that — as of Swift 4.1, the compiler can automatically synthesize conformance for structures whose stored properties all have types that are Equatable. We could replace all of the code in the extension by simply adopting Equatable in the declaration of Binomen: struct Binomen: Equatable { let genus: String let species: String } == == // true // false The Benefits of Being Equal Equatability isn’t just about using the == operator — there’s also the != operator! it also lets a value, among other things, be found in a collection and matched in a switch statement. [ , ].contains( ) // true func commonName(for binomen: Binomen) -> String? { switch binomen { case : return "gray wolf" case : return "brown bear" default: return nil } } commonName(for: ) // "gray wolf" Equatable is also a requirement for conformance to Hashable, another important type in Swift. This is all to say that if a type has equality semantics — if two values of that type can be considered equal or unequal – it should conform to Equatable. The Limits of Automatic Synthesis The Swift standard library and most of the frameworks in Apple SDKs do a great job adopting Equatable for types that make sense to be. So, in practice, you’re unlikely to be in a situation where the dereliction of a built-in type spoils automatic synthesis for your own type. Instead, the most common obstacle to automatic synthesis involves tuples. Consider this poorlyconsidered Trinomen type: struct Trinomen { let genus: String let species: (String, subspecies: String?) // } extension Trinomen: Equatable {} // Type 'Trinomen' does not conform to protocol 'Equatable' As described in our article about Void, tuples aren’t nominal types, so they can’t conform to Equatable. If you wanted to compare two trinomina for equality, you’d have to write the conformance code for Equatable. …like some kind of animal. Conditional Conformance to Equality In addition to automatic synthesis of Equatable, Swift 4.1 added another critical feature: conditional conformance. To illustrate this, consider the following generic type that represents a quantity of something: struct Quantity<Thing> { let count: Int let thing: Thing } Can Quantity conform to Equatable? We know that integers are equatable, so it really depends on what kind of Thing we’re talking about. What conditional conformance Swift 4.1 allows us to do is create an extension on a type with a conditional clause. We can use that here to programmatically express that _“a quantity of a thing is equatable if the thing itself is equatable”: extension Quantity: Equatable where Thing: Equatable {} And with that declaration alone, Swift has everything it needs to synthesize conditional Equatable conformance, allowing us to do the following: let oneHen = Quantity<Character>(count: 1, thing: " ") let twoDucks = Quantity<Character>(count: 2, thing: " ") oneHen == twoDucks // false Conditional conformance is the same mechanism that provides for an Array whose Element is Equatable to itself conform to Equatable: [ , ] == [ , ] // true Equality by Reference For reference types, the notion of equality becomes conflated with identity. It makes sense that two Name structures with the same values would be equal, but two Person objects can have the same name and still be different people. For Objective-C-compatible object types, the == operator is already provided from the isEqual: method: import Foundation class ObjCObject: NSObject {} ObjCObject() == ObjCObject() // false For Swift reference types (that is, classes), equality can be evaluated using the identity equality operator (===): class Object: Equatable { static func == (lhs: Object, rhs: Object) -> Bool { return lhs === rhs } } Object() == Object() // false That said, Equatable semantics for reference types are often not as straightforward as a straight identity check, so before you add conformance to all of your classes, ask yourself whether it actually makes sense to do so. Comparable Building on Equatable, the Comparable protocol allows for values to be considered less than or greater than other values. Comparable requires implementations for the following operators: Operator Name < Less than <= Less than or equal to >= Greater than or equal to > Greater than …so it’s surprising that you can get away with only implementing one of them: the < operator. Going back to our binomial nomenclature example, let’s extend Binomen to conform to Comparable such that values are ordered alphabetically first by their genus name and then by their species name: extension Binomen: Comparable { static func < (lhs: Binomen, rhs: Binomen) -> Bool { if lhs.genus != rhs.genus { return lhs.genus < rhs.genus } else { return lhs.species < rhs.species } } } > // true ("Ursus" lexicographically follows "Canis") Implementing the < operator for types that consider more than one property is deceptively hard to get right the first time. Be sure to write test cases to validate correct behavior. This is quite clever. Since the implementations of each comparison operator can be derived from just < and ==, all of that functionality is made available automatically through type inference. Contrast this with how Ruby and other languages derive equality and comparison operators from a single operator, <=> (a.k.a the “UFO operator”). A few pitches to bring formalized ordering to Swift have floated around over the years, such as this one, but we haven’t seen any real movement in this direction lately. Incomparable Limitations with No Equal Unlike Equatable, the Swift compiler can’t automatically synthesize conformance to Comparable. But that’s not for lack of trying — it’s just not possible. There are no implicit semantics for comparability that could be derived from the types of stored properties. If a type has more than one stored property, there’s no way to determine how they’re compared relative to one another. And even if a type had only a single property whose type was Comparable, there’s no guarantee how the ordering of that property would relate to the ordering of the value as a whole Comparable Benefits Conforming to Comparable confers a multitude of benefits. One such benefit is that arrays containing values of comparable types can call methods like sorted(), min(), and max(): let let let let = = = = Binomen(genus: Binomen(genus: Binomen(genus: Binomen(genus: "Tursiops", species: "truncatus") "Helianthus", species: "annuus") "Amanita", species: "muscaria") "Canis", species: "domesticus") let menagerie = [ , , , , , ] menagerie.sorted() // [ , , , , , menagerie.min() // menagerie.max() // ] Having a defined ordering also lets you create ranges, like so: let lessThan10 = ..<10 lessThan10.contains(1) // true lessThan10.contains(11) // false let oneToFive = 1...5 oneToFive.contains(3) // true oneToFive.contains(7) // false In the Swift standard library, Equatable is a type without an equal; Comparable a protocol without compare. Take care to adopt them in your own types as appropriate and you’ll benefit greatly. Hashable / Hasher 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: struct Color { let red: UInt8 let green: UInt8 let blue: UInt8 } To conform to Equatable, you had to provide an implementation for the == operator. To conform to Hashable, you had to provide an implementation of the computed hashValue property: // Swift < 4.1 extension Color: Equatable { static func ==(lhs: Color, rhs: Color) -> Bool { return lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue } } extension Color: Hashable { var hashValue: Int { return self.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.2 let cyan = Color(red: 0x00, green: 0xFF, blue: 0xFF) let yellow = Color(red: 0xFF, green: 0xFF, blue: 0x00) cyan.hashValue == yellow.hashValue // true, 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 — is now ⅓ of its original size: // Swift >= 4.1 struct Color: Hashable { let red: UInt8 let green: UInt8 let blue: 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.2 struct Color: Hashable { let red: UInt8 let green: UInt8 let blue: UInt8 // Synthesized by compiler func hash(into hasher: inout Hasher) { hasher.combine(self.red) hasher.combine(self.green) hasher.combine(self.blue) } } // Default implementation from protocol extension var hashValue: Int { var hasher = Hasher() self.hash(into: &hasher) return hasher.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.2 let cyan = Color(red: 0x00, green: 0xFF, blue: 0xFF) let yellow = 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. The challenge of programming analogies is they normalize antisocial behavior by way of edge cases. We excel as software engineers when we 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. Identifiable What constitutes the identity of an object? Philosophers have contemplated such matters throughout the ages. Whether it’s to do with reconstructed seafaring vessels from antiquity or spacefaring vessels from science fiction, questions of Ontology reveal our perception and judgment to be much less certain than we’d like to believe. Our humble publication has frequented this topic with some regularity, whether it was attempting to make sense of equality in Objective-C or appreciating the much clearer semantics of Swift vis-à-vis the Equatable protocol. Swift 5.1 gives us yet another occasion to ponder this old chestnut by virtue of the new Identifiable protocol. We’ll discuss the noumenon of this phenomenal addition to the standard library, and help you identify opportunities to realize its potential in your own projects. But let’s dispense with the navel gazing and jump right into some substance: Swift 5.1 adds the Identifiable protocol to the standard library, declared as follows: protocol Identifiable { associatedtype ID: Hashable var id: ID { get } } Values of types adopting the Identifiable protocol provide a stable identifier for the entities they represent. For example, a Parcel object may use the id property requirement to track the package en route to its final destination. No matter where the package goes, it can always be looked up by its id: import CoreLocation struct Parcel: Identifiable { let id: String var location: CLPlacemark? } Our first introduction to the Identifiable protocol actually came by way of SwiftUI; it’s thanks to the community that the type was brought into the fold of the standard library. Though as evidenced by GitHub search results, many of us were already working with Identifiable protocols of similar design… which prompts the question: When was the Identifiable protocol really introduced? The Swift Evolution proposal for Identifiable, SE-0261, was kept small and focused in order to be incorporated quickly. So, if you were to ask, “What do you actually get by conforming to Identifiable?”, the answer right now is “Not much.” As mentioned in the future directions, conformance to Identifiable has the potential to unlock simpler and/or more optimized versions of other functionality, such as the new ordered collection diffing APIs. But the question remains: “Why bother conforming to Identifiable?” The functionality you get from adopting Identifiable is primarily semantic, and require some more explanation. It’s sort of like asking, “Why bother conforming to Equatable?” And actually, that’s not a bad place to start. Let’s talk first about Equatable and its relation to Identifiable: Identifiable vs. Equatable Identifiable distinguishes the identity of an entity from its state. A parcel from our previous example will change locations frequently as it travels to its recipient. Yet a normal equality check (==) would fail the moment it leaves its sender: extension Parcel: Equatable {} var specialDelivery = Parcel(id: "123456789012") specialDelivery.location = CLPlacemark( location: CLLocation(latitude: 37.3327, longitude: -122.0053), name: "Cupertino, CA" ) specialDelivery == Parcel(id: "123456789012") // false specialDelivery.id == Parcel(id: "123456789012").id // true While this is an expected outcome from a small, contrived example, the very same behavior can lead to confusing results further down the stack, where you’re not as clear about how different parts work with one another. var trackedPackages: Set<Parcel> = … trackedPackages.contains(Parcel(id: "123456789012")) // false (?) On the subject of Set, let’s take a moment to talk about the Hashable protocol. Identifiable vs. Hashable In our article about Hashable, we described how Set and Dictionary use a calculated hash value to provide constant-time (O(1)) access to elements in a collection. Although the hash value used to bucket collection elements may bear a passing resemblance to identifiers, Hashable and Identifiable have some important distinctions in their underlying semantics: • Unlike identifiers, hash values are typically state-dependent, changing when an object is mutated. • Identifiers are stable across launches, whereas hash values are calculated by randomly generated hash seeds, making them unstable between launches. • Identifiers are unique, whereas hash values may collide, requiring additional equality checks when fetched from a collection. • Identifiers can be meaningful, whereas hash values are chaotic by virtue of their hashing functions. In short, hash values are similar to but no replacement for identifiers. So what makes for a good identifier, anyway? Choosing ID Types Aside from conforming to Hashable, Identifiable doesn’t make any other demands of its associated ID type requirement. So what are some good candidates? If you’re limited to only what’s available in the Swift standard library, your best options are Int and String. Include Foundation, and you expand your options with UUID and URL. Each has its own strengths and weaknesses as identifiers, and can be more or less suited to a particular situation: Int as ID The great thing about using integers as identifiers is that (at least on 64-bit systems), you’re unlikely to run out of them anytime soon. Most systems that use integers to identify records assign them in an auto-incrementing manner, such that each new ID is 1 more than the last one. Here’s a simple example of how you can do this in Swift: struct Widget: Identifiable { private static var idSequence = sequence(first: 1, next: {$0 + 1}) let id: Int } init?() { guard let id = Widget.idSequence.next() else { return nil} self.id = id } Widget()?.id // 1 Widget()?.id // 2 Widget()?.id // 3 If you wanted to guarantee uniqueness across launches, you might instead initialize the sequence with a value read from a persistent store like UserDefaults. And if you found yourself using this pattern extensively, you might consider factoring everything into a self-contained property wrapper. Monotonically increasing sequences have a lot of benefits, and they’re easy to implement. This kind of approach can provide unique identifiers for records, but only within the scope of the device on which the program is being run (and even then, we’re glossing over a lot with respect to concurrency and shared mutable state). If you want to ensure that an identifier is unique across every device that’s running your app, then congratulations —you’ve hit a fundamental problem in computer science. But before you start in on vector clocks and consensus algorithms, you’ll be relieved to know that there’s a much simpler solution: UUIDs. Insofar as this is a concern for your app, don’t expose serial identifiers to end users. Not only do you inadvertently disclose information about your system (“How many customers are there? Just sign up and check the user ID!”), but you open the door for unauthorized parties to enumerate all of the records in your system (“Just start at id = 1 and keep incrementing until a record doesn’t exist”). Granted, this is more of a concern for web apps, which often use primary keys in URLs, but it’s something to be aware of nonetheless. UUID as ID UUIDs, or universally unique identifiers, (mostly) sidestep the problem of consensus with probability. Each UUID stores 128 bits — minus 6 or 7 format bits, depending on the version — which, when randomly generated, make the chances of collision, or two UUIDs being generated with the same value, astronomically small. As discussed in a previous article, Foundation provides a built-in implementation of (version-4) UUIDs by way of the UUID type. Thus making adoption to Identifiable with UUIDs trivial: import Foundation struct Gadget: Identifiable { let id = UUID() } Gadget().id // 584FB4BA-0C1D-4107-9EE5-C555501F2077 Gadget().id // C9FECDCC-37B3-4AEE-A514-64F9F53E74BA Beyond minor ergonomic and cosmetic issues, UUID serves as an excellent alternative to Int for generated identifiers. However, your model may already be uniquely identified by a value, thereby obviating the need to generate a new one. Under such circumstances, that value is likely to be a String. On macOS, you can generate a random UUID from Terminal with the built-in uuidgen command: $ uuidgen 39C884B8-0A11-4B4F-9107-3AB909324DBA String as ID We use strings as identifiers all the time, whether it takes the form of a username or a checksum or a translation key or something else entirely. The main drawback to this approach is that, thanks to The Unicode® Standard, strings encode thousands of years of written human communication. So you’ll need a strategy for handling identifiers like “⽜”, “𐂌”, “”, and “ ” …and that’s to say nothing of the more pedestrian concerns, like leading and trailing whitespace and case-sensitivity! Normalization is the key to successfully using strings as identifiers. The easiest place to do this is in the initializer, but, again, if you find yourself repeating this code over and over, property wrappers can help you here, too. import Foundation fileprivate extension String { var nonEmpty: String? { isEmpty ? nil : self } } struct Whosit: Identifiable { let id: String init?(id: String) { guard let id = id.trimmingCharacters(in: CharacterSet.letters.inverted) .lowercased() .nonEmpty else { return nil } } } self.id = id Whosit(id: "Cow")?.id // cow Whosit(id: "--- cow ---")?.id // cow Whosit(id: " ") // nil URL as ID URLs (or URIs if you want to be pedantic) are arguably the most ubiquitous kind of identifier among all of the ones described in this article. Every day, billions of people around the world use URLs as a way to point to a particular part of the internet. So URLs a natural choice for an id value if your models already include them. URLs look like strings, but they use syntax to encode multiple components, like scheme, authority, path, query, and fragment. Although these formatting rules dispense with much of the invalid input you might otherwise have to consider for strings, they still share many of their complexities — with a few new ones, just for fun. The essential problem is that equivalent URLs may not be equal. Intrinsic, syntactic details like case sensitivity, the presence or absence of a trailing slash (/), and the order of query components all affect equality comparison. So do extrinsic, semantic concerns like a server’s policy to upgrade http to https, redirect from www to the apex domain, or replace an IP address with a which might cause different URLs to resolve to the same webpage. URL(string: "https://nshipster.com/?a=1&b=2")! == URL(string: "http://www.NSHipster.com?b=2&a=1")! // false try! Data(contentsOf: URL(string: "https://nshipster.com?a=1&b=2")!) == Data(contentsOf: URL(string: "http://www.NSHipster.com?b=2&a=1")!) // true Many of the same concerns apply to file URLs as well, which have the additional prevailing concern of resolving relative paths. If your model gets identifier URLs for records from a trusted source, then you may take URL equality as an article of faith; if you regard the server as the ultimate source of truth, it’s often best to follow their lead. But if you’re working with URLs in any other capacity, you’ll want to employ some combination of URL normalizations before using them as an identifier. Unfortunately, the Foundation framework doesn’t provide a single, suitable API for URL canonicalization, but URL and URLComponents provide enough on their own to let you roll your own (though we’ll leave that as an exercise for the reader): import Foundation fileprivate extension URL { var normalizedString: String { … } } struct Whatsit: Identifiable { let url: URL var id: { url.normalizedString } } Whatsit(url: "https://example.com/123").id // example.com/123 Whatsit(id: "http://Example.com/123/").id // example.com/123 Creating Custom Identifier ID Types UUID and URL both look like strings, but they use syntax rules to encode information in a structured way. And depending on your app’s particular domain, you may find other structured data types that would make for a suitable identifier. Thanks to the flexible design of the Identifiable protocol, there’s nothing to stop you from implementing your own ID type. For example, if you’re working in a retail space, you might create or repurpose an existing UPC type to serve as an identifier: struct UPC: Hashable { var digits: String … } struct Product: Identifiable { let id: UPC var name: String var price: Decimal } Three Forms of ID Requirements As Identifiable makes its way into codebases, you’re likely to see it used in one of three different ways: The newer the code, the more likely it will be for id to be a stored property — most often this will be declared as a constant (that is, with let): import Foundation // Style 1: id requirement fulfilled by stored property struct Product: Identifiable { let id: UUID } Older code that adopts Identifiable, by contrast, will most likely satisfy the id requirement with a computed property that returns an existing value to serve as a stable identifier. In this way, conformance to the new protocol is purely additive, and can be done in an extension: import Foundation struct Product { var uuid: UUID } // Style 2: id requirement fulfilled by computed property extension Product: Identifiable { var id { uuid } } If by coincidence the existing class or structure already has an id property, it can add conformance by simply declaring it in an extension (assuming that the property type conforms to Hashable). import Foundation struct Product { var id: UUID } // Style 3: id requirement fulfilled by existing property extension Product: Identifiable {} No matter which way you choose, you should find adopting Identifiable in a new or existing codebase to be straightforward and noninvasive. As we’ve said time and again, often it’s the smallest additions to the language and standard library that have the biggest impact on how we write code. (This speaks to the thoughtful, protocol-oriented design of Swift’s standard library.) Because what Identifiable does is kind of amazing: it extends reference semantics to value types. When you think about it, reference types and value types differ not in what information they encode, but rather how we treat them. For reference types, the stable identifier is the address in memory in which the object resides. This fact can be plainly observed by the default protocol implementation of id for AnyObject types: extension Identifiable where Self: AnyObject { var id: ObjectIdentifier { return ObjectIdentifier(self) } } Ever since Swift first came onto the scene, the popular fashion has been to eschew all reference types for value types. And this neophilic tendency has only intensified with the announcement of SwiftUI. But taking such a hard-line approach makes a value judgment of something better understood to be a difference in outlook. It’s no coincidence that much of the terminology of programming is shared by mathematics and philosophy. As developers, our work is to construct logical universes, after all. And in doing so, we’re regularly tasked with reconciling our own mental models against that of every other abstraction we encounter down the stack — down to the very way that we understand electricity and magnetism to work. Void Nothingness has been a regular topic of discussion on NSHipster, from our first article about nil in Objective-C to our recent look at the Never type in Swift. But today’s article is perhaps the most fraught with horror vacui of them all, as we gaze now into the Void of Swift. What is Void? In Swift, it’s nothing but an empty tuple. typealias Void = () We become aware of the Void as we fill it. let void: Void = () void. // No Completions Void values have no members: no methods, no values, not even a name. It’s a something more nothing than nil. For an empty vessel, Xcode gives us nothing more than empty advice. Something for Nothing Perhaps the most prominent and curious use of the Void type in the standard library is found in the ExpressibleByNilLiteral protocol. protocol ExpressibleByNilLiteral { init(nilLiteral: ()) } Types conforming to ExpressibleByNilLiteral can be initialized with the nil literal. Most types don’t adopt this protocol, as it makes more sense to represent the absence of a specified value using an Optional for that type. But you may encounter it occasionally. The required initializer for ExpressibleByNilLiteral shouldn’t take any real argument. (If it did, what would that even be?) However, the requirement can’t just be an empty initializer init() — that’s already used as the default initializer for many types. You could try to work around this by changing the requirement to a type method that returned a nil instance, but some mandatory internal state may be inaccessible outside of an initializer. But a better solution — and the one used here — is to add a nilLiteral label by way of a Void argument. It’s an ingenious use of existing functionality to achieve unconventional results. No Things Being Equal Tuples, along with metatypes (like Int.Type, the result of calling Int.self), function types (like (String) -> Bool) and existentials (like Encodable & Decodable), comprise non-nominal types. In contrast to the nominal, or named, types that comprise most of Swift, non-nominal types are defined in relation to other types. Non-nominal types cannot be extended. Void is an empty tuple, and because tuples are non-nominal types, you can’t add methods or properties or conformance to protocols. extension Void {} // Non-nominal type 'Void' cannot be extended Void doesn’t conform to Equatable — it simply can’t. Yet when we invoke the “is equal to” operator (==), it works as expected. void == void // true We reconcile this apparent contradiction with a global free-function, declared outside of any formal protocol. func == (lhs: (), rhs: ()) -> Bool { return true } This same treatment is given to the “is less than” operator (<), which acts as a stand-in for the Comparable protocol and its derived comparison operators. func < (lhs: (), rhs: ()) -> Bool { return false } The Swift standard library provides implementations of the comparison functions for tuples with arity, or size, up to 6. This is, however, considered a hack. At various times, the Swift core team has expressed interest in adding formal Equatable conformance for tuples, but at the time of writing, no formal proposals are being discussed. Ghost in the Shell Void, as a non-nominal type, can’t be extended. However, Void is still a type, and can, therefore, be used as a generic constraint. For example, consider this generic container for a single value: struct Wrapper<Value> { let value: Value } We can first take advantage of conditional conformance, arguably the killer feature in Swift 4.1, to extend Wrapper to adopt Equatable when it wraps a value that is itself Equatable. extension Wrapper: Equatable where Value: Equatable { static func ==(lhs: Wrapper<Value>, rhs: Wrapper<Value>) -> Bool { return lhs.value == rhs.value } } Using the same trick from before, we can approximate Equatable behavior by implementing a top-level == function that takes Wrapper<Void> arguments. func ==(lhs: Wrapper<Void>, rhs: Wrapper<Void>) -> Bool { return true } In doing so, we can now successfully compare two constructed wrappers around Void values. Wrapper(value: void) == Wrapper(value: void) // true However, if we attempt to assign such a wrapped value to a variable, the compiler generates a mysterious error. let wrapperOfVoid = Wrapper<Void>(value: void) // error: Couldn't apply expression side effects : // Couldn't dematerialize wrapperOfVoid: corresponding symbol wasn't found The horror of the Void becomes once again its own inverted retort. The Phantom Type Even when you dare not speak its non-nominal name, there is no escaping Void. Any function declaration with no explicit return value implicitly returns Void func doSomething() { ... } // Is equivalent to func doSomething() -> Void { ... } This behavior is curious, but not particularly useful, and the compiler will generate a warning if you attempt to assign a variable to the result of a function that returns Void. doSomething() // no warning let result = doSomething() // ️ Constant 'result' inferred to have type 'Void', which may be unexpected You can silence this warning by explicitly specifying the Void type. let result: Void = doSomething() // () By contrast, functions that return non-Void values generate warnings if you don’t assign their return value. For more details, see SE-0047 “Defaulting non-Void functions so they warn on unused results”. Trying to Return from the Void If you squint at Void? long enough, you might start to mistake it for Bool. These types are isometric, both having exactly two states: true / .some(()) and false / .none. But isometry doesn’t imply equivalence. The most glaring difference between the two is that Bool is ExpressibleByBooleanLiteral, whereas Void isn’t — and can’t be, for the same reasons that it’s not Equatable. So you can’t do this: (true as Void?) // error Void may be the spookiest type in Swift, but Bool gives it a run for its money when typealias’d to Booooooool . But hard-pressed, Void? can act in the same way as Bool. Consider the following function that randomly throws an error: struct Failure: Error {} func failsRandomly() throws { if Bool.random() { throw Failure() } } The correct way to use this method is to call it within a do / catch block using a try expression. do { try failsRandomly() // executes on success } catch { // executes on failure } The incorrect-but-ostensibly-valid way to do this would be to exploit the fact that failsRandomly() implicitly returns Void. The try? expression transforms the result of a statement that throws into an optional value, which in the case of failsRandomly(), results in Void?. If Void? has .some value (that is, != nil), that means the method returned without throwing an error. If success is nil, then we know that the method produced an error. let success: Void? = try? failsRandomly() if success != nil { // executes on success } else { // executes on failure } As much as you may dislike the ceremony of do / catch blocks, you have to admit that they’re a lot prettier than what’s happening here. It’s a stretch, but this approach might be valid in very particular and peculiar situations. For example, you might use a static property on a class to lazily produce some kind of side effect exactly once using a self-evaluating closure: static var oneTimeSideEffect: Void? = { return try? data.write(to: fileURL) }() Even still, an Error or Bool value would probably be more appropriate. Things that go “Clang” in the Night If, while reading this chilling account you start to shiver, you can channel the necrotic energy of the Void type to conjure immense amounts of heat to warm your spirits. …which is to say, the following code causes lldb-rpc-server to max out your CPU: extension Optional: ExpressibleByBooleanLiteral where Wrapped == Void { public typealias BooleanLiteralType = Bool } public init(booleanLiteral value: Bool) { if value { self.init(())! } else { self.init(nilLiteral: ())! } } let pseudoBool: Void? = true // we never get to find out Keeping in the Lovecraft-ian tradition, Void has a physical form that the computer is incapable of processing; simply witnessing it renders the process incurably insane. A Hollow Victory Let’s conclude our hallowed study of Void with a familiar refrain. enum Result<Value, Error> { case success(Value) case failure(Error) } If you recall our article about the Never type, a Result type with its Error type set to Never can be used to represent operations that always succeed. In a similar way, we might use Void as the Value type to represent operations that, when they succeed, don’t produce any meaningful result. For example, apps may implement tell-tale heartbeat by regularly “pinging” a server with a simple network request. func ping(_ url: URL, completion: (Result<Void, Error>) -> Void) { … } According to HTTP semantics, the correct status code for a hypothetical /ping endpoint would be 204 No Content. In the completion handler of the request, success would be indicated by the following call: completion(.success(())) Not enamored with effusive parentheticals (but why not?), you could make thing a bit nicer through a strategic extension on Result. extension Result where Value == Void { static var success: Result { return .success(()) } } Nothing lost; nothing gained. completion(.success) It may seem like a purely academic exercise — philosophical, even. But an investigation into the Void yields deep insights into the very fabric of reality for the Swift programming language. In ancient times, long before Swift had seen the light of day, tuples played a fundamental role in the language. They represented argument lists and associated enumeration values, fluidly moving between its different contexts. At some point that model broke down. And the language has yet to reconcile the incongruities between these disparate constructs. So according to Swift Mythos, Void would be the paragon of the elder gods: the true singleton, blind nullary at the center of infinity unaware of its role or influence; the compiler unable to behold it. But perhaps this is all just an invention at the periphery of our understanding — a manifestation of our misgivings about the long-term viability of the language. After all, when you stare into the Void, the Void stares back into you. Never “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 call site. // Swift < 3.0 @noreturn func fatalError(_ 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.0 func fatalError(_ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line) -> Never For a type to replace the functionality of 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: enum Never {} 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 throughout the Swift standard library to do things like namespace functionality and reason about types. 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. (Because how could they?) 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: enum Result<Value, Error> { case success(Value) case failure(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 wrap either a response and data or an error: func fetch(_ request: URLRequest, completion: (Result<(URLResponse, Data), Error>) -> Void) { … } When calling that method, you’d switch over result to handle .success and .failure separately: fetch(request) { result in switch result { case let .success(response, _): print("Success: \(response)") case .failure(let error): print("Failure: \(error)") } } Now consider a function that’s guaranteed to always return a successful result in its completion handler: func alwaysSucceeds(_ 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’t need to handle .failure for the switch statement to be exhaustive: alwaysSucceeds { (result) in switch result { case .success(let string): print(string) } } You can see this effect played out to its logical extreme in the implementation conforming Never to Comparable: extension Never: Comparable { public static func < (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: let array: [Int] let firstIem = array.first! To avoid force-unwrapping, you could use a guard statement with conditional assignment instead: let array: [Int] guard let firstItem = array.first else { 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? let firstItem = 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 { switch lhs { case let value?: return value case nil: 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? let firstItem = array.first ?? throw Error.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? func neverThrows() 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. OptionSet Objective-C uses the NS_OPTIONS macro to define option types, or sets of values that may be combined together. For example, values in the UIViewAutoresizing type in UIKit can be combined with the bitwise OR operator (|) and passed to the autoresizingMask property of a UIView to specify which margins and dimensions should automatically resize: typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) { UIViewAutoresizingNone = 0, UIViewAutoresizingFlexibleLeftMargin = 1 << 0, UIViewAutoresizingFlexibleWidth = 1 << 1, UIViewAutoresizingFlexibleRightMargin = 1 << 2, UIViewAutoresizingFlexibleTopMargin = 1 << 3, UIViewAutoresizingFlexibleHeight = 1 << 4, UIViewAutoresizingFlexibleBottomMargin = 1 << 5 }; Swift imports this and other types defined using the NS_OPTIONS macro as a structure that conforms to the OptionSet protocol. extension UIView { struct AutoresizingMask: OptionSet { init(rawValue: UInt) } } static static static static static static var var var var var var flexibleLeftMargin: UIView.AutoresizingMask flexibleWidth: UIView.AutoresizingMask flexibleRightMargin: UIView.AutoresizingMask flexibleTopMargin: UIView.AutoresizingMask flexibleHeight: UIView.AutoresizingMask flexibleBottomMargin: UIView.AutoresizingMask The renaming and nesting of imported types are the result of a separate mechanism. At the time OptionSet was introduced (and RawOptionSetType before it), this was the best encapsulation that the language could provide. Towards the end of this article, we’ll demonstrate how to take advantage of language features added in Swift 4.2 to improve upon OptionSet. …but that’s getting ahead of ourselves. This week on NSHipster, let’s take a by-the-books look at using imported OptionSet types, and how you can create your own. After that, we’ll offer a different option for setting options. Working with Imported Option Set Types According to the documentation, there are over 300 types in Apple SDKs that conform to OptionSet, from ARHitTestResult.ResultType to XMLNode.Options. No matter which one you’re working with, the way you use them is always the same: To specify a single option, pass it directly (Swift can infer the type when setting a property so you can omit everything up to the leading dot): view.autoresizingMask = .flexibleHeight OptionSet conforms to the SetAlgebra protocol, so to you can specify multiple options with an array literal — no bitwise operations required: view.autoresizingMask = [.flexibleHeight, .flexibleWidth] To specify no options, pass an empty array literal ([]): view.autoresizingMask = [] // no options Declaring Your Own Option Set Types You might consider creating your own option set type if you have a property that stores combinations from a closed set of values and you want that combination to be stored efficiently using a bitset. To do this, declare a new structure that adopts the OptionSet protocol with a required rawValue instance property and type properties for each of the values you wish to represent. The raw values of these are initialized with increasing powers of 2, which can be constructed using the left bitshift (<<) operation with incrementing right-hand side values. You can also specify named aliases for specific combinations of values. For example, here’s how you might represent topping options for a pizza: struct Toppings: OptionSet { let rawValue: Int static static static static static static } let let let let let let pepperoni onions bacon extraCheese greenPeppers pineapple = = = = = = Toppings(rawValue: Toppings(rawValue: Toppings(rawValue: Toppings(rawValue: Toppings(rawValue: Toppings(rawValue: 1 1 1 1 1 1 << << << << << << 0) 1) 2) 3) 4) 5) static let meatLovers: Toppings = [.pepperoni, .bacon] static let hawaiian: Toppings = [.pineapple, .bacon] static let all: Toppings = [ .pepperoni, .onions, .bacon, .extraCheese, .greenPeppers, .pineapple ] Taken into a larger example for context: struct Pizza { enum Style { case neapolitan, sicilian, newHaven, deepDish } struct Toppings: OptionSet { ... } let diameter: Int let style: Style let toppings: Toppings } init(inchesInDiameter diameter: Int, style: Style, toppings: Toppings = []) { self.diameter = diameter self.style = style self.toppings = toppings } let dinner = Pizza(inchesInDiameter: 12, style: .neapolitan, toppings: [.greenPeppers, .pineapple]) Another advantage of OptionSet conforming to SetAlgebra is that you can perform set operations like determining membership, inserting and removing elements, and forming unions and intersections. This makes it easy to, for example, determine whether the pizza toppings are vegetarian-friendly: extension Pizza { var isVegetarian: Bool { return toppings.isDisjoint(with: [.pepperoni, .bacon]) } } dinner.isVegetarian // true A Fresh Take on an Old Classic Alright, now that you know how to use OptionSet, let’s show you how not to use OptionSet. As we mentioned before, new language features in Swift 4.2 make it possible to have our cake pizza pie and eat it too. First, declare a new Option protocol that inherits RawRepresentable, Hashable, and CaseIterable. protocol Option: RawRepresentable, Hashable, CaseIterable {} Next, declare an enumeration with String raw values that adopts the Option protocol: enum Topping: String, Option { case pepperoni, onions, bacon, extraCheese, greenPeppers, pineapple } Compare the structure declaration from before to the following enumeration. Much nicer, right? Just wait — it gets even better. Automatic synthesis of Hashable provides effortless usage with Set, which gets us halfway to the functionality of OptionSet. Using conditional conformance, we can create an extension for any Set whose element is a Topping and define our named topping combos. As a bonus, CaseIterable makes it easy to order a pizza with “the works”: extension Set where Element == Topping { static var meatLovers: Set<Topping> { return [.pepperoni, .bacon] } static var hawaiian: Set<Topping> { return [.pineapple, .bacon] } } static var all: Set<Topping> { return Set(Element.allCases) } typealias Toppings = Set<Topping> And that’s not all CaseIterable has up its sleeves; by enumerating over the allCases type property, we can automatically generate the bitset values for each case, which we can combine to produce the equivalent rawValue for any Set containing Option types: extension Set where Element: Option { var rawValue: Int { var rawValue = 0 for (index, element) in Element.allCases.enumerated() { if self.contains(element) { rawValue |= (1 << index) } } } } return rawValue Because OptionSet and Set both conform to SetAlgebra our new Topping implementation can be swapped in for the original one without needing to change anything about the Pizza itself. This approach assumes that the order of cases provided by CaseIterable is stable across launches. If it isn’t, the generated raw value for combinations of options may be inconsistent. So, to summarize: you’re likely to encounter OptionSet when working with Apple SDKs in Swift. And although you could create your own structure that conforms to OptionSet, you probably don’t need to. You could use the fancy approach outlined at the end of this article, or do with something more straightforward. Whichever option you choose, you should be all set. numericCast(_:) Everyone has their favorite analogy to describe programming. It’s woodworking or it’s knitting or it’s gardening. Or maybe it’s problem solving and storytelling and making art. That programming is like writing there is no doubt; the question is whether it’s poetry or prose. And if programming is like music, it’s always jazz for whatever reason. But perhaps the closest point of comparison for what we do all day comes from Middle Eastern folk tales: Open any edition of The Thousand and One Nights ( )أَْلف لَْيَلة َولَْيَلةand you’ll find descriptions of supernatural beings known as jinn, djinn, genies, or . No matter what you call them, you’re certainly familiar with their habit of granting wishes, and the misfortune that inevitably causes. In many ways, computers are the physical embodiment of metaphysical wish fulfillment. Like a genie, a computer will happily go along with whatever you tell it to do, with no regard for what your actual intent may have been. And by the time you’ve realized your error, it may be too late to do anything about it. As a Swift developer, there’s a good chance that you’ve been hit by integer type conversion errors and thought “I wish these warnings would go away and my code would finally compile.” If that sounds familiar, you’ll happy to learn about numericCast(_:), a small utility function in the Swift Standard Library that may be exactly what you were hoping for. But be careful what you wish for, it might just come true. Let’s start by dispelling any magical thinking about what numericCast(_:) does by looking at its implementation: func numericCast<T: BinaryInteger, U: BinaryInteger>(_ x: T) -> U { return U(x) } (As we learned in our article about Never, even the smallest amount of Swift code can have a big impact.) The BinaryInteger protocol was introduced in Swift 4 as part of an overhaul to how numbers work in the language. It provides a unified interface for working with integers, both signed and unsigned, and of all shapes and sizes. When you convert an integer value to another type, it’s possible that the value can’t be represented by that type. This happens when you try to convert a signed integer to an unsigned integer (for example, -42 as a UInt), or when a value exceeds the representable range of the destination type (for example, UInt8 can only represent numbers between 0 and 255). BinaryInteger defines four strategies of conversion between integer types, each with different behaviors for handling out-of-range values: • Range-Checked Conversion - init(_:): Trigger a runtime error for out-of-range values • Exact Conversion - init?(exactly:): Return nil for out-of-range values • Clamping Conversion - init(clamping:): Use the closest representable value for out-ofrange values • Bit Pattern Conversion - init(truncatingIfNeeded:): Truncate to the width of the target integer type The correct conversion strategy depends on the situation in which it’s being used. Sometimes it’s desireable to clamp values to a representable range; other times, it’s better to get no value at all. In the case of numericCast(_:), range-checked conversion is used for convenience. The downside is that calling this function with out-of-range values causes a runtime error (specifically, it traps on overflow in -O and -Onone). For more information about the changes to how numbers work in Swift 4, see SE-0104: “Protocoloriented integers”. This subject is also discussed at length in the Flight School Guide to Numbers. Thinking Literally, Thinking Critically Before we go any further, let’s take a moment to talk about integer literals. As we’ve discussed in previous articles, Swift provides a convenient and extensible way to represent values in source code. When used in combination with the language’s use of type inference, things often “just work” …which is nice and all, but can be confusing when things “just don’t”. Consider the following example in which arrays of signed and unsigned integers are initialized from identical literal values: let arrayOfInt: [Int] = [1, 2, 3] let arrayOfUInt: [UInt] = [1, 2, 3] Despite their seeming equivalence, we can’t, for example, do this: arrayOfInt as [UInt] // Error: Cannot convert value of type '[Int]' to type '[UInt]' in coercion One way to reconcile this issue would be to pass the numericCast function as an argument to map(_:): arrayOfInt.map(numericCast) as [UInt] This is equivalent to passing the UInt range-checked initializer directly: arrayOfInt.map(UInt.init) But let’s take another look at that example, this time using slightly different values: let arrayOfNegativeInt: [Int] = [-1, -2, -3] arrayOfNegativeInt.map(numericCast) as [UInt] // is not representable Fatal error: Negative value As a run-time approximation of compile-time type functionality numericCast(_:) is closer to as! than as or as?. Compare this to what happens if you instead pass the exact conversion initializer, init?(exactly:): let arrayOfNegativeInt: [Int] = [-1, -2, -3] arrayOfNegativeInt.map(UInt.init(exactly:)) // [nil, nil, nil] numericCast(_:), like its underlying range-checked conversion, is a blunt instrument, and it’s important to understand what tradeoffs you’re making when you decide to use it. The Cost of Being Right In Swift, the general guidance is to use Int for integer values (and Double for floating-point values) unless there’s a really good reason to use a more specific type. Even though the count of a Collection is nonnegative by definition, we use Int instead of UInt because the cost of going back and forth between types when interacting with other APIs outweighs the potential benefit of a more precise type. For the same reason, it’s almost always better to represent even small numbers, like weekday numbers, with an Int, despite the fact that any possible value would fit into an 8-bit integer with plenty of room to spare. The best argument for this practice is a 5-minute conversation with a C API from Swift. Older and lower-level C APIs are rife with architecture-dependent type definitions and finely-tuned value storage. On their own, they’re manageable. But on top of all the other inter-operability woes like headers to pointers, they can be a breaking point for some (and I don’t mean the debugging kind). numericCast(_:) is there for when you’re tired of seeing red and just want to get things to compile. Random Acts of Compiling The example in the official docs should be familiar to many of us: Prior to SE-0202, the standard practice for generating numbers in Swift (on Apple platforms) involved importing the Darwin framework and calling the arc4random_uniform(3) function: uint32_t arc4random_uniform(uint32_t __upper_bound) arc4random requires not one but two separate type conversions in Swift: first for the upper bound parameter (Int → UInt32) and second for the return value (UInt32 → Int): import Darwin func random(in range: Range<Int>) -> Int { return Int(arc4random_uniform(UInt32(range.count))) + range.lowerBound } Gross. By using numericCast(_:), we can make things a little more readable, albeit longer: import Darwin func random(in range: Range<Int>) -> Int { return numericCast(arc4random_uniform(numericCast(range.count))) + range.lowerBound } numericCast(_:) isn’t doing anything here that couldn’t otherwise be accomplished with typeappropriate initializers. Instead, it serves as an indicator that the conversion is perfunctory — the minimum of what’s necessary to get the code to compile. But as we’ve learned from our run-ins with genies, we should be careful what we wish for. Upon closer inspection, it’s apparent that the example usage of numericCast(_:) has a critical flaw: it traps on values that exceed UInt32.max! random(in: 0..<0x1_0000_0000) // the passed value Fatal error: Not enough bits to represent If we look at the Standard Library implementation that now lets us do Int.random(in: 0...10), we’ll see that it uses clamping, rather than range-checked, conversion. And instead of delegating to a convenience function like arc4random_uniform, it populates values from a buffer of random bytes. Getting code to compile is different than doing things correctly. But sometimes it takes the former to ultimately get to the latter. When used judiciously, numericCast(_:) is a convenient tool to resolve issues quickly. It also has the added benefit of signaling potential misbehavior more clearly than a conventional type initializer. Ultimately, programming is about describing exactly what we want — often with painstaking detail. There’s no genie-equivalent CPU instruction for “Do the Right Thing” (and even if there was, would we really trust it?) Fortunately for us, Swift allows us to do this in a way that’s safer and more concise than many other languages. And honestly, who could wish for anything more? guard & defer “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” It’s a shame that his essay is most remembered for popularizing the “____ Consider Harmful” meme among programmers and their ill-considered online diatribes. Because (as usual) Dijkstra was making an excellent point: the structure of code should reflect its behavior. Swift 2.0 introduced two new control statements that aimed to simplify and streamline the programs we write: guard and defer. While the former by its nature makes our code more linear, the latter does the opposite by delaying 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 guard is a conditional statement requires an expression to evaluate to true for execution to continue. If the expression is false, the mandatory else clause is executed instead. func sayHello(numberOfTimes: Int) { guard numberOfTimes > 0 else { return } } for _ in 1...numberOfTimes { print("Hello!") } The else clause in a guard statement must exit the current scope by using return to leave a function, continue or break to get out of a loop, or a function that returns Never like fatalError(_:file :line:). guard statements are most useful when combined with optional bindings. Any new optional bindings created in a guard statement’s condition are available for the rest of the function or block. Compare how optional binding works with a guard-let statement to an if-let statement: var name: String? if let name = name { // name is nonoptional inside (name is String) } // name is optional outside (name is String?) guard let name = name else { return } // name is nonoptional from now on (name is String) 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. for imageName in imageNamesList { guard let image = UIImage(named: imageName) else { continue } } // do something with image Guarding Against Excessive Indentation and Errors Let’s take a before-and-after look at how guard can improve our code and help prevent errors. As an example, we’ll implement a readBedtimeStory() function: enum StoryError: Error { case missing case illegible case tooScary } func readBedtimeStory() throws { if let url = Bundle.main.url(forResource: "book", withExtension: "txt") { if let data = try? Data(contentsOf: url), let story = String(data: data, encoding: .utf8) { if story.contains(" ") { throw StoryError.tooScary } else { print("Once upon a time... \(story)") } } else { throw StoryError.illegible } } else { throw StoryError.missing } } To read a bedtime story, we need to be able to find the book, the storybook must be decipherable, and the story can’t be too scary (no monsters at the end of this book, please and thank you!). But note how far apart the throw statements are from the checks themselves. To find out what happens when you can’t find book.txt, you need to read all the way to the bottom of the method. Like a good book, code should tell a story: with an easy-to-follow plot, and clear a beginning, middle, and end. (Just try not to write too much code in the “post-modern” genre). Strategic use of guard statements allow us to organize our code to read more linearly. func readBedtimeStory() throws { guard let url = Bundle.main.url(forResource: "book", withExtension: "txt") else { throw StoryError.missing } guard let data = try? Data(contentsOf: url), let story = String(data: data, encoding: .utf8) else { throw StoryError.illegible } if story.contains(" ") { throw StoryError.tooScary } } print("Once upon a time... \(story)") Much better! Each error case is handled as soon as it’s checked, so we can follow the flow of execution straight down the left-hand side. Don’t Not Guard Against Double Negatives One habit to guard against as you embrace this new control flow mechanism is overuse — particularly when the evaluated condition is already negated. For example, if you want to return early if a string is empty, don’t write: // Huh? guard !string.isEmpty else { return } Keep it simple. Go with the (control) flow. Avoid the double negative. // Aha! if string.isEmpty { return } defer Between guard and the new throw statement for error handling, Swift encourages 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 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 the following function that wraps a system call to gethostname(2) to return the current hostname of the system: import Darwin func currentHostName() -> String { let capacity = Int(NI_MAXHOST) let buffer = UnsafeMutablePointer<Int8>.allocate(capacity: capacity) guard gethostname(buffer, capacity) == 0 else { buffer.deallocate() return "localhost" } let hostname = String(cString: buffer) buffer.deallocate() } return hostname Here, we allocate an UnsafeMutablePointer<Int8> early on but we need to remember to deallocate it both in the failure condition and once we’re finished with the buffer. Error prone? Yes. Frustratingly repetitive? Check. By using a defer statement, we can remove the potential for programmer error and simplify our code: func currentHostName() -> String { let capacity = Int(NI_MAXHOST) let buffer = UnsafeMutablePointer<Int8>.allocate(capacity: capacity) defer { buffer.deallocate() } guard gethostname(buffer, capacity) == 0 else { return "localhost" } } return String(cString: buffer) Even though defer comes immediately after the call to allocate(capacity), its execution is delayed until the end of the current scope. Thanks to defer, buffer will be properly deallocated regardless of where the function returns. Consider using defer whenever an API requires calls to be balanced, such as allocate(capacity:) / deallocate(), wait() / signal(), or open() / close(). This way, you not only eliminate a potential source of programmer error, but make Dijkstra proud. “Goed gedaan!” he’d say, in his native Dutch. Deferring Frequently If you use multiple defer statements in the same scope, they’re executed in reverse order of appearance — like a stack. 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. For example, running the following code prints the output below: func procrastinate() { defer { print("wash the dishes") } defer { print("take out the recycling") } defer { print("clean the refrigerator") } } print("play videogames") play videogames clean the refrigerator take out the recycling wash the dishes What happens if you nest defer statements, like this? defer { defer { print("clean the gutter") } } Your first thought might be that it pushes the statement to the very bottom of the stack. But that’s not what happens. Think it through, and then test your hypothesis in a Playground. Deferring Judgement If a variable is referenced in the body of a defer statement, its final value is evaluated. That is to say: defer blocks don’t capture the current value of a variable. If you run this next code sample, you’ll get the output that follows: func flipFlop() { var position = "It's pronounced /ɡɪf/" defer { print(position) } } position = "It's pronounced /dʒɪf/" defer { print(position) } It's pronounced /dʒɪf/ It's pronounced /dʒɪf/ Deferring Demurely Another thing to keep in mind is that defer blocks can’t break out of their scope. So if you try to call a method that can throw, the error can’t be passed to the surrounding context. func burnAfterReading(file url: URL) throws { defer { try FileManager.default.removeItem(at: url) } // Errors not handled } let string = try String(contentsOf: url) Instead, you can either ignore the error by using try? or simply move the statement out of the defer block and at the end of the function to execute conventionally. (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: postfix func ++(inout x: Int) -> Int { let current = x x += 1 return current } In this case, defer offers a clever alternative. Why create a temporary variable when we can just defer the increment? postfix func ++(inout x: Int) -> Int { defer { x += 1 } return x } 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 prevent confusion and obscurity. Mirror / CustomReflectable / CustomLeafReflectable Reflection in Swift is a limited affair, providing read-only access to information about objects. In fact, this functionality might be better described as introspection rather than reflection. But can you really blame them for going with that terminology? “Introspection” is such a bloviated and acoustically unpleasant word compared to “Reflection.” And how could anyone pass on the nominal slam dunk of calling the type responsible for reflecting an object’s value a Mirror? What equivalent analogy can we impart for the act of introspection? struct LongDarkTeaTimeOfTheSoul? (As if we didn’t have better things to do than ask our Swift code about the meaning of Self) Without further ado, let’s take a peek inside the inner workings of how Swift exposes that of our own code: Mirror, CustomReflectable, and CustomLeafReflectable. Swift provides a default reflection, or structured view, for every object. This view is used by Playgrounds (unless a custom description is provided), and also acts as a fallback when describing objects that don’t conform to the CustomStringConvertible or CustomDebugStringConvertible protocols. You can customize how instances of a type are reflected by adopting the CustomReflectable protocol and implementing the required customMirror property. The computed customMirror property returns a Mirror object, which is typically constructed by reflecting self and passing the properties you wish to expose to the reflected interface. Properties may be either keyed and passed in a dictionary or unkeyed and passed in an array. You can optionally specify a display style to override the default for the kind of type (struct, class, tuple, etc.); if the type is a class, you also have the ability to adjust how ancestors are represented. Conforming to CustomReflectable To get a better understanding of how this looks in practice, let’s look at an example involving an implementation of a data model for the game of chess: A chessboard comprises 64 squares, divided into 8 rows (called ranks) and 8 columns (called files). A compact way to represent each location on a board is the 0x88 method, in which the rank and file are encoded into 3 bits of each nibble of a byte (0b0rrr0fff). For simplicity, Rank and File are typealias’d to UInt8 and use one-based indices; a more complete implementation might use enumerations instead. struct ChessBoard { typealias Rank = UInt8 typealias File = UInt8 struct Coordinate: Equatable, Hashable { private let value: UInt8 var rank: Rank { return (value >> 3) + 1 } var file: File { return (value & 0b00000111) + 1 } } } init(rank: Rank, file: File) { precondition(1...8 ~= rank && 1...8 ~= file) self.value = ((rank - 1) << 3) + (file - 1) } If we were to construct a coordinate for b8 (rank 8, file “b” or 2), its default reflection wouldn’t be particularly helpful: let b8 = ChessBoard.Coordinate(rank: 8, file: 2) String(reflecting: b8) // ChessBoard.Coordinate(value: 57) 57 in decimal is equal to 0111001 in binary, or a value of 7 for rank and 1 for file, which adding the index offset of 1 brings us to rank 8 and file 2. The default mirror provided for a structure includes each of an object’s stored properties. However, consumers of this API shouldn’t need to know or even care about the implementation details of how this information is stored. It’s more important to reflect the programmatic surface area of the type, and we can use CustomReflectable to do just that: extension ChessBoard.Coordinate: CustomReflectable { var customMirror: Mirror { return Mirror(self, children: ["rank": rank, "file": file]) } } Rather than exposing the private stored value property, our custom mirror provides the computed rank and file properties. The result: a much more useful representation that reflects our understanding of the type: String(reflecting: b8) // ChessBoard.Coordinate(rank: 8, file: 2) Introspecting the Custom Mirror The String(reflecting:) initializer is one way that mirrors are used. But you can also get at them directly through the customMirror property to access the type metatype and display style, and enumerate each of the mirror’s children. b8.customMirror.subjectType // ChessBoard.Coordinate.Type b8.customMirror.displayStyle // struct for child in b8.customMirror.children { print("\(child.label ?? ""): \(child.value)") } Customizing the Display of a Custom Mirror There are differently-shaped mirrors for each kind of Swift value, each with their own particular characteristics. For example, the most important information about a class is its identity, so its reflection contains only its fully-qualified type name. Whereas a structure is all about substance and offers up its type name and values. If you find this to be less than flattering for your special type you can change up your look by specifying an enumeration value to the displayStyle attribute in the Mirror initializer. Here’s a sample of what you can expect for each of the available display styles when providing both labeled and unlabeled children: Labeled Children extension ChessBoard.Coordinate: CustomReflectable { var customMirror: Mirror { return Mirror(self, children: ["rank": rank, "file": file]) displayStyle: displayStyle ) } } Style Output class ChessBoard.Coordinate struct Coordinate(rank: 8, file: 2) enum Coordinate(8) optional 8 tuple (rank: 8, file: 2) collection [8, 2] set {8, 2} dictionary [rank: 8, file: 2] Unlabeled Children extension ChessBoard.Coordinate: CustomReflectable { var customMirror: Mirror { return Mirror(self, unlabeledChildren: [rank, file], displayStyle: displayStyle ) } } Style Output class ChessBoard.Coordinate struct Coordinate() enum Coordinate(8) optional 8 tuple (8, 2) collection [8, 2] set {8, 2} dictionary [ 8, 2] You can’t change how tuples are reflected. As described in our article about Void, tuples aren’t nominal types, so you can’t write extensions to add conformance to CustomReflectable Reflection and Class Hierarchies Continuing with our chess example, let’s introduce some class by defining an abstract Piece class and subclasses for pawns, knights, bishops, rooks, queens, and kings. (For flavor, let’s add standard valuations while we’re at it). class Piece { enum Color: String { case black, white } let color: Color let location: ChessBoard.Coordinate? } init(color: Color, location: ChessBoard.Coordinate? = nil) { self.color = color self.location = location precondition(type(of: self) != Piece.self, "Piece is abstract") } class class class class class class Pawn: Knight: Bishop: Rook: Queen: King: Piece Piece Piece Piece Piece Piece { static { static { static { static { static {} let let let let let value value value value value = = = = = 1 3 3 5 9 } } } } } So far, so good. Now let’s use the same approach as before to define a custom mirror (setting the display Style to struct so that we get more reflected information than we would otherwise for a class): extension Piece: CustomReflectable { var customMirror: Mirror { return Mirror(self, children: ["color": color, "location": location ?? "N/A"], displayStyle: .struct)) } } Let’s see what happens if we try to reflect a new Knight object: let knight = Knight(color: .black, location: b8) String(reflecting: knight) // Piece(color: black, location: (rank: 8, file: 2)) Hmm… that’s no good. What if we try to override that in an extension to Knight? extension Knight { override var customMirror: Mirror { return Mirror(self, children: ["color": color, "location": location ?? "N/A"]) } } // ! error: overriding declarations in extensions is not supported No dice (to mix metaphors). Let’s look at three different options to get this working with classes. Option 1: Conform to CustomLeafReflectable Swift actually provides something for just such a situation — a protocol that inherits from Custom Reflectable called CustomLeafReflectable. If a class conforms to CustomLeafReflectable, its reflections of subclasses will be suppressed unless they explicitly override customMirror and provide their own Mirror. Let’s take another pass at our example from before, this time adopting CustomLeafReflectable in our initial declaration: class Piece: CustomLeafReflectable { … } var customMirror: Mirror { return Mirror(reflecting: self) } class Knight: Piece { … } override var customMirror: Mirror { return Mirror(self, children: ["color": color, "location": location ?? "N/A", "value": Knight.value], displayStyle: .struct) } Now when go to reflect our knight, we get the correct type — Knight instead of Piece. String(reflecting: knight) // Knight(color:Piece.Color.black, location: (rank: 8, file: 2), value: 3) Unfortunately, this approach requires us to do the same for each of the subclasses. Before we suck it up and copy-paste our way to victory, let’s consider our alternatives. Types that wrap a single value like a string or number can improve their reflected representation simply by conforming to the CustomStringConvertible protocol. For example, the nested Color type can be unambiguously understood by its raw value alone: extension Piece.Color: CustomStringConvertible { var description: String { return self.rawValue } } With this in place, the raw string values will be used anytime Piece.Color is reflected or shows up as a child in a mirror representation. String(reflecting: Piece.Color.black) // black String(reflecting: knight) // Knight(color: black, location: (rank: 8, file: 2), value: 3) Option 2: Encode Type in Superclass Mirror A simpler alternative to going the CustomLeafReflectable route is to include the type information as a child of the mirror declared in the base class. extension Piece: CustomReflectable { var customMirror: Mirror { return Mirror(self, children: ["type": type(of: self), "color": color, "location": location ?? "N/A"], displayStyle: .struct) } } String(reflecting: knight) // Piece(type: Knight, color: black, location: (rank: 8, file: 2)) This approach is appealing because it allows reflection to be hidden as an implementation detail. However, it can only work if subclasses are differentiated by behavior. If a subclass adds stored members or significantly differs in other ways, it’ll need a specialized representation. Which begs the question: why are we using classes in the first place? Option 3: Avoid Classes Altogether Truth be told, we only really introduced classes here as a contrived example of how to use CustomLeaf Reflectable. And what we came up with is kind of a mess, right? Ad hoc additions of value type members for only some of the pieces (kings don’t have a value in chess because they’re a win condition)? An “abstract” base class that crashes if you try to instantiate it (for current lack of a first-class language feature)? Yikes. These are the kinds of things that give OOP a bad name. We can mitigate nearly all of these problems by adopting a protocol-oriented approach like the following: First, define a protocol for Piece and a new protocol to encapsulate pieces that have value, called Valuable: protocol Piece { var color: Color { get } var location: ChessBoard.Coordinate? { get } } protocol Valuable: Piece { static var value: Int { get } } Next, define value types — an enum for Color and structures for each piece: enum Color: String { case black, white } … struct Knight: Piece, Valuable { static let value: Int = 3 let color: Color let location: ChessBoard.Coordinate? } … Although we can’t provide conformance by protocol through an extension, we can provide a default implementation for the requirements and declare adoption in extensions to each concrete Piece type. As a bonus, Valuable can provide a specialized variant that includes its value: extension Piece { var customMirror: Mirror { return Mirror(self, children: ["color": color, "location": location ?? "N/A"], displayStyle: .struct) } } extension Valuable{ var customMirror: Mirror { return Mirror(self, children: ["color": color, "location": location ?? "N/A", "value": Self.value], displayStyle: .struct) } } … extension Knight: CustomReflectable {} … Putting that all together, we get exactly the behavior we want with the advantage of value semantics. let b8 = ChessBoard.Coordinate(rank: 8, file: 2) let knight = Knight(color: .black, location: b8) String(reflecting: knight) // Knight(color: black, location: (rank: 8, file: 2)) Ultimately, CustomLeafReflectable is provided mostly for compatibility with the classical, hierarchical types in Objective-C frameworks like UIKit. If you’re writing new code, you need not follow in their footsteps. When Swift first came out, longtime Objective-C developers missed the dynamism provided by that runtime. And — truth be told — truth be told, things weren’t all super great. Programming in Swift could, at times, feel unnecessarily finicky and constraining. “If only we could swizzle…” we’d grumble. And perhaps this is still the case for some of us some of the time, but with Swift 4, many of the most frustrating use cases have been solved. Codable is a better solution to JSON serialization than was ever available using dynamic introspection. Codable can also provide mechanisms for fuzzing and fixtures to sufficiently enterprising and clever developers. The CaseIterable protocol fills another hole, allowing us a first-class mechanism for getting an inventory of enumeration cases. In many ways, Swift has leap-frogged the promise of dynamic programming, making APIs like Mirror largely unnecessary beyond logging and debugging. That said, should you find the need for introspection, you’ll know just where to look. KeyValuePairs Cosmologies seek to create order by dividing existence into discrete, interdependent parts. Thinkers in every society throughout history have posited various arrangements — though Natural numbers being what they are, there are only so many ways to slice the ontological pie. There are dichotomies like 陰陽 ( yīnyáng ) ( ) : incontrovertible and self-evident (albeit reductive). There are trinities, which position man in relation to heaven and earth. One might divide everything into four, like the ancient Greeks with the elements of earth, water, air, and fire. Or you could carve things into five, like the Chinese with Wood ( 木 mù ) , Fire ( 火 huǒ ) , Earth ( 土 tǔ ) , Metal ( 金 jīn ) , and Water ( 水 shuǐ ) . Still not satisfied? Perhaps the eight-part 八卦 (bāguà) will provide the answers that you seek: ☰ ☱ ☲ ☳ ☴ ☵ ☶ ☷ 天 澤 火 雷 風 水 山 地 ( Heaven ) ( Lake / Marsh ) ( Fire ) ( Thunder ) ( Wind ) ( Water ) ( Mountain ) ( Ground ) Despite whatever galaxy brain opinion we may have about computer science, the pragmatic philosophy of day-to-day programming more closely aligns with a mundane cosmology; less imago universi, more じゃんけん jan-ken ( Rock-Paper-Scissors ️). For a moment, ponder the mystical truths of fundamental Swift collection types: Arrays are ordered collections of values. Sets are unordered collections of unique values. Dictionaries are unordered collections of key-value associations. ― The Book of Swift Thus compared to the pantheon of java.util collections or std containers, Swift offers a coherent coalition of three. Yet, just as we no longer explain everyday phenomena strictly in terms of humors or æther, we must reject this formulation. Such a model is incomplete. We could stretch our understanding of sets to incorporate OptionSet (as explained in a previous article), but we’d be remiss to try and shoehorn Range and ClosedRange into the same bucket as Array — and that’s to say nothing of the panoply of Swift Collection Protocols (an article in dire need of revision). This week on NSHipster, we’ll take a look at KeyValuePairs, a small collection type that challenges our fundamental distinctions between Array, Set, and Dictionary. In the process, we’ll gain a new appreciation and a deeper understanding of the way things work in Swift. KeyValuePairs is a structure in the Swift standard library that — surprise, surprise — represents a collection of key-value pairs. struct KeyValuePairs<Key, Value>: ExpressibleByDictionaryLiteral, RandomAccessCollection { typealias Element = (key: Key, value: Value) typealias Index = Int typealias Indices = Range<Int> typealias SubSequence = Slice<KeyValuePairs> } … This truncated declaration highlights the defining features of KeyValuePairs: • Its ability to be expressed by a dictionary literal • Its capabilities as a random-access collection KeyValuePairs as Expressible by Dictionary Literal Literals allow us to represent values directly in source code, and Swift is rather unique among other languages by extending this functionality to our own custom types through protocols. A dictionary literal represents a value as mapping of keys and values like so: ["key": "value"] However, the term “dictionary literal” is a slight misnomer, since a sequence of key-value pairs — not a Dictionary — are passed to the ExpressibleByDictionaryLiteral protocol’s required initializer: protocol ExpressibleByDictionaryLiteral { associatedtype Key associatedtype Value } init(dictionaryLiteral elements: (Key, Value)...) This confusion was amplified by the existence of a DictionaryLiteral type, which was only recently renamed to KeyValuePairs in Swift 5. The name change served to both clarify its true nature and bolster use as a public API (and not some internal language construct). You can create a KeyValuesPairs object with a dictionary literal (in fact, this is the only way to create one): let pairs: KeyValuePairs<String, String> = [ "木": "wood", "火": "fire", "土": "ground", "金": "metal", "水": "water" ] For more information about the history and rationale of this change, see SE-0214: “Renaming the DictionaryLiteral type to KeyValuePairs”. KeyValuePairs as Random-Access Collection KeyValuePairs conforms to RandomAccessCollection, which allows its contents to be retrieved by (in this case, Int) indices. In contrast to Array, KeyValuePairs doesn’t conform to RangeReplaceable Collection, so you can’t append elements or remove individual elements at indices or ranges. This narrowly constrains KeyValuePairs, such that it’s effectively immutable once initialized from a dictionary literal. These functional limitations are the key to understanding its narrow application in the standard library. KeyValuePairs in the Wild Across the Swift standard library and Apple SDK, KeyValuePairs are found in just two places: • A Mirror initializer (as discussed previously): struct Mirror { init<Subject>(_ subject: Subject, children: KeyValuePairs<String, Any>, displayStyle: DisplayStyle? = nil, ancestorRepresentation: AncestorRepresentation = .generated) } typealias RGBA = UInt32 typealias RGBAComponents = (UInt8, UInt8, UInt8, UInt8) let color: RGBA = 0xFFEFD5FF let mirror = Mirror(color, children: ["name": "Papaya Whip", "components": (0xFF, 0xEF, 0xD5, 0xFF) as RGBAComponents], displayStyle: .struct) mirror.children.first(where: { (label, _) in label == "name" })?.value // "Papaya Whip" • The @dynamicCallable method: @dynamicCallable struct KeywordCallable { func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Int { return args.count } } let object = KeywordCallable() object(a: 1, 2) // desugars to `object.dynamicallyCall(withKeywordArguments: ["a": 1, "": 2])` On both occasions, KeyValuePairs is employed as an alternative to [(Key, Value)] to enforce restraint by the caller. Without any other public initializers, KeyValuePairs can only be constructed from dictionary literals, and can’t be constructed dynamically. (Sort of like how one-way streets are used to prevent rat running on residential streets.) Working with KeyValuePairs Values If you want to do any kind of work with a KeyValuePairs, you’ll first want to convert it into a conventional Collection type — either Array or Dictionary. Converting to Arrays KeyValuePairs is a Sequence, by virtue of its conformance to RandomAccessCollection (and therefore Collection). When we pass it to the corresponding Array initializer, it becomes an array of its associated Element type ((Key, Value)). let arrayOfPairs: [(Key, Value)] = Array(pairs) Though, if you just want to iterate over each key-value pair, it’s conformance to Sequence means that you can pass it directly to a for-in loop: for (key, value) in pairs { … } You can always create an Array from a KeyValuePairs object, but creating Dictionary is more complicated. Converting to Dictionaries There are four built-in types that conform to ExpressibleByDictionaryLiteral: • Dictionary • NSDictionary • NSMutableDictionary • KeyValuePairs Each of the three dictionary types constitutes a surjective mapping, such that every value element has one or more corresponding keys. KeyValuePairs is the odd one out: it instead maintains an ordered list of tuples that allows for duplicate key associations. Dictionary got a number of convenient initializers in Swift 4 thanks to SE-0165 (thanks, Nate!), including init(uniqueKeysWithValues:), init(_:uniquingKeysWith:), and init(grouping:by) Consider the following example that constructs a KeyValuePairs object with a duplicate key: let pairsWithDuplicateKey: KeyValuePairs<String, String> = [ "天": "Heaven", "澤": "Lake", "澤": "Marsh", … ] Attempting to pass this to init(uniqueKeysWithValues:) results in a fatal error: Dictionary<String, Int>(uniqueKeysWithValues: Array(pairsWithDuplicateKey)) // Fatal error: Duplicate values for key: '澤' Instead, you must either specify which value to map or map Dictionary(Array(pairsWithDuplicateKey), uniquingKeysWith: { (first, _) in first }) // ["澤": "Lake", … ] Dictionary(Array(pairsWithDuplicateKey), uniquingKeysWith: { (_, last) in last }) // ["澤": "Marsh", … ] Dictionary(grouping: Array(pairsWithDuplicateKey), by: { (pair) in pair.value }) // ["澤": ["Lake", "Marsh"], … ] (The resolution of KeyValuePairs into a Dictionary is reminiscent of a conflict-free replicated data type (CRDT).) Outside of its narrow application in the standard library, KeyValuePairs are unlikely to make an appearance in your own codebase. You’re almost always better off going with a simple [(Key, Value)] tuple array. Much as today’s Standard Model more closely resembles the cacophony of a zoo than the musica universalis of celestial spheres, KeyValuePairs challenges our tripartite view of Swift collection types. But like all cosmological exceptions — though uncomfortable or even unwelcome at times — it serves to expand our understanding. That’s indeed its key value. Swift Property Wrappers Years ago, we remarked that the “at sign” (@) — along with square brackets and ridiculously-long method names — was as characteristic of Objective-C as parentheses are to Lisp or punctuation is to Perl. Then came Swift, and with it an end to these curious little Or so we thought. -shaped glyphs. At first, the function of @ was limited to Objective-C interoperability: @IBAction, @NSCopying, @UIApplicationMain, and so on. But in time, Swift has continued to incorporate an ever-increasing number of @-prefixed attributes. We got our first glimpse of Swift 5.1 at WWDC 2019 by way of the SwiftUI announcement. And with each “mind-blowing” slide came a hitherto unknown attribute: @State, @Binding, @Environment Object… We saw the future of Swift, and it was full of @s. We’ll dive into SwiftUI once it’s had a bit longer to bake. But this week, we wanted to take a closer look at a key language feature for SwiftUI — something that will have arguably the biggest impact on the «je ne sais quoi» of Swift in version 5.1 and beyond: property wrappers About Property Delegates Wrappers Property wrappers were first pitched to the Swift forums back in March of 2019 — months before the public announcement of SwiftUI. In his original pitch, Swift Core Team member Douglas Gregor described the feature (then called “property delegates”) as a user-accessible generalization of functionality currently provided by language features like the lazy keyword. Laziness is a virtue in programming, and this kind of broadly useful functionality is characteristic of the thoughtful design decisions that make Swift such a nice language to work with. When a property is declared as lazy, it defers initialization of its default value until first access. For example, you could implement equivalent functionality yourself using a private property whose access is wrapped by a computed property, but a single lazy keyword makes all of that unnecessary. SE-0258: Property Wrappers is currently in its third review (scheduled to end yesterday, at the time of publication), and it promises to open up functionality like lazy so that library authors can implement similar functionality themselves. The proposal does an excellent job outlining its design and implementation. So rather than attempt to improve on this explanation, we thought it’d be interesting to look at some new patterns that property wrappers make possible — and, in the process, get a better handle on how we might use this feature in our projects. So, for your consideration, here are four potential use cases for the new @propertyWrapper attribute: • Constraining Values • Transforming Values on Property Assignment • Changing Synthesized Equality and Comparison Semantics • Auditing Property Access Constraining Values SE-0258 offers plenty of practical examples, including @Lazy, @Atomic, @ThreadSpecific, and @Box. But the one we were most excited about was that of the @Constrained property wrapper. Swift’s standard library offer correct, performant, floating-point number types, and you can have it in any size you want — so long as it’s 32 or 64 (or 80) bits long (to paraphrase Henry Ford). If you wanted to implement a custom floating-point number type that enforced a valid range of values, this has been possible since Swift 3. However, doing so would require conformance to a labyrinth of protocol requirements. Pulling this off is no small feat, and often far too much work to justify for most use cases. Fortunately, property wrappers offer a way to parameterize standard number types with significantly less effort. Implementing a value clamping property wrapper Consider the following Clamping structure. As a property wrapper (denoted by the @propertyWrapper attribute), it automatically “clamps” out-of-bound values within the prescribed range. @propertyWrapper struct Clamping<Value: Comparable> { var value: Value let range: ClosedRange<Value> init(initialValue value: Value, _ range: ClosedRange<Value>) { precondition(range.contains(value)) self.value = value self.range = range } } var wrappedValue: Value { get { value } set { value = min(max(range.lowerBound, newValue), range.upperBound) } } You could use @Clamping to guarantee that a property modeling acidity in a chemical solution within the conventional range of 0 – 14. struct Solution { @Clamping(0...14) var pH: Double = 7.0 } let carbonicAcid = Solution(pH: 4.68) // at 1 mM under standard conditions Attempting to set pH values outside that range results in the closest boundary value (minimum or maximum) to be used instead. let superDuperAcid = Solution(pH: -1) superDuperAcid.pH // 0 You can use property wrappers in implementations of other property wrappers. For example, this UnitInterval property wrapper delegates to @Clamping for constraining values between 0 and 1, inclusive. @propertyWrapper struct UnitInterval<Value: FloatingPoint> { @Clamping(0...1) var wrappedValue: Value = .zero } init(initialValue value: Value) { self.wrappedValue = value } For example, you might use the @UnitInterval property wrapper to define an RGB type that expresses red, green, blue intensities as percentages. struct RGB { @UnitInterval var red: Double @UnitInterval var green: Double @UnitInterval var blue: Double } let cornflowerBlue = RGB(red: 0.392, green: 0.584, blue: 0.929) Related Ideas • A @Positive / @NonNegative property wrapper that provides the unsigned guarantees to signed integer types. • A @NonZero property wrapper that ensures that a number value is either greater than or less than 0. • @Validated or @Whitelisted / @Blacklisted property wrappers that restrict which values can be assigned. Transforming Values on Property Assignment Accepting text input from users is a perennial headache among app developers. There are just so many things to keep track of, from the innocent banalities of string encoding to malicious attempts to inject code through a text field. But among the most subtle and frustrating problems that developers face when accepting user-generated content is dealing with leading and trailing whitespace. A single leading space can invalidate URLs, confound date parsers, and sow chaos by way of off-byone errors: import Foundation URL(string: " https://nshipster.com") // nil (!) ISO8601DateFormatter().date(from: " 2019-06-24") // nil (!) let words = " Hello, world!".components(separatedBy: .whitespaces) words.count // 3 (!) When it comes to user input, clients most often plead ignorance and just send everything as-is to the server. ¯\_(ツ)_/¯. While I’m not advocating for client apps to take on more of this responsibility, the situation presents another compelling use case for Swift property wrappers. Foundation bridges the trimmingCharacters(in:) method to Swift strings, which provides, among other things, a convenient way to lop off whitespace from both the front or back of a String value. Calling this method each time you want to ensure data sanity is, however, less convenient. And if you’ve ever had to do this yourself to any appreciable extent, you’ve certainly wondered if there might be a better approach. In your search for a less ad-hoc approach, you may have sought redemption through the willSet property callback… only to be disappointed that you can’t use this to change events already in motion. struct Post { var title: String { willSet { title = newValue.trimmingCharacters(in: .whitespacesAndNewlines) /* ️ Attempting to store to property 'title' within its own willSet, which is about to be overwritten by the new value */ } } } From there, you may have realized the potential of didSet as an avenue for greatness… only to realize later that didSet isn’t called during initial property assignment. struct Post { var title: String { // Not called during initialization didSet { self.title = title.trimmingCharacters(in: .whitespacesAndNewlines) } } } Setting a property in its own didSet callback thankfully doesn’t cause the callback to fire again, so you don’t have to worry about accidental infinite self-recursion. Undeterred, you may have tried any number of other approaches… ultimately finding none to yield an acceptable combination of ergonomics and performance characteristics. If any of this rings true to your personal experience, you can rejoice in the knowledge that your search is over: property wrappers are the solution you’ve long been waiting for. Implementing a Property Wrapper that Trims Whitespace from String Values Consider the following Trimmed struct that trims whitespaces and newlines from incoming string values. import Foundation @propertyWrapper struct Trimmed { private(set) var value: String = "" var wrappedValue: String { get { value } set { value = newValue.trimmingCharacters(in: .whitespacesAndNewlines) } } } init(initialValue: String) { self.wrappedValue = initialValue } By marking each String property in the Post structure below with the @Trimmed annotation, any string value assigned to title or body — whether during initialization or via property access afterward — automatically has its leading or trailing whitespace removed. struct Post { @Trimmed var title: String @Trimmed var body: String } let quine = Post(title: " Swift Property Wrappers ", body: " … ") quine.title // "Swift Property Wrappers" (no leading or trailing spaces!) quine.title = " @propertyWrapper " quine.title // "@propertyWrapper" (still no leading or trailing spaces!) Related Ideas • A @Transformed property wrapper that applies ICU transforms to incoming string values. • A @Normalized property wrapper that allows a String property to customize its normalization form. • A @Quantized / @Rounded / @Truncated property that quantizes values to a particular degree (e.g. “round up to nearest ½”), but internally tracks precise intermediate values to prevent cascading rounding errors. Changing Synthesized Equality and Comparison Semantics This behavior is contingent on an implementation detail of synthesized protocol conformance and may change before this feature is finalized (though we hope this continues to work as described below). In Swift, two String values are considered equal if they are canonically equivalent. By adopting these equality semantics, Swift strings behave more or less as you’d expect in most circumstances: if two strings comprise the same characters, it doesn’t matter whether any individual character is composed or precomposed — that is, “é” (U+00E9 LATIN SMALL LETTER E WITH ACUTE) is equal to “e” (U+0065 LATIN SMALL LETTER E) + “◌́” (U+0301 COMBINING ACUTE ACCENT). But what if your particular use case calls for different equality semantics? Say you wanted a case insensitive notion of string equality? There are plenty of ways you might go about implementing this today using existing language features: • You could take the lowercased() result anytime you do == comparison, but as with any manual process, this approach is error-prone. • You could create a custom CaseInsensitive type that wraps a String value, but you’d have to do a lot of additional work to make it as ergonomic and functional as the standard String type. • You could define a custom comparator function to wrap that comparison — heck, you could even define your own custom operator for it — but nothing comes close to an unqualified == between two operands. None of these options are especially compelling, but thanks to property wrappers in Swift 5.1, we’ll finally have a solution that gives us what we’re looking for. As with numbers, Swift takes a protocol-oriented approach that delegates string responsibilities across a constellation of narrowly-defined types. While you could create your own String-equivalent type, the documentation carries a strong directive to the contrary: Do not declare new conformances to StringProtocol. Only the String and Substring types in the standard library are valid conforming types. Implementing a case-insensitive property wrapper The CaseInsensitive type below implements a property wrapper around a String / SubString value. The type conforms to Comparable (and by extension, Equatable) by way of the bridged NSString API caseInsensitiveCompare(_:): import Foundation @propertyWrapper struct CaseInsensitive<Value: StringProtocol> { var wrappedValue: Value } extension CaseInsensitive: Comparable { private func compare(_ other: CaseInsensitive) -> ComparisonResult { wrappedValue.caseInsensitiveCompare(other.wrappedValue) } static func == (lhs: CaseInsensitive, rhs: CaseInsensitive) -> Bool { lhs.compare(rhs) == .orderedSame } static func < (lhs: CaseInsensitive, rhs: CaseInsensitive) -> Bool { lhs.compare(rhs) == .orderedAscending } } static func > (lhs: CaseInsensitive, rhs: CaseInsensitive) -> Bool { lhs.compare(rhs) == .orderedDescending } Although the greater-than operator (>) can be derived automatically, we implement it here as a performance optimization to avoid unnecessary calls to the underlying caseInsensitive Compare method. Construct two string values that differ only by case, and they’ll return false for a standard equality check, but true when wrapped in a CaseInsensitive object. let hello: String = "hello" let HELLO: String = "HELLO" hello == HELLO // false CaseInsensitive(wrappedValue: hello) == CaseInsensitive(wrappedValue: HELLO) // true So far, this approach is indistinguishable from the custom “wrapper type” approach described above. And this is normally where we’d start the long slog of implementing conformance to Expressible ByStringLiteral and all of the other protocols to make CaseInsensitive start to feel enough like String to feel good about our approach. Property wrappers allow us to forego all of this busywork entirely: struct Account: Equatable { @CaseInsensitive var name: String } init(name: String) { $name = CaseInsensitive(wrappedValue: name) } var johnny = Account(name: "johnny") let JOHNNY = Account(name: "JOHNNY") let Jane = Account(name: "Jane") johnny == JOHNNY // true johnny == Jane // false johnny.name == JOHNNY.name // false johnny.name = "Johnny" johnny.name // "Johnny" Here, Account objects are checked for equality by a case-insensitive comparison on their name property value. However, when we go to get or set the name property, it’s a bona fide String value. That’s neat, but what’s actually going on here? Since Swift 4, the compiler automatically synthesizes Equatable conformance to types that adopt it in their declaration and whose stored properties are all themselves Equatable. Because of how compiler synthesis is implemented (at least currently), wrapped properties are evaluated through their wrapper rather than their underlying value: // Synthesized by Swift Compiler extension Account: Equatable { static func == (lhs: Account, rhs: Account) -> Bool { lhs.$name == rhs.$name } } Related Ideas • Defining @CompatibilityEquivalence such that wrapped String properties with the values "①" and "1" are considered equal. • A @Approximate property wrapper to refine equality semantics for floating-point types (See also SE-0259) • A @Ranked property wrapper that takes a function that defines strict ordering for, say, enumerated values; this could allow, for example, the playing card rank .ace to be treated either low or high in different contexts. Auditing Property Access Business requirements may stipulate certain controls for who can access which records when or prescribe some form of accounting for changes over time. Once again, this isn’t a task typically performed by, say, an iOS app; most business logic is defined on the server, and most client developers would like to keep it that way. But this is yet another use case too compelling to ignore as we start to look at the world through property-wrapped glasses. Implementing a Property Value Versioning The following Versioned structure functions as a property wrapper that intercepts incoming values and creates a timestamped record when each value is set. import Foundation @propertyWrapper struct Versioned<Value> { private var value: Value private(set) var timestampedValues: [(Date, Value)] = [] var wrappedValue: Value { get { value } } } set { defer { timestampedValues.append((Date(), value)) } value = newValue } init(initialValue value: Value) { self.wrappedValue = value } A hypothetical ExpenseReport class could wrap its state property with the @Versioned annotation to keep a paper trail for each action during processing. class ExpenseReport { enum State { case submitted, received, approved, denied } } @Versioned var state: State = .submitted Related Ideas • An @Audited property wrapper that logs each time a property is read or written to. • A @Decaying property wrapper that divides a set number value each time the value is read. However, this particular example highlights a major limitation in the current implementation of property wrappers that stems from a longstanding deficiency of Swift generally: Properties can’t be marked as throws. Without the ability to participate in error handling, property wrappers don’t provide a reasonable way to enforce and communicate policies. For example, if we wanted to extend the @Versioned property wrapper from before to prevent state from being set to .approved after previously being .denied, our best option is fatalError(), which isn’t really suitable for real applications: class ExpenseReport { @Versioned var state: State = .submitted { willSet { if newValue == .approved, $state.timestampedValues.map { $0.1 }.contains(.denied) { fatalError("J'Accuse!") } } } } var tripExpenses = ExpenseReport() tripExpenses.state = .denied tripExpenses.state = .approved // Fatal error: "J'Accuse!" This is just one of several limitations that we’ve encountered so far with property wrappers. In the interest of creating a balanced perspective on this new feature, we’ll use the remainder of this article to enumerate them. Limitations Some of the shortcomings described below may be more a limitation of my current understanding or imagination than that of the proposed language feature itself. Please reach out with any corrections or suggestions you might have for reconciling them! Properties Can’t Participate in Error Handling Properties, unlike functions, can’t be marked as throws. As it were, this is one of the few remaining distinctions between these two varieties of type members. Because properties have both a getter and a setter, it’s not entirely clear what the right design would be if we were to add error handling — especially when you consider how to play nice with syntax for other concerns like access control, custom getters / setters, and callbacks. As described in the previous section, property wrappers have but two methods of recourse to deal with invalid values: 1. Ignoring them (silently) 2. Crashing with fatalError() Neither of these options is particularly great, so we’d be very interested by any proposal that addresses this issue. Wrapped Properties Can’t Be Aliased Another limitation of the current proposal is that you can’t use instances of property wrappers as property wrappers. Our UnitInterval example from before, which constrains wrapped values between 0 and 1 (inclusive), could be succinctly expressed as: typealias UnitInterval = Clamping(0...1) // However, this isn’t possible. Nor can you use instances of property wrappers to wrap properties. let UnitInterval = Clamping(0...1) struct Solution { @UnitInterval var pH: Double } // All this actually means in practice is more code replication than would be ideal. But given that this problem arises out of a fundamental distinction between types and values in the language, we can forgive a little duplication if it means avoiding the wrong abstraction. Property Wrappers Are Difficult To Compose Composition of property wrappers is not a commutative operation; the order in which you declare them affects how they’ll behave. Consider the interplay between an attribute that performs string inflection and other string transforms. For example, a composition of property wrappers to automatically normalize the URL “slug” in a blog post will yield different results if spaces are replaced with dashes before or after whitespace is trimmed. struct Post { … @Dasherized @Trimmed var slug: String } But getting that to work in the first place is easier said than done! Attempting to compose two property wrappers that act on String values fails, because the outermost wrapper is acting on a value of the innermost wrapper type. @propertyWrapper struct Dasherized { private(set) var value: String = "" var wrappedValue: String { get { value } set { value = newValue.replacingOccurrences(of: " ", with: "-") } } } init(initialValue: String) { self.wrappedValue = initialValue } struct Post { … @Dasherized @Trimmed var slug: String // } ️ An internal error occurred. There’s a way to get this to work, but it’s not entirely obvious or pleasant. Whether this is something that can be fixed in the implementation or merely redressed by documentation remains to be seen. Property Wrappers Aren’t First-Class Dependent Types A dependent type is a type defined by its value. For instance, “a pair of integers in which the latter is greater than the former” and “an array with a prime number of elements” are both dependent types because their type definition is contingent on its value. Swift’s lack of support for dependent types in its type system means that any such guarantees must be enforced at run time. The good news is that property wrappers get closer than any other language feature proposed thus far in filling this gap. However, they still aren’t a complete replacement for true value-dependent types. You can’t use property wrappers to, for example, define a new type with a constraint on which values are possible. typealias pH = @Clamping(0...14) Double // func acidity(of: Chemical) -> pH {} Nor can you use property wrappers to annotate key or value types in collections. enum HTTP { struct Request { var headers: [@CaseInsensitive String: String] // } } These shortcomings are by no means deal-breakers; property wrappers are extremely useful and fill an important gap in the language. It’ll be interesting to see whether the addition of property wrappers will create a renewed interest in bringing dependent types to Swift, or if they’ll be seen as “good enough”, obviating the need to formalize the concept further. Property Wrappers Are Difficult to Document Pop Quiz: Which property wrappers are made available by the SwiftUI framework? Go ahead and visit the official SwiftUI docs and try to answer. In fairness, this failure isn’t unique to property wrappers. If you were tasked with determining which protocol was responsible for a particular API in the standard library or which operators were supported for a pair of types based only on what was documented on developer.apple.com, you’re likely to start considering a mid-career pivot away from computers. This lack of comprehensibility is made all the more dire by Swift’s increasing complexity. Property Wrappers Further Complicate Swift Swift is a much, much more complex language than Objective-C. That’s been true since Swift 1.0 and has only become more so over time. The profusion of @-prefixed features in Swift — whether it’s @dynamicMemberLookup and @dynamicCallable from Swift 4, or @differentiable and @memberwise from Swift for Tensorflow — makes it increasingly difficult to come away with a reasonable understanding of Swift APIs based on documentation alone. In this respect, the introduction of @propertyWrapper will be a force multiplier. How will we make sense of it all? (That’s a genuine question, not a rhetorical one.) Alright, let’s try to wrap this thing up — Swift property wrappers allow library authors access to the kind of higher-level behavior previously reserved for language features. Their potential for improving safety and reducing complexity of code is immense, and we’ve only begun to scratch the surface of what’s possible. Yet, for all of their promise, property wrappers and its cohort of language features debuted alongside SwiftUI introduce tremendous upheaval to Swift. Or, as Nataliya Patsovska put it in a tweet: iOS API design, short history: • Objective C - describe all semantics in the name, the types don’t mean much • Swift 1 to 5 - name focuses on clarity and basic structs, enums, classes and protocols hold semantics • Swift 5.1 - @wrapped $path @yolo ― @nataliya_bg Perhaps we’ll only know looking back whether Swift 5.1 marked a tipping point or a turning point for our beloved language. Swift API Availability Code exists in a world of infinite abundance. Whatever you can imagine is willed into being — so long as you know how to express your desires. As developers, we know that code will eventually be compiled into software, and forced to compete in the real-world for allocation of scarce hardware resources. Though up until that point, we can luxuriate in the feeling of unbounded idealism… well, mostly. For software is not a pure science, and our job — in reality — is little more than shuttling data through broken pipes between leaky abstractions. This week on NSHipster, we’re exploring a quintessential aspect of our unglamorous job: API availability. The good news is that Swift provides first-class constructs for dealing with these real-world constraints by way of @available and #available. However, there are a few nuances to these language features, of which many Swift developers are unaware. So be sure to read on to make sure that you’re clear on all the options available to you. @available In Swift, you use the @available attribute to annotate APIs with availability information, such as “this API is deprecated in macOS 10.15” or “this API requires Swift 5.1 or higher”. With this information, the compiler can ensure that any such APIs used by your app are available to all platforms supported by the current target. The @available attribute can be applied to declarations, including top-level functions, constants, and variables, types like structures, classes, enumerations, and protocols, and type members — initializers, class deinitializers, methods, properties, and subscripts. The @available attribute, however, can’t be applied to operator precedence group (precedencegroup) or protocol associated type (associatedtype) declarations. Platform Availability When used to designate platform availability for an API, the @available attribute can take one or two forms: • A “shorthand specification” that lists minimum version requirements for multiple platforms • An extended specification that can communicate additional details about availability for a single platform Shorthand Specification @available( platform version , [platform version] ... , *) • A platform ; iOS, macCatalyst, macOS / OSX, tvOS, or watchOS, or any of those with ApplicationExtension appended (e.g. macOSApplicationExtension). • A version number consisting of one, two, or three positive integers, separated by a period (.), to denote the major, minor, and patch version. • Zero or more versioned platforms in a comma-delimited (,) list. • An asterisk (*), denoting that the API is unavailable for all other platforms. An asterisk is always required for platform availability annotations to handle potential future platforms (such as the long-rumored iDishwasherOS). For example, new, cross-platform APIs introduced at WWDC 2019 might be annotated as: @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) The list of available platform names is specified by the Clang compiler front-end. Look a few lines up, though, and you’ll find a curious mention of “android”(introduced by D7929). Does anyone have more context about why that’s in there? Shorthand specifications are a convenient way to annotate platform availability. But if you need to communicate more information, such as when or why an API was deprecated or what should be used as a replacement, you’ll want to opt for an extended specification instead. Introduced, Deprecated, Obsoleted, and Unavailable // With introduced, deprecated, and/or obsoleted @available( platform | * , introduced: version , deprecated: version [, renamed: "..."] [, message: "..."] ) , obsoleted: version // With unavailable @available( platform | * , unavailable [, renamed: "..."] [, message: "..."] ) • A platform , same as before, or an asterisk (*) for all platforms. • Either introduced, deprecated, and/or obsoleted… ◦ An introduced version , denoting the first version in which the API is available ◦ A deprecated version , denoting the first version when using the API generates a compiler warning ◦ An obsoleted version , denoting the first version when using the API generates a compiler error • …or unavailable, which causes the API to generate a compiler error when used • renamed with a keypath to another API; when provided, Xcode provides an automatic “fix-it” • A message string to be included in the compiler warning or error Unlike shorthand specifications, this form allows for only one platform to be specified. So if you want to annotate availability for multiple platforms, you’ll need stack @available attributes. For example, here’s how the previous shorthand example can be expressed in multiple attributes: @available(macOS, introduced: 10.15) @available(iOS, introduced: 13) @available(watchOS, introduced: 6) @available(tvOS, introduced: 13) Once again, diving into the underlying Clang source code unearths some curious tidbits. In addition to the @available arguments listed above, Clang includes strict and priority. Neither of these works in Swift, but it’s interesting nonetheless. Apple SDK frameworks make extensive use of availability annotations to designate new and deprecated APIs with each release of iOS, macOS, and other platforms. For example, iOS 13 introduces a new UICollectionViewCompositionalLayout class. If you jump to its declaration by holding command ( ⌘ ) and clicking on that symbol in Xcode, you’ll see that it’s annotated with @available: @available(iOS 13.0, *) open class UICollectionViewCompositionalLayout : UICollectionViewLayout { … } (At the time of writing, the docs for UICollectionViewCompositionalLayout are “No overview available.” — Which is a real shame because this is an insanely great addition to UIKit. If you haven’t already, go watch WWDC Session 215: “Advances in Collection View Layout” for a great overview of this new API.) This @available attribute tells the compiler that UICollectionViewCompositionalLayout can only be called on devices running iOS, version 13.0 or later (with caveats; see note below). If your app targets iOS 13 only, then you can use UICollectionViewCompositionalLayout without any special consideration. If, however, your deployment target is set below iOS 13 in order to support previous versions of iOS (as is the case for many existing apps), then any use of UICollectionView CompositionalLayout must be conditionalized. More on that in a moment. When an API is marked as available in iOS, it’s implicitly marked available on tvOS and Mac Catalyst, because both of those platforms are derivatives of iOS. That’s why, for example, the (missing) documentation for UICollectionViewCompositionalLayout reports availability in iOS 13.0+, Mac Catalyst 13.0+, and tvOS 13.0+ despite its declaration only mentions iOS 13.0. If necessary, you can explicitly mark these derived platforms as being unavailable with additional @available attributes: @available(iOS 13, *) @available(tvOS, unavailable) @available(macCatalyst, unavailable) func handleShakeGesture() { … } Swift Language Availability Your code may depend on a new language feature of Swift, such as property wrappers or default enumeration case associated values — both new in Swift 5.1. If you want to support development of your app with previous versions of Xcode or for your Swift package to work for multiple Swift compiler toolchains, you can use the @available attribute to annotate declarations containing new language features. When used to designate Swift language availability for an API, the @available attribute takes the following form: @available(swift version ) Unlike platform availability, Swift language version annotations don’t require an asterisk (*); to the compiler, there’s one Swift language, with multiple versions. Historically, the @available attribute was first introduced for platform availability and later expanded to include Swift language version availability with SE-0141. Although these use cases share a syntax, we recommend that you keep any combined application in separate attributes for greater clarity. import Foundation @available(swift 5.1) @available(iOS 13.0, macOS 10.15, *) @propertyWrapper struct WebSocketed<Value: LosslessStringConvertible> { private var value: Value var wrappedValue: URLSessionWebSocketTask.Message { get { .string(value) } set { if case let .string(description) = newValue { value = Value(description) } } } } #available In Swift, you can predicate if, guard, and while statements with an availability condition, #available, to determine the availability of APIs at runtime. Unlike the @available attribute, an #available condition can’t be used for Swift language version checks. The syntax of an #available expression resembles that of an @available attribute: if | guard | while #available( platform … version , [platform version] ... , *) You can’t combine #available expressions with other conditions using logical operators like && and ||. Now that we know how APIs are annotated for availability, let’s look at how to annotate and conditionalize code based on our platform and/or Swift language version. Working with Unavailable APIs Similar to how, in Swift, thrown errors must be handled or propagated, use of potentially unavailable APIs must be either annotated or conditionalized code. When you attempt to call an API that is unavailable for at least one of your supported targets, Xcode will recommend the following options: • “Add if #available version check” • “Add @available attribute to enclosing declaration ” (suggested at each enclosing scope; for example, the current method as well as that method’s containing class) Following our analogy to error handling, the first option is similar to prepending try to a function call, and the second option is akin to wrapping a statement within do/catch. For example, within an app supporting iOS 12 and iOS 13, a class that subclasses UICollectionView CompositionalLayout must have its declaration annotated with @available, and any references to that subclass would need to be conditionalized with #available: @available(iOS 13.0, *) final class CustomCompositionalLayout: UICollectionViewCompositionalLayout { … } func createLayout() -> UICollectionViewLayout { if #available(iOS 13, *) { return CustomCompositionalLayout() } else { return UICollectionViewFlowLayout() } } Swift comprises many inter-dependent concepts, and crosscutting concerns like availability often result in significant complexity as various parts of the language interact. For instance, what happens if you create a subclass that overrides a property marked as unavailable by its superclass? Or what if you try to call a function that’s renamed on one platform, but replaced by an operator on another? While it’d be tedious to enumerate every specific behavior here, these questions can and often do arise in the messy business of developing apps in the real world. If you find yourself wondering “what if ” and tire of trial-and-error experimentation, you might instead try consulting the Swift language test suite to determine what’s expected behavior. A quick shout-out to Slava Pestov for this gem of a test case: @available(OSX, introduced: 10.53) class EsotericSmallBatchHipsterThing : WidelyAvailableBase {} Alternatively, you can surmise how things work generally from Clang’s diagnostic text: Annotating Availability in Your Own APIs Although you’ll frequently interact with @available as a consumer of Apple APIs, you’re much less likely to use them as an API producer. Availability in Apps Within the context of an app, it may be convenient to use @available deprecation warnings to communicate across a team that use of a view controller, convenience method, or what have you is no longer advisable. @available(iOS, deprecated: 13, renamed: "NewAndImprovedViewController") class OldViewController: UIViewController { … } class NewAndImprovedViewController: UIViewController { … } Use of unavailable or deprecated, however, are much less useful for apps; without any expectation to vend an API outside that context, you can simply remove an API outright. Availability in Third-Party Frameworks If you maintain a framework that depends on the Apple SDK in some way, you may need to annotate your APIs according to the availability of the underlying system calls. For example, a convenience wrapper around Keychain APIs would likely annotate the availability of platform-specific biometric features like Touch ID and Face ID. However, if your APIs wrap the underlying system call in a way that doesn’t expose the implementation details, you may be able For example, an NLP library that previously delegated functionality to NSLinguisticTagger could instead use Natural Language framework when available (as determined by #available), without any user-visible API changes. Availability in Swift Packages If you’re writing Swift qua Swift in a platform-agnostic way and distributing that code as a Swift package, you may want to use @available to give a heads-up to consumers about APIs that are on the way out. Unfortunately, there’s currently no way to designate deprecation in terms of the library version (the list of platforms are hard-coded by the compiler). While it’s a bit of a hack, you could communicate deprecation by specifying an obsolete / non-existent Swift language version like so: @available(swift, deprecated: 0.0.1, message: "Deprecated in 1.2.0") func going(going: Gone...) {} The closest we have to package versioning is the private _PackageDescription platform used by the Swift Package Manager. public enum SwiftVersion { @available(_PackageDescription, introduced: 4, obsoleted: 5) case v3 @available(_PackageDescription, introduced: 4) case v4 } … If you’re interested in extending this functionality to third-party packages, consider starting a discussion in Swift Evolution. Working Around Deprecation Warnings As some of us are keenly aware, it’s not currently possible to silence deprecation warnings in Swift. Whereas in Objective-C, you could suppress warnings with #pragma clang diagnostic push / ignored / pop, no such convenience is afforded to Swift. If you’re among the l33t coders who have “hard mode” turned on (“Treat Warnings as Errors” a.k.a. SWIFT_TREAT_WARNINGS_AS_ERRORS), but find yourself stymied by deprecation warnings, here’s a cheat code you can use: class CustomView { @available(iOS, introduced: 10, deprecated: 13, message: " func method() {} } CustomView().method() // "'method()' was deprecated in iOS 13: ") " protocol IgnoringMethodDeprecation { func method() } extension CustomView: IgnoringMethodDeprecation {} (CustomView() as IgnoringMethodDeprecation).method() // No warning! As an occupation, programming epitomizes the post-scarcity ideal of a post-industrial world economy in the information age. But even so far removed from physical limitations, we remain inherently constrained by forces beyond our control. However, with careful and deliberate practice, we can learn to make use of everything available to us. Swift Import Declarations One of the first lessons we learn as software developers is how to organize concepts and functionality into discrete units. At the smallest level, this means thinking about types and methods and properties. These pieces then form the basis of one or more modules, which may then be packaged into libraries or frameworks. In this way, import declarations are the glue that holds everything together. Yet despite their importance, most Swift developers are familiar only with their most basic form: import module This week on NSHipster, we’ll explore the other shapes of this most prominent part of Swift. An import declaration allows your code to access symbols that are declared in other files. However, if more than one module declares a function or type with the same name, the compiler may not be able to tell which one you want to call in code. To demonstrate this, consider two modules representing the multisport competitions of Triathlon and Pentathlon: A triathlon consists of three events: swimming, cycling, and running. // Triathlon Module func swim() { print(" Swim 1.5 km") } func bike() { print(" Cycle 40 km") } func run() { print(" Run 10 km") } The modern pentathlon comprises five events: fencing, swimming, equestrian, shooting, and running. // Pentathlon Module func fence() { print(" Bout with épées") } func swim() { print(" Swim 200 m") } func ride() { print(" Complete a show jumping course") } func shoot() { print(" Shoot 5 targets") } func run() { print(" Run 3 km cross-country") } If we import either of the modules individually, we can reference each of their functions using their unqualified names without a problem. import Triathlon swim() // OK, calls Triathlon.swim bike() // OK, calls Triathlon.bike run() // OK, calls Triathlon.run But if we import both modules together, we can’t always use unqualified function names. Triathlon and Pentathlon both include swimming and running, so a reference to swim() is ambiguous. import Triathlon import Pentathlon bike() // OK, calls Triathlon.bike fence() // OK, calls Pentathlon.fence swim() // Error, ambiguous How do we reconcile this? One strategy is to use fully-qualified names to work around any ambiguous references. By including the module name, there’s no confusion about whether the program will swim a few laps in a pool or a mile in open water. import Triathlon import Pentathlon Triathlon.swim() // OK, fully-qualified reference to Triathlon.swim Pentathlon.swim() // OK, fully-qualified reference to Pentathlon.swim Another way to resolve API name collision is to change the import declaration to be more selective about what’s included from each module. Importing Individual Declarations Import declarations have a form that can specify individual structures, classes, enumerations, protocols, and type aliases as well as functions, constants, and variables declared at the top-level: import kind module.symbol Here, kind can be any of the following keywords: Kind Description struct Structure class Class enum Enumeration protocol Protocol typealias Type Alias func Function let Constant var Variable For example, the following import declaration adds only the swim() function from the Pentathlon module: import func Pentathlon.swim swim() // OK, calls Pentathlon.swim fence() // Error, unresolved identifier Resolving Symbol Name Collisions When multiple symbols are referenced by the same name in code, the Swift compiler resolves this reference by consulting the following, in order: 1. Local Declarations 2. Imported Declarations 3. Imported Modules If any of these have more than one candidate, Swift is unable to resolve the ambiguity and raises a compilation error. For example, importing the Triathlon module provides the swim(), bike(), and run() methods. The imported swim() function declaration from the Pentathlon overrides that of the Triathlon module. Likewise, the locally-declared run() function overrides the symbol by the same name from Triathlon, and would also override any imported function declarations. import Triathlon import func Pentathlon.swim // Local function shadows whole-module import of Triathlon func run() { print(" Run 42.195 km") } swim() // OK, calls Pentathlon.swim bike() // OK, calls Triathlon.bike run() // OK, calls local run The result of calling this code? A bizarre multi-sport event involving a few laps in the pool, a modest bike ride, and a marathon run. (@ us, IRONMAN) If a local or imported declaration collides with a module name, the compiler first consults the declaration and falls back to qualified lookup in the module. import Triathlon enum Triathlon { case sprint, olympic, ironman } Triathlon.olympic // references local enumeration case Triathlon.swim() // references module function The Swift compiler doesn’t communicate and cannot reconcile naming collisions between modules and local declarations, so you should be aware of this possibility when working with dependencies. Clarifying and Minimizing Scope Beyond resolving name collisions, importing declarations can also be a way to clarify your intent as a programmer. If you’re, for example, using only a single function from a mega-framework like AppKit, you might single that out in your import declaration. import func AppKit.NSUserName NSUserName() // "jappleseed" This technique can be especially helpful when importing top-level constants and variables, whose provenance is often more difficult to discern than other imported symbols. For example, the Darwin framework exports — among other things — a top-level stderr variable. An explicit import declaration here can preempt any questions during code review about where that variable is coming from. import func Darwin.fputs import var Darwin.stderr struct StderrOutputStream: TextOutputStream { mutating func write(_ string: String) { fputs(string, stderr) } } var standardError = StderrOutputStream() print("Error!", to: &standardError) Importing a Submodule The final form of import declarations offers another way to limit API exposure: import module.submodule You’re most likely to encounter submodules in large system frameworks like AppKit and Accelerate. These umbrella frameworks are no longer considered a best-practice, but they served an important role during Apple’s transition to Cocoa in the early 00’s. For example, you might import only the DictionaryServices submodule from the Core Services framework to insulate your code from the myriad deprecated APIs like Carbon Core. import Foundation import CoreServices.DictionaryServices func define(_ word: String) -> String? { let nsstring = word as NSString let cfrange = CFRange(location: 0, length: nsstring.length) guard let definition = DCSCopyTextDefinition(nil, nsstring, cfrange) else { return nil } } return String(definition.takeUnretainedValue()) define("apple") // "apple | ˈapəl | noun 1 the round fruit of a tree..." In practice, isolating imported declarations and submodules doesn’t confer any real benefit beyond signaling programmer intent. Your code won’t compile any faster doing it this way. And since most submodules seem to re-import their umbrella header, this approach won’t do anything to reduce noise in autocomplete lists. Like many obscure and advanced topics, the most likely reason you haven’t heard about these import declaration forms before is that you don’t need to know about them. If you’ve gotten this far making apps without them, you can be reasonably assured that you don’t need to start using them now. Rather, the valuable takeaway here is understanding how the Swift compiler resolves name collisions. And to that end, import declarations are a concept of great import. API Pollution in Swift Modules When you import a module into Swift code, you expect the result to be entirely additive. That is to say: the potential for new functionality comes at no expense (other than, say, a modest increase in the size of your app bundle). Import the NaturalLanguage framework, and *boom* your app can determine the language of text; import CoreMotion, and *whoosh* your app can respond to changes in device orientation. But it’d be surprising if, say, the ability to distinguish between French and Japanese interfered with your app’s ability to tell which way was magnetic north. Although this particular example isn’t real (to the relief of Francophones in Hokkaido), there are situations in which a Swift dependency can change how your app behaves — even if you don’t use it directly. In this week’s article, we’ll look at a few ways that imported modules can silently change the behavior of existing code, and offer suggestions for how to prevent this from happening as an API provider and mitigate the effects of this as an API consumer. Module Pollution It’s a story as old as <time.h>: two things are called Foo, and the compiler has to decide what to do. Pretty much every language with a mechanism for code reuse has to deal with naming collisions one way or another. In the case of Swift, you can use fully-qualified names to distinguish between the Foo type declared in module A (A.Foo) from the Foo type in module B (B.Foo). However, Swift has some unique characteristics that cause other ambiguities to go unnoticed by the compiler, which may result in a change to existing behavior when modules are imported. For the purposes of this article, we use the term pollution to describe such side-effects caused by importing Swift modules that aren’t surfaced by the compiler. We’re not 100% on this terminology, so please get in touch if you can think of any other suggestions. Operator Overloading In Swift, the + operator denotes concatenation when its operands are arrays. One array plus another results in an array with the elements of the former array followed by those of the latter. let oneTwoThree: [Int] = [1, 2, 3] let fourFiveSix: [Int] = [4, 5, 6] oneTwoThree + fourFiveSix // [1, 2, 3, 4, 5, 6] If we look at the operator’s declaration in the standard library, we see that it’s provided in an unqualified extension on Array: extension Array { @inlinable public static func + (lhs: Array, rhs: Array) -> Array {} } The Swift compiler is responsible for resolving API calls to their corresponding implementations. If an invocation matches more than one declaration, the compiler selects the most specific one available. To illustrate this point, consider the following conditional extension on Array, which defines the + operator to perform member-wise addition for arrays whose elements conform to Numeric: extension Array where Element: Numeric { public static func + (lhs: Array, rhs: Array) -> Array { return Array(zip(lhs, rhs).map {$0 + $1}) } } oneTwoThree + fourFiveSix // [5, 7, 9] Because the requirement of Element: Numeric is more specific than the unqualified declaration in the standard library, the Swift compiler resolves + to this function instead. Now, these new semantics may be perfectly acceptable — indeed preferable. But only if you’re aware of them. The problem is that if you so much as import a module containing such a declaration you can change the behavior of your entire app without even knowing it. This problem isn’t limited to matters of semantics; it can also come about as a result of ergonomic affordances. Function Shadowing In Swift, function declarations can specify default arguments for trailing parameters, making them optional (though not necessarily Optional) for callers. For example, the top-level function dump(_:name :indent:maxDepth:maxItems:) has an intimidating number of parameters: @discardableResult func dump<T>(_ value: T, name: String? = nil, indent: Int = 0, maxDepth: Int = .max, maxItems: Int = .max) -> T But thanks to default arguments, you need only specify the first one to call it: dump(" ") // " " Alas, this source of convenience can become a point of confusion when method signatures overlap. Imagine a hypothetical module that — not being familiar with the built-in dump function — defines a dump(_:) that prints the UTF-8 code units of a string. public func dump(_ string: String) { print(string.utf8.map {$0}) } The dump function declared in the Swift standard library takes an unqualified generic T argument in its first parameter (which is effectively Any). Because String is a more specific type, the Swift compiler will choose the imported dump(_:) method when it’s available. dump(" ") // [240, 159, 143, 173, 240, 159, 146, 168] Unlike the previous example, it’s not entirely clear that there’s any ambiguity in the competing declarations. After all, what reason would a developer have to think that their dump(_:) method could in any way be confused for dump(_:name:indent:maxDepth:maxItems:)? Which leads us to our final example, which is perhaps the most confusing of all… String Interpolation Pollution In Swift, you can combine two strings by interpolation in a string literal as an alternative to concatenation. let name = "Swift" let greeting = "Hello, \(name)!" // "Hello, Swift!" This has been true from the first release of Swift. However, with the new ExpressibleByString Interpolation protocol in Swift 5, this behavior can no longer be taken for granted. Consider the following extension on the default interpolation type for String: extension DefaultStringInterpolation { public mutating func appendInterpolation<T>(_ value: T) where T: StringProtocol { self.appendInterpolation(value.uppercased() as TextOutputStreamable) } } StringProtocol inherits, among other things the TextOutputStreamable and CustomString Convertible protocols, making it more specific than the appendInterpolation method declared by DefaultStringInterpolation that would otherwise be invoked when interpolating String values. public struct DefaultStringInterpolation: StringInterpolationProtocol { @inlinable public mutating func appendInterpolation<T>(_ value: T) where T: TextOutputStreamable, T: CustomStringConvertible {} } Once again, the Swift compiler’s notion of specificity causes behavior to go from expected to unexpected. If the previous declaration is made accessible by any module in your app, it would change the behavior of all interpolated string values. let greeting = "Hello, \(name)!" // "Hello, SWIFT!" Admittedly, this last example’s a bit contrived; an implementor has to go out of their way to make the implementation not recursive. But consider this a stand-in for a less-obvious example that’s more likely to actually happen in real-life. Given the rapid and upward trajectory of the language, it’s not unreasonable to expect that these problems will be solved at some point in the future. But what are we to do in the meantime? Here are some suggestions for managing this behavior both as an API consumer and as an API provider. Strategies for API Consumers As an API consumer, you are — in many ways — beholden to the constraints imposed by imported dependencies. It really shouldn’t be your problem to solve, but at least there are some remedies available to you. Add Hints to the Compiler Often, the most effective way to get the compiler to do what you want is to explicitly cast an argument down to a type that matches the method you want to call. Take our example of the dump(_:) method from before: by downcasting to CustomStringConvertible from String, we can get the compiler to resolve the call to use the standard library function instead. dump(" dump(" ") // [240, 159, 143, 173, 240, 159, 146, 168] " as CustomStringConvertible) // " " Scoped Import Declarations As discussed in a previous article you can use Swift import declarations to resolve naming collisions. Unfortunately, scoping an import to certain APIs in a module doesn’t currently prevent extensions from applying to existing types. That is to say, you can’t import an adding(_:) method without also importing an overloaded + operator declared in that module. Fork Dependencies If all else fails, you can always solve the problem by taking it into your own hands. If you don’t like something that a third-party dependency is doing, simply fork the source code, get rid of the stuff you don’t want, and use that instead. (You could even try to get them to upstream the change.) Unfortunately, this strategy won’t work for closed-source modules, including the ones in Apple’s SDKs. “Radar or GTFO”, I suppose. Strategies for API Provider As someone developing an API, it’s ultimately your responsibility to be deliberate and considerate in your design decisions. As you think about the greater consequences of your actions, here are some things to keep in mind: Be More Discerning with Generic Constraints Unqualified <T> generic constraints are the same as Any. If it makes sense to do so, consider making your constraints more specific to reduce the chance of overlap with unrelated declarations. Isolate Core Functionality from Convenience As a general rule, code should be organized into modules such that module is responsible for a single responsibility. If it makes sense to do so, consider packaging functionality provided by types and methods in a module that is separate from any extensions you provide to built-in types to improve their usability. Until it’s possible to pick and choose which behavior we want from a module, the best option is to give consumers the choice to opt-in to features if there’s a chance that they might cause problems downstream. Avoid Collisions Altogether Of course, it’d be great if you could knowingly avoid collisions to begin with… but that gets into the whole “unknown unknowns” thing, and we don’t have time to get into epistemology now. So for now, let’s just say that if you’re aware of something maybe being a conflict, a good option might be to avoid it altogether. For example, if you’re worried that someone might get huffy about changing the semantics of fundamental arithmetic operators, you could choose a different one instead, like .+: infix operator .+: AdditionPrecedence extension Array where Element: Numeric { static func .+ (lhs: Array, rhs: Array) -> Array { return Array(zip(lhs, rhs).map {$0 + $1}) } } oneTwoThree + fourFiveSix // [1, 2, 3, 4, 5, 6] oneTwoThree .+ fourFiveSix // [5, 7, 9] As developers, we’re perhaps less accustomed to considering the wider impact of our decisions. Code is invisible and weightless, so it’s easy to forget that it even exists after we ship it. But in Swift, our decisions have impacts beyond what’s immediately understood so it’s important to be considerate about how we exercise our responsibilities as stewards of our APIs. Swift Operators What would a program be without operators? A mishmash of classes, namespaces, conditionals, loops, and namespaces signifying nothing. Operators are what do the work of a program. They are the very execution of an executable; the teleological driver of every process. Operators are a topic of great importance for developers and the focus of this week’s NSHipster article. Operator Precedence and Associativity If we were to dissect an expression — say 1 + 2 — and decompose it into constituent parts, we would find one operator and two operands: 1 + 2 left operand operator right operand Expressions are expressed in a single, flat line of code, from which the compiler constructs an AST, or abstract syntax tree: + 1 2 For compound expressions, like 1 + 2 * 3 or 5 - 2 + 3, the compiler uses rules for operator precedence and associativity to resolve the expression into a single value. Operator precedence rules, similar to the ones you learned in primary school, determine the order in which different kinds of operators are evaluated. In this case, multiplication has a higher precedence than addition, so 2 * 3 evaluates first. 1 + (2 * 3) left operand operator right operand + 1 * 2 3 Associativity determines the order in which operators with the same precedence are resolved. If an operator is left-associative, then the operand on the left-hand side is evaluated first: ((5 - 2) + 3); if right-associative, then the right-hand side operator is evaluated first: 5 - (2 + 3). Arithmetic operators are left-associative, so 5 - 2 + 3 evaluates to 6. (5 - 2) + 3 left operand operator right operand + 3 5 2 Swift Operators The Swift Standard Library includes most of the operators that a programmer might expect coming from another language in the C family, as well as a few convenient additions like the nil-coalescing operator (??) and pattern match operator (~=), as well as operators for type checking (is), type casting (as, as?, as!) and forming open or closed ranges (..., ..<). Infix Operators Swift uses infix notation for binary operators (as opposed to, say Reverse Polish Notation). The Infix operators are grouped below according to their associativity and precedence level, in descending order: BitwiseShiftPrecedence << >> Bitwise left shift Bitwise right shift MultiplicationPrecedence * &* & Multiply Multiply, ignoring overflow Bitwise AND / &/ Divide Divide, ignoring overflow % &% Remainder Remainder, ignoring overflow AdditionPrecedence + &+ | Add Add with overflow Bitwise OR - &- ^ Subtract Subtract with overflow Bitwise XOR RangeFormationPrecedence ..< ... Half-open range Closed range CastingPrecedence is as Type check Type cast NilCoalescingPrecedence ?? nil Coalescing ComparisonPrecedence < >= === Less than Greater than or equal Identical <= == !== Less than or equal Equal Not identical > != ~= Greater than Not equal Pattern match LogicalConjunctionPrecedence && Logical AND LogicalDisjunctionPrecedence || Logical OR DefaultPrecedence (None) AssignmentPrecedence = += &= Assign Add and assign Bitwise AND and assign *= -= ^= Multiply and assign Subtract and assign Bitwise XOR and assign /= <<= |= Divide and assign Left bit shift and assign Bitwise OR and assign %= >>= Remainder and assign Right bit shift and assign Operator precedence groups were originally defined with numerical precedence. For example, multiplicative operators defined a precedence value of 150, so they were evaluated before additive operators, which defined a precedence value of 140. In Swift 3, operators changed to define precedence by partial ordering to form a DAG or directed acyclic graph. For detailed information about this change, read Swift Evolution proposal SE-0077 “Improved operator declarations”. Unary Operators In addition to binary operators that take two operands, there are also unary operators, which take a single operand. Prefix Operators Prefix operators come before the expression they operate on. Swift defines a handful of these by default: • +: Unary plus • -: Unary minus • !: Logical NOT • ~: Bitwise NOT • ...: Open-ended partial range • ..<: Closed partial range For example, the ! prefix operator negates a logical value of its operand and the - prefix operator negates the numeric value of its operand. !true // false -(1.0 + 2.0) // -3.0 The increment/decrement (++ / --) operators were removed in Swift 3. This was one of the first changes to be made as part of the Swift Evolution process after the language was released as open source. In the proposal, Chris Lattner describes how these operators can be confusing, and argues for why they aren’t needed in the language. Postfix Operators Unary operators can also come after their operand, as is the case for the postfix variety. These are less common; the Swift Standard Library declares only the open-ended range postfix operator, .... let fruits = [" ", " ", " ", " fruits[3...] // [" ", " "] ", " "] Ternary Operators The ternary ?: operator is special. It takes three operands and functions like a single-line if-else statement: evaluate the logical condition on the left side of the ? and produces the expression on the left or right-hand side of the : depending on the result: true ? "Yes" : "No" // "Yes" In Swift, TernaryPrecedence is defined lower than DefaultPrecedence and higher than Assignment Precedence. But, in general, it’s better to keep ternary operator usage simple (or avoid them altogether). Operator Overloading Once an operator is declared, it can be associated with a type method or top-level function. When an operator can resolve different functions depending on the types of operands, then we say that the operator is overloaded. The most prominent examples of overloading can be found with the + operator. In many languages, + can be used to perform arithmetic addition (1 + 2 => 3) or concatenation for arrays and other collections ([1] + [2] => [1, 2] ). Developers have the ability to overload standard operators by declaring a new function for the operator symbol with the appropriate number and type of arguments. For example, to overload the * operator to repeat a String a specified number of times, you’d declare the following top-level function: func * (lhs: String, rhs: Int) -> String { guard rhs > 0 else { return "" } } return String(repeating: lhs, count: rhs) "hello" * 3 // hellohellohello This kind of language use is, however, controversial. (Any C++ developer would be all too eager to regale you with horror stories of the non-deterministic havoc this can wreak) Consider the following statement: [1, 2] + [3, 4] // [1, 2, 3, 4] By default, the + operator concatenates the elements of both arrays, and is implemented using a generic function definition. If you were to declare a specialized function that overloads the + for arrays of Double values to perform member-wise addition, it would override the previous concatenating behavior: // func + (lhs: [Double], rhs: [Double]) -> [Double] { return zip(lhs, rhs).map(+) } [1.0, 3.0, 5.0] + [2.0, 4.0, 6.0] // [3.0, 7.0, 11.0] Herein lies the original sin of operator overloading: ambiguous semantics. It makes sense that + would work on numbers — that’s maths. But if you really think about it, why should adding two strings together concatenate them? 1 + 2 isn’t 12 (except in Javascript). Is this really intuitive? …or is it just familiar. Something to keep in mind when deciding whether to overload an existing operator. By comparison, PHP uses . for string concatenation, whereas SQL uses ||; Objective-C doesn’t have an operator, per se, but will append consecutive string literals with whitespace. Defining Custom Operators One of the most exciting features of Swift (though also controversial) is the ability to define custom operators. Consider the exponentiation operator, **, found in many programming languages, but missing from Swift. It raises the left-hand number to the power of the right-hand number. (The ^ symbol, commonly used for superscripts, is already used by the bitwise XOR operator). Exponentiation has a higher operator precedence than multiplication, and since Swift doesn’t have a built-in precedence group that we can use, we first need to declare one ourselves: precedencegroup ExponentiationPrecedence { associativity: right higherThan: MultiplicationPrecedence } Now we can declare the operator itself: infix operator ** : ExponentiationPrecedence Finally, we implement a top-level function using our new operator: import Darwin func ** (lhs: Double, rhs: Double) -> Double { return pow(lhs, rhs) } 2 ** 3 // 8 We need to import the Darwin module to access the standard math function, pow(_:_:). (Alternatively, we could import Foundation instead to the same effect.) When you create a custom operator, consider providing a mutating variant as well: infix operator **= : AssignmentPrecedence func **= (lhs: inout Double, rhs: Double) { lhs = pow(lhs, rhs) } var n: Double = 10 n **= 1 + 2 // n = 1000 Use of Mathematical Symbols A custom operator can use combinations of the characters /, =, -, +, !, *, %, <, >, &, |, ^, or ~, and any characters found in the Mathematical Operators Unicode block, among others. This makes it possible to take the square root of a number with a single √ prefix operator: import Darwin prefix operator √ prefix func √ (_ value: Double) -> Double { return sqrt(value) } √4 // 2 Or consider the ± operator, which can be used either as an infix or prefix operator to return a tuple with the sum and difference: infix operator ± : AdditionPrecedence func ± <T: Numeric>(lhs: T, rhs: T) -> (T, T) { return (lhs + rhs, lhs - rhs) } prefix operator ± prefix func ± <T: Numeric>(_ value: T) -> (T, T) { return 0 ± value } 2 ± 3 // (5, -1) ±4 // (4, -4) For more examples of functions using mathematical notation in Swift, check out Euler. Custom operators are hard to type, and therefore hard to use, so exercise restraint with exotic symbols. Code should be typed, not be copy-pasted. When overriding or defining new operators in your own code, make sure to follow these guidelines: 1. Don’t create an operator unless its meaning is obvious and undisputed. Seek out any potential conflicts to ensure semantic consistency. 2. Pay attention to the precedence and associativity of custom operators, and only define new operator groups as necessary. 3. If it makes sense, consider implementing assigning variants for your custom operator (e.g. += for +). Swift Literals 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. Standard Literals 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: Name Default Inferred Type Examples Integer Int 123, 0b1010, 0o644, 0xFF, Floating-Point Double 3.14, 6.02e23, 0xAp-2 String String "Hello", """ . . . """ Extended Grapheme Cluster Character Unicode Scalar Unicode.Scalar "A", "´", "\u{1F1FA}" Boolean Bool true, false Nil Optional nil Array Array [1, 2, 3] Dictionary Dictionary ["a": 1, "b": 2] "A", "é", " " 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 type nil as String? // 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 compilation let dictionary: [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: Name Default Inferred Type Examples Color NSColor / UIColor #colorLiteral(red: 1, green: 0, blue: 1, alpha: 1) Image NSImage / UIImage #imageLiteral(resourceName: "icon") File URL #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" as Character, "e", "l", "l", "o"] // Array<Character> ["h", "e", "l", "l", "o"] as Set<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. Literal Protocol Integer ExpressibleByIntegerLiteral Floating-Point ExpressibleByFloatLiteral String ExpressibleByStringLiteral Extended Grapheme Cluster ExpressibleByExtendedGraphemeClusterLiteral Unicode Scalar ExpressibleByUnicodeScalarLiteral Boolean ExpressibleByBooleanLiteral Nil ExpressibleByNilLiteral Array ExpressibleByArrayLiteral Dictionary ExpressibleByDictionaryLiteral To conform to a protocol, a type must implement its required initializer. For example, the Expressible ByIntegerLiteral 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: struct Fuzzy: Equatable { var value: Double } init(_ 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. extension Fuzzy: ExpressibleByBooleanLiteral { init(booleanLiteral value: 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: true is Bool // true true is Fuzzy // false (true as Fuzzy) is Fuzzy // true (false as Fuzzy).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). extension Fuzzy: ExpressibleByIntegerLiteral { init(integerLiteral value: Int) { self.init(Double(value)) } } extension Fuzzy: ExpressibleByFloatLiteral { init(floatLiteral value: Double) { self.init(value) } } With these protocols adopted, the Fuzzy type now looks and feels like a bona fide member of Swift standard library. let completelyTrue: Fuzzy = true let mostlyTrue: Fuzzy = 0.8 let mostlyFalse: 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. ExpressibleByString Interpolation Swift is designed — first and foremost — to be a safe language. Numbers and collections are checked for overflow, variables are always initialized before first use, optionals ensure that non-values are handled correctly, and any potentially unsafe operations are named accordingly. These language features go a long way to eliminate some of the most common programming errors, but we’d be remiss to let our guard down. Today, I want to talk about one of the most exciting new features in Swift 5: an overhaul to how values in string literals are interpolated by way of the ExpressibleByStringInterpolation protocol. A lot of folks are excited about the cool things you can do with it. (And rightfully so! We’ll get to all of that in just a moment) But I think it’s important to take a broader view of this feature to understand the full scope of its impact. Format strings are awful. After incorrect NULL handling, buffer overflows, and uninitialized variables, printf / scanf-style format strings are arguably the most problematic holdovers from C-style programming language. In the past 20 years, security professionals have documented hundreds of vulnerabilities related to format string vulnerabilities. It’s so commonplace that it’s assigned its very own Common Weakness Enumeration (CWE) category. Not only are they insecure, but they’re hard to use. Yes, hard to use. Consider the dateFormat property on DateFormatter, which takes an strftime format string. If we wanted to create a string representation of a date that included its year, we’d use "Y", as in "Y" for year …right? import Foundation let formatter = DateFormatter() formatter.dateFormat = "M/d/Y" formatter.string(from: Date()) // "2/4/2019" It sure looks that way, at least for the first 360-ish days of the year. But what happens when we jump to the last day of the year? let dateComponents = DateComponents(year: 2019, month: 12, day: 31) let date = Calendar.current.date(from: dateComponents)! formatter.string(from: date) // "12/31/2020" ( ) Huh what? Turns out "Y" is the format for the ISO week-numbering year, which returns 2020 for December 31st, 2019 because the following day is a Wednesday in the first week of the new year. What we actually want is "y". formatter.dateFormat = "M/d/y" formatter.string(from: date) // 12/31/2019 ( ) Format strings are the worst kind of hard to use, because they’re so easy to use incorrectly. And date format strings are the worst of the worst, because it may not be clear that you’re doing it wrong until it’s too late. They’re literal time bombs in your codebase. Take a moment now, if you haven’t already, to audit your code base for use of "Y" in date format strings when you actually meant to use "y". The problem up until now has been that APIs have had to choose between dangerous-but-expressive domain-specific languages (DSLs), such as format strings, and the correct-but-less-flexible method calls. New in Swift 5, the ExpressibleByStringInterpolation protocol allows for these kinds of APIs to be both correct and expressive. And in doing so, it overturns decades’ worth of problematic behavior. So without further ado, let’s look at what ExpressibleByStringInterpolation is and how it works: ExpressibleByStringInterpolation Types that conform to the ExpressibleByStringInterpolation protocol can customize how interpolated values (that is, values escaped by \( … )) in string literals. You can take advantage of this new protocol either by extending the default String interpolation type (DefaultStringInterpolation) or by creating a new type that conforms to ExpressibleByString Interpolation. For more information, see Swift Evolution proposal SE-0228: “Fix ExpressibleByStringInterpolation”. Extending Default String Interpolation By default, and prior to Swift 5, all interpolated values in a string literal were sent to directly to a String initializer. Now with ExpressibleByStringInterpolation, you can specify additional parameters as if you were calling a method (indeed, that’s what you’re doing under the hood). As an example, let’s revisit the previous mixup of "Y" and "y" and see how this confusion could be avoided with ExpressibleByStringInterpolation. By extending String’s default interpolation type (aptly-named DefaultStringInterpolation), we can define a new method called appendingInterpolation. The type of the first, unnamed parameter determines which interpolation methods are available for the value to be interpolated. In our case, we’ll define an appendInterpolation method that takes a Date argument and an additional component parameter of type Calendar.Component that we’ll use to specify which import Foundation extension DefaultStringInterpolation { mutating func appendInterpolation(_ value: Date, component: Calendar.Component) { let dateComponents = Calendar.current.dateComponents([component], from: value) } } self.appendInterpolation( dateComponents.value(for: component)! ) Now we can interpolate the date for each of the individual components: "\(date, component: .month)/\(date, component: .day)/\(date, component: .year)" // "12/31/2019" ( ) It’s verbose, yes. But you’d never mistake .yearForWeekOfYear, the calendar component equivalent of "Y", for what you actually want: .year. But really, we shouldn’t be formatting dates by hand like this anyway. We should be delegating that responsibility to a DateFormatter: You can overload interpolations just like any other Swift method, and having multiple with the same name but different type signatures. For example, we can define interpolators for dates and numbers that take a formatter of the corresponding type. import Foundation extension DefaultStringInterpolation { mutating func appendInterpolation(_ value: Date, formatter: DateFormatter) { self.appendInterpolation( formatter.string(from: value) ) } } mutating func appendInterpolation<T>(_ value: T, formatter: NumberFormatter) where T : Numeric { self.appendInterpolation( formatter.string(from: value as! NSNumber)! ) } This allows for a consistent interface to equivalent functionality, such as formatting interpolated dates and numbers. let dateFormatter = DateFormatter() dateFormatter.dateStyle = .full dateFormatter.timeStyle = .none "Today is \(Date(), formatter: dateFormatter)" // "Today is Monday, February 4, 2019" let numberformatter = NumberFormatter() numberformatter.numberStyle = .spellOut "one plus one is \(1 + 1, formatter: numberformatter)" // "one plus one is two" Implementing a Custom String Interpolation Type In addition to extending DefaultStringInterpolation, you can define custom string interpolation behavior on a custom type that conforms to ExpressibleByStringInterpolation. You might do that if any of the following is true: • You want to differentiate between literal and interpolated segments • You want to restrict which types can be interpolated • You want to support different interpolation behavior than provided by default • You want to avoid burdening the built-in string interpolation type with excessive API surface area For a simple example of this, consider a custom type that escapes values in XML, similar to one of the loggers that we described last week. Our goal: to provide a nice templating API that allows us to write XML / HTML and interpolate values in a way that automatically escapes characters like < and >. We’ll start simply with a wrapper around a single String value. struct XMLEscapedString: LosslessStringConvertible { var value: String init?(_ value: String) { self.value = value } } var description: String { return self.value } We add conformance to ExpressibleByStringInterpolation in an extension, just like any other protocol. It inherits from ExpressibleByStringLiteral, which requires an init(stringLiteral:) initializer. ExpressibleByStringInterpolation itself requires an init(stringInterpolation:) initializer that takes an instance of the required, associated StringInterpolation type. This associated StringInterpolation type is responsible for collecting all of the literal segments and interpolated values from the string literal. All literal segments are passed to the appendLiteral(_:) method. For interpolated values, the compiler finds the appendInterpolation method that matches the specified parameters. In this case, both literal and interpolated values are collected into a mutable string. The StringInterpolationProtocol, requires an initializer, init(literalCapacity :interpolationCount:); as an optional optimization, the capacity and interpolation counts can be used to, for example, allocate enough space to hold the resulting string. import Foundation extension XMLEscapedString: ExpressibleByStringInterpolation { init(stringLiteral value: String) { self.init(value)! } init(stringInterpolation: StringInterpolation) { self.init(stringInterpolation.value)! } struct StringInterpolation: StringInterpolationProtocol { var value: String = "" init(literalCapacity: Int, interpolationCount: Int) { self.value.reserveCapacity(literalCapacity) } mutating func appendLiteral(_ literal: String) { self.value.append(literal) } mutating func appendInterpolation<T>(_ value: T) where T: CustomStringConvertible { let escaped = CFXMLCreateStringByEscapingEntities( nil, value.description as NSString, nil )! as NSString } } } self.value.append(escaped as String) With all of this in place, we can now initialize XMLEscapedString with a string literal that automatically escapes interpolated values. (No XSS exploits for us, thank you!) let name = "<bobby>" let markup: XMLEscapedString = """ <p>Hello, \(name)!</p> """ print(markup) // <p>Hello, &lt;bobby&gt;!</p> One of the best parts of this functionality is how transparent its implementation is. For behavior that feels quite magical, you’ll never have to wonder how it works. Compare the string literal above to the equivalent API calls below: var interpolation = XMLEscapedString.StringInterpolation(literalCapacity: 15, interpolationCount: 1) interpolation.appendLiteral("<p>Hello, ") interpolation.appendInterpolation(name) interpolation.appendLiteral("!</p>") let markup = XMLEscapedString(stringInterpolation: interpolation) // <p>Hello, &lt;bobby&gt;!</p> Reads just like poetry, doesn’t it? For a more advanced example of ExpressibleByStringInterpolation, check out the Unicode Styling playground included in the sample code for the Flight School Guide to Swift Strings Seeing how ExpressibleByStringInterpolation works, it’s hard not to look around and find countless opportunities for where it could be used: • Formatting String interpolation offers a safer and easier-to-understand alternative to date and number format strings. • Escaping Whether its escaping entities in URLs, XML documents, shell command arguments, or values in SQL queries, extensible string interpolation makes correct behavior seamless and automatic. • Decorating Use string interpolation to create a type-safe DSL for creating attributed strings for apps and terminal output with ANSI control sequences for color and effects, or pad unadorned text to match the desired alignment. • Localizing Rather than relying on a a script that scans source code looking for matches on “NSLocalizedString”, string interpolation allows us to build tools that leverage the compiler to find all instances of localized strings. If you take all of these and factor in possible future support for compile-time constant expression, what you find is that Swift 5 may have just stumbled onto the new best way to deal with formatting. TextOutputStream print is among the most-used functions in the Swift standard library. Indeed, it’s the first function a programmer learns when writing “Hello, world!”. So it’s surprising how few of us are familiar with its other forms. For instance, did you know that the actual signature of print is print(_:separator:terminator:)? Or that it had a variant named print(_:separator:terminator:to:)? Shocking, I know. It’s like learning that your best friend “Chaz” goes by his middle name and that his full legal name is actually “R. Buckminster Charles Lagrand Jr.” — oh, and also, they’ve had an identical twin the whole time. Once you’ve taken a moment to collect yourself, read on to find out the whole truth about a function that you may have previously thought to need no further introduction. Let’s start by taking a closer look at that function declaration from before: func print<Target>(_ items: Any..., separator: String = default, terminator: String = default, to output: inout Target) where Target : TextOutputStream This overload of print takes a variable-length list of arguments, followed by separator and terminator parameters — both of which have default values. • separator is the string used to join the representation of each element in items into a single string. By default, this is a space (" "). • terminator is the string appended to the end of the printed representation. By default, this is a newline ("\n"). The last parameter, output takes a mutable instance of a generic Target type that conforms to the Text OutputStream protocol. An instance of a type conforming to TextOutputStream can be passed to the print(_:to:) function to capture and redirect strings from standard output. Implementing a Custom Text Output Stream Type Due to the mercurial nature of Unicode, you can’t know what characters lurk within a string just by looking at it. Between combining marks, format characters, unsupported characters, variation sequences, ligatures, digraphs, and other presentational forms, a single extended grapheme cluster can contain much more than meets the eye. So as an example, let’s create a custom type that conforms to TextOutputStream. Instead of writing a string to standard output verbatim, we’ll have it inspect each constituent code point. Conforming to the TextOutputStream protocol is simply a matter of fulfilling the write(_:) method requirement. protocol TextOutputStream { mutating func write(_ string: String) } In our implementation, we iterate over each Unicode.Scalar value in the passed string; the enumerated() collection method provides the current offset on each loop. At the top of the method, a guard statement bails out early if the string is empty or a newline (this reduces the amount of noise in the console). struct UnicodeLogger: TextOutputStream { mutating func write(_ string: String) { guard !string.isEmpty && string != "\n" else { return } } } for (index, unicodeScalar) in string.unicodeScalars.lazy.enumerated() { let name = unicodeScalar.name ?? "" let codePoint = String(format: "U+%04X", unicodeScalar.value) print("\(index): \(unicodeScalar) \(codePoint)\t\(name)") } To use our new UnicodeLogger type, initialize it and assign it to a variable (with var) so that it can be passed as an inout argument. Anytime we want to get an X-ray of a string instead of merely printing its surface representation, we can tack on an additional parameter to our print statement. Doing so allows us to reveal a secret about the emoji character : it’s actually a sequence of four individual emoji joined by ZWJ characters — seven code points in total! print(" ") // Prints: " " var logger = UnicodeLogger() print(" ", to: &logger) // Prints: // 0: U+1F468 MAN // 1: U+200D ZERO WIDTH JOINER // 2: U+1F469 WOMAN // 3: U+200D ZERO WIDTH JOINER // 4: U+1F467 GIRL // 5: U+200D ZERO WIDTH JOINER // 6: U+1F467 GIRL In Swift 5.0, you can access the name of a scalar value by through its Unicode properties property. In the meantime, we can use a string transform to pull the name for us (we just need to strip some cruft at either end). import Foundation extension Unicode.Scalar { var name: String? { guard var escapedName = "\(self)".applyingTransform(.toUnicodeName, reverse: false) else { return nil } escapedName.removeFirst(3) // remove "\\N{" escapedName.removeLast(1) // remove "}" } } return escapedName For more information, see SE-0211: “Add Unicode Properties to Unicode.Scalar”. Ideas for Using Custom Text Output Streams Now that we know about an obscure part of the Swift standard library, what can we do with it? As it turns out, there are plenty of potential use cases for TextOutputStream. To get a better sense of what they are, consider the following examples: Logging to Standard Error By default, Swift print statements are directed to standard output (stdout). If you wanted to instead direct to standard error (stderr), you could create a new text output stream type and use it in the following way: import func Darwin.fputs import var Darwin.stderr struct StderrOutputStream: TextOutputStream { mutating func write(_ string: String) { fputs(string, stderr) } } var standardError = StderrOutputStream() print("Error!", to: &standardError) Writing Output to a File The previous example of writing to stderr can be generalized to write to any stream or file by instead creating an output stream to a FileHandle (for which standard error is accessible through a type property). import Foundation struct FileHandlerOutputStream: TextOutputStream { private let fileHandle: FileHandle let encoding: String.Encoding init(_ fileHandle: FileHandle, encoding: String.Encoding = .utf8) { self.fileHandle = fileHandle self.encoding = encoding } mutating func write(_ string: String) { if let data = string.data(using: encoding) { } } } fileHandle.write(data) Following this approach, you can customize print to write to a file instead of a stream. let url = URL(fileURLWithPath: " /path/to/file.txt ") let fileHandle = try FileHandle(forWritingTo: url) var output = FileHandlerOutputStream(fileHandle) print("\(Date())", to: &output) Escaping Streamed Output As a final example, let’s imagine a situation in which you find yourself frequently copy-pasting console output into a form on some website. Unfortunately, the website has the unhelpful behavior of trying to parse < and > as if they were HTML. Rather than taking an extra step to escape the text each time you post to the site, you could create a TextOutputStream that takes care of that for you automatically (in this case, we use an XML-escaping function that we found buried deep in Core Foundation). import Foundation struct XMLEscapingLogger: TextOutputStream { mutating func write(_ string: String) { guard !string.isEmpty && string != "\n", let xmlEscaped = CFXMLCreateStringByEscapingEntities(nil, string as NSString, nil) else { return } } } print(xmlEscaped) var logger = XMLEscapingLogger() print("<3", to: &logger) // Prints "&lt;3" Printing is a familiar and convenient way for developers to understand the behavior of their code. It complements more comprehensive techniques like logging frameworks and debuggers, and — in the case of Swift — proves to be quite capable in its own right. Have any other cool ideas for using TextOutputStream that you’d like to share? Let us know on Twitter! Regular Expressions in Swift Like everyone else in the Pacific Northwest, we got snowed-in over the weekend. To pass the time, we decided to break out our stash of board games: Carcassonne, Machi Koro, Power Grid, Pandemic; we had plenty of excellent choices available. But cooped up in our home for the afternoon, we decided on a classic: Cluedo. Me, I’m an avid fan of Cluedo — and yes, that’s what I’m going to call it. Because despite being born and raised in the United States, where the game is sold and marketed exclusively under the name “Clue”, I insist on referring to it by its proper name. (Otherwise, how would we refer to the 1985 film adaptation?) Alas, my relentless pedantry often causes me to miss out on invitations to play. If someone were to ask: var invitation = "Hey, would you like to play Clue?" invitation.contains("Cluedo") // false …I’d have no idea what they were talking about. If only they’d bothered to phrase it properly, there’d be no question about their intention: invitation = "Fancy a game of Cluedo™?" invitation.contains("Cluedo") // true Of course, a regular expression would allow me to relax my exacting standards. I could listen for /Clue(do)?™?/ and never miss another invitation. But who can be bothered to figure out regexes in Swift, anyway? Well, sharpen your pencils, gather your detective notes, and warm up your 6-sided dice, because this week on NSHipster, we crack the case of the cumbersome class known as NSRegularExpression. Who killed regular expressions in Swift? I have a suggestion: It was NSRegularExpression, in the API, with the cumbersome usability. In any other language, regular expressions are something you can sling around in one-liners. • Need to substitute one word for another? Boom: regular expression. • Need to extract a value from a templated string? Boom: regular expression. • Need to parse XML? Boom: regular expression actually, you should really use an XML parser in this case But in Swift, you have to go through the trouble of initializing an NSRegularExpression object and converting back and forth from String ranges to NSRange values. It’s a total drag. Here’s the good news: 1. You don’t need NSRegularExpression to use regular expressions in Swift. 2. Recent additions in Swift 4 and 5 make it much, much nicer to use NSRegularExpression when you need to. Let’s interrogate each of these points, in order: Regular Expressions without NSRegularExpression You may be surprised to learn that you can, in fact, use regular expressions in a Swift one-liner — you just have to bypass NSRegularExpression entirely. Matching Strings Against Patterns When you import the Foundation framework, the Swift String type automatically gets access to NSString instance methods and initializers. Among these is range(of:options:range:locale:), which finds and returns the first range of the specified string. Normally, this performs a by-the-books substring search operation. Meh. But, if you pass the .regularExpression option, the string argument is matched as a pattern. Eureka! Let’s take advantage of this lesser-known feature to dial our Cluedo sense to the “American” setting. import Foundation let invitation = "Fancy a game of Cluedo™?" invitation.range(of: #"\bClue(do)?™?\b"#, options: .regularExpression) != nil // true If the pattern matches the specified string, the method returns a Range<String.Index> object. Therefore, checking for a non-nil value tells us whether or not a match occurred. The method itself provides default arguments to the options, range, and locale parameters; by default, it performs a localized, unqualified search over the entire string in the current locale. Within a regular expression, the ? operator matches the preceding character or group zero or one times. We use it in our pattern to make the “-do” in “Cluedo” optional (accommodating both the American and correct spelling), and allow a trademark symbol (™) for anyone wishing to be prim and proper about it. The \b metacharacters match if the current position is a word boundary, which occurs between word (\w) and non-word (\W) characters. Anchoring our pattern to match on word boundaries prevents false positives like “Pseudo-Cluedo”. The raw string literals introduced in Swift 5 are a perfect fit for declaring regular expression patterns, which frequently contain backslashes (such as for the \b metacharacter) that would otherwise need to be escaped. That solves our problem of missing out on invitations. The next question is how to respond in kind. Searching and Retrieving Matches Rather than merely checking for a non-nil value, we can actually use the return value to see the string that got matched. import Foundation func respond(to invitation: String) { if let range = invitation.range(of: #"\bClue(do)?™?\b"#, options: .regularExpression) { switch invitation[range] { case "Cluedo": print("I'd be delighted to play!") case "Clue": print("Did you mean Cluedo? If so, then yes!") default: fatalError("(Wait... did I mess up my regular expression?)") } } else { print("Still waiting for an invitation to play Cluedo.") } } Conveniently, the range returned by the range(of:...) method can be plugged into a subscript to get a Substring for the matching range. Finding and Replacing Matches Once we’ve established that the game is on, the next step is to read the instructions. (Despite its relative simplicity, players often forget important rules in Cluedo, such as needing to be in a room in order to suggest it.) Naturally, we play the original, British edition. But as a favor to the American players, I’ll go to the trouble of localizing the rules on-the-fly. For example, the victim’s name in the original version is “Dr. Black”, but in America, it’s “Mr. Boddy”. We automate this process using the replacingOccurrences(of:with:options:) method — again passing the .regularExpression option. import Foundation let instructions = """ The object is to solve by means of elimination and deduction the problem of the mysterious murder of Dr. Black. """ instructions.replacingOccurrences( of: #"(Dr\.|Doctor) Black"#, with: "Mr. Boddy", options: .regularExpression ) Regular Expressions with NSRegularExpression There are limits to what we can accomplish with the range(of:options:range:locale:) and replacingOccurrences(of:with:options:) methods. Specifically, you’ll need to use NSRegularExpression if you want to match a pattern more than once in a string or extract values from capture groups. Enumerating Matches with Positional Capture Groups A regular expression can match its pattern one or more times on a string. Within each match, there may be one or more capture groups, which are designated by enclosing by parentheses in the regex pattern. For example, let’s say you wanted to use regular expressions to determine how many players you need to play Cluedo: import Foundation let description = """ Cluedo is a game of skill for 2-6 players. """ let pattern = #"(\d+)[ \p{Pd}](\d+) players"# let regex = try NSRegularExpression(pattern: pattern, options: []) This pattern includes two capture groups for one or more digits, as denoted by the + operator and \d metacharacter, respectively. Between them, we match on a set containing a space and any character in the Unicode General Category Pd (Punctuation, dash). This allows us to match on hyphen / minus (-), en dash (–), em dash (—), or whatever other exotic typographical marks we might encounter. The en dash is the correct punctuation for denoting a span or range of numbers. We can use the enumerateMatches(in:options:range) method to try each match until we find one that has three ranges (the entire match and the two capture groups), whose captured values can be used to initialize a valid range. In the midst of all of this, we use the new(-ish) NSRange(_: in:) and Range(_:in:) initializers to convert between String and NSString index ranges. Once we find such a match, we set the third closure parameter (a pointer to a Boolean value) to true as a way to tell the enumeration to stop. var playerRange: ClosedRange<Int>? let nsrange = NSRange(description.startIndex..<description.endIndex, in: description) regex.enumerateMatches(in: description, options: [], range: nsrange) { (match, _, stop) in guard let match = match else { return } if match.numberOfRanges == 3, let firstCaptureRange = Range(match.range(at: 1), in: description), let secondCaptureRange = Range(match.range(at: 2), in: description), { } } let lowerBound = Int(description[firstCaptureRange]), let upperBound = Int(description[secondCaptureRange]), lowerBound > 0 && lowerBound < upperBound playerRange = lowerBound...upperBound stop.pointee = true print(playerRange!) // Prints "2...6" Each capture group can be accessed by position by calling the range(at:) method on the match object. *Sigh*. What? No, we like the solution we came up with — longwinded as it may be. It’s just… gosh, wouldn’t it be nice if we could play Cluedo solo? Matching Multi-Line Patterns with Named Capture Groups The only thing making Cluedo a strictly multiplayer affair is that you need some way to test a theory without revealing the answer to yourself. If we wanted to write a program to check that without spoiling anything for us, one of the first steps would be to parse a suggestion into its component parts: suspect, location, and weapon. let suggestion = """ I suspect it was Professor Plum, \ in the Dining Room, \ with the Candlestick. """ When writing a complex regular expression, it helps to know exactly which features your platform supports. In the case of Swift, NSRegularExpression is a wrapper around the ICU regular expression engine, which lets us do some really nice things: let pattern = #""" (?xi) (?<suspect> ((Miss|Ms\.) \h Scarlett?) | ((Colonel | Col\.) \h Mustard) | ((Reverend | Mr\.) \h Green) | (Mrs\. \h Peacock) | ((Professor | Prof\.) \h Plum) | ((Mrs\. \h White) | ((Doctor | Dr\.) \h Orchid)) ),?(?-x: in the ) (?<location> Kitchen | Ballroom | Conservatory | Dining \h Room | Library | Lounge | Hall | Study ),?(?-x: with the ) (?<weapon> Candlestick | Knife | (Lead(en)?\h)? Pipe | Revolver | Rope | Wrench ) """# let regex = try NSRegularExpression(pattern: pattern, options: []) First off, declaring the pattern with a multi-line raw string literal is a huge win in terms of readability. That, in combination with the x and i flags within those groups, allows us to use whitespace to organize our expression into something more understandable. Another nicety is how this pattern uses named capture groups (designated by (?<name>)) instead of the standard, positional capture groups from the previous example. Doing so allows us to access groups by name by calling the range(withName:) method on the match object. Beyond the more outlandish maneuvers, we have affordances for regional variations, including the spelling of “Miss Scarlet(t)”, the title of “Mr. / Rev. Green”, and the replacement of Mrs. White with Dr. Orchid in standard editions after 2016. let nsrange = NSRange(suggestion.startIndex..<suggestion.endIndex, in: suggestion) if let match = regex.firstMatch(in: suggestion, options: [], range: nsrange) { for component in ["suspect", "location", "weapon"] { let nsrange = match.range(withName: component) if nsrange.location != NSNotFound, let range = Range(nsrange, in: suggestion) { print("\(component): \(suggestion[range])") } } // // // // } Prints: "suspect: Professor Plum" "location: Dining Room" "weapon: Candlestick" Regular expressions are a powerful tool for working with text, but it’s often a mystery how to use them in Swift. We hope this article has helped clue you into finding a solution. Swift GYB 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 (“Generate Your Projects”) the Haskell package SYB (“Scrap Your Boilerplate”) . 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: % for type in codable_types: mutating func encode(_ value: ${type}) throws % end Evaluating the GYB template results in the following declarations: mutating mutating mutating mutating mutating mutating mutating mutating mutating mutating mutating mutating mutating mutating func func func func func func func func func func func func func func encode(_ encode(_ encode(_ encode(_ encode(_ encode(_ encode(_ encode(_ encode(_ encode(_ encode(_ encode(_ encode(_ encode(_ value: value: value: value: value: value: value: value: value: value: value: value: value: value: Bool) throws String) throws Double) throws Float) throws Int) throws Int8) throws Int16) throws Int32) throws Int64) throws UInt) throws UInt8) throws UInt16) throws UInt32) throws 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 2183 $ wc 5790 -l Codable.swift.gyb Codable.swift.gyb -l Codable.swift Codable.swift 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. Installing GYB GYB isn’t part of the standard Xcode toolchain, so you won’t find it with xcrun. Instead, you can download it using Homebrew: $ brew install nshipster/formulae/gyb Alternatively, you can download the source code and 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 If you go this route, be sure to 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). Using GYB in Xcode 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'] }% class Character { var name: String % for ability in abilities: var ${ability}: Int % end } Evaluating this with GYB produces the following Swift code: class Character { var name: String } var var var var var var strength: Int dexterity: Int constitution: Int intelligence: Int wisdom: Int charisma: Int A lot of repetition in code is a 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 Python packages like json, xml, and csv, so you can parse pretty much any kind of file you might encounter: %{ import csv } % with open('path/to/file.csv') as file: % for row in csv.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. SwiftSyntax SwiftSyntax is a Swift library that lets you parse, analyze, generate, and transform Swift source code. It’s based on the libSyntax library, and was spun out from the main Swift language repository in August 2017. Together, the goal of these projects is to provide safe, correct, and intuitive facilities for structured editing, which is described thusly: What is structured editing? It’s an editing strategy that is keenly aware of the structure of source code, not necessarily its representation (i.e. characters or bytes). This can be achieved at different granularities: replacing an identifier, changing a call to global function to a method call, or indenting and formatting an entire source file based on declarative rules. At the time of writing, SwiftSyntax is still in development and subject to API changes. But you can start using it today to work with Swift source code in a programmatic way. It’s currently used by the Swift Migrator, and there are ongoing efforts to adopt the tool, both internally and externally. How Does It Work? To understand how SwiftSyntax works, let’s take a step back and look at the Swift compiler architecture: The Swift compiler is primarily responsible for turning Swift code into executable machine code. The process is divided up into several discrete steps, starting with the parser, which generates an abstract syntax tree, (AST). From there, semantic analysis is performed on the syntax to produce a type-checked AST, which lowered into Swift Intermediate Language; the SIL is transformed and optimized and itself lowered into LLVM IR, which is ultimately compiled into machine code. The most important takeaway for our discussion is that SwiftSyntax operates on the AST generated at the first step of the compilation process. As such, it can’t tell you any semantic or type information about code. Contrast this with something like SourceKit, which operates with a much more complete understanding of Swift code. This additional information can be helpful for implementing editor features like codecompletion or navigating across files. But there are plenty of important use cases that can be satisfied on a purely syntactic level, such as code formatting and syntax highlighting. Demystifying the AST Abstract syntax trees can be difficult to understand in the abstract. So let’s generate one and see what it looks like. Consider the following single-line Swift file, which declares a function named one() that returns the value 1: func one() -> Int { return 1 } Run the swiftc command on this file passing the -frontend -emit-syntax arguments: $ xcrun swiftc -frontend -emit-syntax ./One.swift The result is a chunk of JSON representing the AST. Its structure becomes much clearer once you reformat the JSON: { "kind": "SourceFile", "layout": [{ "kind": "CodeBlockItemList", "layout": [{ "kind": "CodeBlockItem", "layout": [{ "kind": "FunctionDecl", "layout": [null, null, { "tokenKind": { "kind": "kw_func" }, "leadingTrivia": [], "trailingTrivia": [{ "kind": "Space", "value": 1 }], "presence": "Present" }, { "tokenKind": { "kind": "identifier", "text": "one" }, "leadingTrivia": [], "trailingTrivia": [], "presence": "Present" }, ... The Python json.tool module offers a convenient way to format JSON. It comes standard in macOS releases going back as far as anyone can recall. For example, here’s how you could use it with the redirected compiler output: $ xcrun swiftc -frontend -emit-syntax ./One.swift | python -m json.tool At the top-level, we have a SourceFile consisting of CodeBlockItemList elements and their constituent CodeBlockItem parts. This example has a single CodeBlockItem for the function declaration (Function Decl), which itself comprises subcomponents including a function signature, parameter clause, and return clause. The term trivia is used to describe anything that isn’t syntactically meaningful, like whitespace. Each token can have one or more pieces of leading and trailing trivia. For example, the space after the Int in the return clause (-> Int) is represented by the following piece of trailing trivia. { } "kind": "Space", "value": 1 Working Around File System Constraints SwiftSyntax generates abstract syntax trees by delegating system calls to swiftc. However, this requires code to be associated with a file in order to be processed, and it’s often useful to work with code as a string. One way to work around this constraint is to write code to a temporary file and pass that to the compiler. We’ve written about temporary files in the past, but nowadays, there’s a much nicer API for working with them that’s provided by the Swift Package Manager itself. In your Package.swift file, add the following package dependency, and add the "Utility" dependency to the appropriate target: .package(url: "https://github.com/apple/swift-package-manager.git", from: "0.3.0"), Now, you can import the Basic module and use its TemporaryFile API like so: import Basic import Foundation let code: String let tempfile = try TemporaryFile(deleteOnClose: true) defer { tempfile.fileHandle.closeFile() } tempfile.fileHandle.write(code.data(using: .utf8)!) let url = URL(fileURLWithPath: tempfile.path.asString) let sourceFile = try SyntaxTreeParser.parse(url) What Can You Do With It? Now that we have a reasonable idea of how SwiftSyntax works, let’s talk about some of the ways that you can use it! Writing Swift Code: The Hard Way The first and least compelling use case for SwiftSyntax is to make writing Swift code an order of magnitude more difficult. SwiftSyntax, by way of its SyntaxFactory APIs, allows you to generate entirely new Swift code from scratch. Unfortunately, doing this programmatically isn’t exactly a walk in the park. For example, consider the following code: import SwiftSyntax let structKeyword = SyntaxFactory.makeStructKeyword(trailingTrivia: .spaces(1)) let identifier = SyntaxFactory.makeIdentifier("Example", trailingTrivia: .spaces(1)) let leftBrace = SyntaxFactory.makeLeftBraceToken() let rightBrace = SyntaxFactory.makeRightBraceToken(leadingTrivia: .newlines(1)) let members = MemberDeclBlockSyntax { builder in builder.useLeftBrace(leftBrace) builder.useRightBrace(rightBrace) } let structureDeclaration = StructDeclSyntax { builder in builder.useStructKeyword(structKeyword) builder.useIdentifier(identifier) builder.useMembers(members) } print(structureDeclaration) Whew. So what did all of that effort get us? struct Example { } Oofa doofa. This certainly isn’t going to replace GYB for everyday code generation purposes. (In fact, libSyntax and SwiftSyntax both make extensive use of gyb to generate its interfaces.) But this interface can be quite useful when precision matters. For instance, you might use SwiftSyntax to implement a fuzzer for the Swift compiler, using it to randomly generate arbitrarily-complex-butostensibly-valid programs to stress test its internals. Rewriting Swift Code The example provided in the SwiftSyntax README shows how to write a program to take each integer literal in a source file and increment its value by one. Looking at that, you can already extrapolate out to how this might be used to create a canonical swift-format tool. But for the moment, let’s consider a considerably less productive — and more seasonally appropriate ( ) — use of source rewriting: import SwiftSyntax public class ZalgoRewriter: SyntaxRewriter { public override func visit(_ token: TokenSyntax) -> Syntax { guard case let .stringLiteral(text) = token.tokenKind else { return token } } } return token.withKind(.stringLiteral(zalgo(text))) What’s that zalgo function all about? You’re probably better off not knowing… Anyway, running this rewriter on your source code transforms all string literals in the following manner: // Before print("Hello, world!") // After print("H͞͏̟̂ͩel̵ͬ͆͜ĺ͎̪̣͠ơ̡̼͓̋͝, w͎̽̇ͪ͢ǒ̩͔̲̕͝r̷̡̠͓̉͂l̘̳̆ͯ̊d!") Spooky, right? Highlighting Swift Code Let’s conclude our look at SwiftSyntax with something that’s actually useful: a Swift syntax highlighter. A syntax highlighter, in this sense, describes any tool that takes source code and formats it in a way that’s more suitable for display in HTML. NSHipster is built on top of Jekyll, and uses the Ruby library Rouge to colorize the example code you see in every article. However, due to Swift’s relatively complex syntax and rapid evolution, the generated HTML isn’t always 100% correct. Instead of messing with a pile of regular expressions, we could instead build a syntax highlighter that leverages SwiftSyntax’s superior understanding of the language. At its core, the implementation is rather straightforward: implement a subclass of SyntaxRewriter and override the visit(_:) method that’s called for each token as a source file is traversed. By switching over each of the different kinds of tokens, you can map them to the HTML markup for their corresponding highlighter tokens. For example, numeric literals are represented with <span> elements whose class name begins with the letter m (mf for floating-point, mi for integer, etc.). Here’s the corresponding code in our Syntax Rewriter subclass: import SwiftSyntax class SwiftSyntaxHighlighter: SyntaxRewriter { var html: String = "" override func visit(_ token: TokenSyntax) -> Syntax { switch token.tokenKind { … case .floatingLiteral(let string): html += "<span class=\"mf\">\(string)</span>" case .integerLiteral(let string): if string.hasPrefix("0b") { html += "<span class=\"mb\">\(string)</span>" } else if string.hasPrefix("0o") { html += "<span class=\"mo\">\(string)</span>" } else if string.hasPrefix("0x") { html += "<span class=\"mh\">\(string)</span>" } else { html += "<span class=\"mi\">\(string)</span>" } … default: break } } } return token Although SyntaxRewriter has specialized visit(_:) methods for each of the different kinds of syntax elements, I found it easier to handle everything in a single switch statement. (Printing unhandled tokens in the default branch was a really helpful way to find any cases that I wasn’t already handling). It’s not the most elegant of implementations, but it was a convenient place to start given my limited understanding of the library. Anyway, after a few hours of development, I was able to generate reasonable colorized output for a wide range of Swift syntactic features: The project comes with a library and a command line tool. Go ahead and try it out and let me know what you think! Swift Documentation Code structure and organization is a matter of pride for developers. Clear and consistent code signifies clear and consistent thought. Even though the compiler lacks a discerning palate when it comes to naming, whitespace, or documentation, it makes all the difference for human collaborators. This week, we’ll be documenting the here and now of documentation in Swift. Since the early ’00s, Headerdoc has been Apple’s preferred documentation standard. Starting off as little more than a Perl script that parsed trumped-up Javadoc comments, Headerdoc would eventually be the engine behind Apple’s developer documentation online and in Xcode. But like so much of the Apple developer ecosystem, Swift changed everything. In the spirit of “Out with the old, in with the new”, Xcode 7 traded Headerdoc for fan favorite Markdown — specifically, Swift-flavored Markdown. Documentation Comments & Swift-Flavored Markdown Even if you’ve never written a line of Markdown before, you can get up to speed in just a few minutes. Here’s pretty much everything you need to know: Basic Markup Documentation comments look like normal comments, but with a little something extra. Single-line documentation comments have three slashes (///). Multi-line documentation comments have an extra star in their opening delimiter (/** ... */). Standard Markdown rules apply inside documentation comments: • Paragraphs are separated by blank lines. • Unordered lists are marked by bullet characters (-, +, *, or •). • Ordered lists use numerals (1, 2, 3, …) followed by either a period (1.) or a right parenthesis (1)). • Headers are preceded by # signs or underlined with = or -. • Both links and images work, with web-based images pulled down and displayed directly in Xcode. /** # Lists You can apply *italic*, **bold**, or `code` inline styles. ## Unordered Lists - Lists are great, - but perhaps don't nest; - Sub-list formatting... - ...isn't the best. ## Ordered Lists */ 1. 2. 3. 4. Ordered lists, too, for things that are sorted; Arabic numerals are the only kind supported. Summary & Description The leading paragraph of a documentation comment becomes the documentation Summary. Any additional content is grouped together into the Discussion section. If a documentation comment starts with anything other than a paragraph, all of its content is put into the Discussion. Parameters & Return Values Xcode recognizes a few special fields and makes them separate from a symbol’s description. The parameters, return value, and throws sections are broken out in the Quick Help popover and inspector when styled as a bulleted item followed by a colon (:). • Parameters: Start the line with Parameter <param name>: and the description of the parameter. • Return values: Start the line with Returns: and information about the return value. • Thrown errors: Start the line with Throws: and a description of the errors that can be thrown. Since Swift doesn’t type-check thrown errors beyond Error conformance, it’s especially important to document errors properly. /** Creates a personalized greeting for a recipient. - Parameter recipient: The person being greeted. - Throws: `MyError.invalidRecipient` if `recipient` is "Derek" (he knows what he did). - Returns: A new string saying hello to `recipient`. */ func greeting(to recipient: String) throws -> String { guard recipient != "Derek" else { throw MyError.invalidRecipient } } return "Greetings, \(recipient)!" Are you documenting a function whose method signature has more arguments than a Hacker News thread about tabs vs. spaces? Break out your parameters into a bulleted list underneath a Parameters: callout: /// Returns the magnitude of a vector in three dimensions /// from the given components. /// /// - Parameters: /// - x: The *x* component of the vector. /// - y: The *y* component of the vector. /// - z: The *z* component of the vector. func magnitude3D(x: Double, y: Double, z: Double) -> Double { return sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2)) } Additional Fields In addition to Parameters, Throws and Returns, Swift-flavored Markdown defines a handful of other fields, which can be loosely organized in the following way: Algorithm/Safety Information Metadata General Notes & Exhortations • Precondition • Author • Attention • Postcondition • Authors • Bug • Requires • Copyright • Experiment • Invariant • Date • Note • Complexity • SeeAlso • Remark • Important • Since • ToDo • Warning • Version Each of these fields is rendered in Quick Help as a bold header followed by a block of text: Field Header: The text of the subfield is displayed starting on the next line. Code blocks Demonstrate the proper usage or implementation details of a function by embedding code blocks. Inset code blocks by at least four spaces: /** The area of the `Shape` instance. Computation depends on the shape of the instance. For a triangle, `area` is equivalent to: let height = triangle.calculateHeight() let area = triangle.base * height / 2 */ var area: CGFloat { get } Fenced code blocks are also recognized, delimited by either three backticks (`) or tildes (~): /** The perimeter of the `Shape` instance. Computation depends on the shape of the instance, and is equivalent to: ~~~ // Circles: let perimeter = circle.radius * 2 * Float.pi // Other shapes: let perimeter = shape.sides.map { $0.length } .reduce(0, +) ~~~ */ var perimeter: CGFloat { get } Documentation Is My New Bicycle How does this look when applied to an entire class? Quite nice, actually! /// A two-wheeled, human-powered mode of transportation. class Bicycle { /// Frame and construction style. enum Style { /// A style for streets or trails. case road /// A style for long journeys. case touring /// A style for casual trips around town. case cruiser } /// A style for general-purpose transportation. case hybrid /// Mechanism for converting pedal power into motion. enum Gearing { /// A single, fixed gear. case fixed } /// A variable-speed, disengageable gear. case freewheel(speeds: Int) /// Hardware used for steering. enum Handlebar { /// A casual handlebar. case riser /// An upright handlebar. case café /// A classic handlebar. case drop } /// A powerful handlebar. case bullhorn /// The style of the bicycle. let style: Style /// The gearing of the bicycle. let gearing: Gearing /// The handlebar of the bicycle. let handlebar: Handlebar /// The size of the frame, in centimeters. let frameSize: Int /// The number of trips traveled by the bicycle. private(set) var numberOfTrips: Int /// The total distance traveled by the bicycle, in meters. private(set) var distanceTraveled: Double /** Initializes a new bicycle with the provided parts and specifications. - Parameters: - style: The style of the bicycle - gearing: The gearing of the bicycle - handlebar: The handlebar of the bicycle - frameSize: The frame size of the bicycle, in centimeters - Returns: A beautiful, brand-new bicycle, custom-built just for you. */ init(style: Style, gearing: Gearing, handlebar: Handlebar, frameSize centimeters: Int) { self.style = style self.gearing = gearing self.handlebar = handlebar self.frameSize = centimeters } self.numberOfTrips = 0 self.distanceTraveled = 0 /** Take a bike out for a spin. Calling this method increments the `numberOfTrips` and increases `distanceTraveled` by the value of `meters`. - Parameter meters: The distance to travel in meters. - Precondition: `meters` must be greater than 0. */ func travel(distance meters: Double) { precondition(meters > 0) } } distanceTraveled += meters numberOfTrips += 1 Option-click on the initializer declaration, and the description renders beautifully with a bulleted list: Open Quick Documentation for the method travel, and the parameter is parsed out into a separate field, as expected: MARK / TODO / FIXME In Objective-C, the preprocessor directive #pragma mark is used to divide functionality into meaningful, easy-to-navigate sections. In Swift, the same can be accomplished with the comment // MARK:. The following comments are surfaced in the Xcode source navigator: • // MARK: • // TODO: • // FIXME: Other conventional comment tags, such as NOTE and XXX aren’t recognized by Xcode. As with #pragma, marks followed by a single dash (-) are preceded with a horizontal divider To show these new tags in action, here’s how the Bicycle class could be extended to adopt the Custom StringConvertible protocol, and implement the description property. // MARK: - CustomStringConvertible extension Bicycle: CustomStringConvertible { public var description: String { var descriptors: [String] = [] switch self.style { case .road: descriptors.append("A case .touring: descriptors.append("A case .cruiser: descriptors.append("A case .hybrid: descriptors.append("A transportation") } road bike for streets or trails") touring bike for long journeys") cruiser bike for casual trips around town") hybrid bike for general-purpose switch self.gearing { case .fixed: descriptors.append("with a single, fixed gear") case .freewheel(let n): descriptors.append("with a \(n)-speed freewheel gear") } switch self.handlebar { case .riser: descriptors.append("and case .café: descriptors.append("and case .drop: descriptors.append("and case .bullhorn: descriptors.append("and } casual, riser handlebars") upright, café handlebars") classic, drop handlebars") powerful bullhorn handlebars") descriptors.append("on a \(frameSize)\" frame") // FIXME: Use a distance formatter descriptors.append("with a total of \(distanceTraveled) meters traveled over \(numberOfTrips) trips.") // TODO: Allow bikes to be named? } } return descriptors.joined(separator: ", ") Bringing everything together in code: var bike = Bicycle(style: .road, gearing: .freewheel(speeds: 8), handlebar: .drop, frameSize: 53) bike.travel(distance: 1_500) // Trip around the town bike.travel(distance: 200) // Trip to the store print(bike) // "A road bike for streets or trails, with a 8-speed freewheel gear, and classic, drop handlebars, on a 53" frame, with a total of 1700.0 meters traveled over 2 trips." At the time of writing, there’s no official tool for transforming documentation comments into something more tangible than Quick Help panels in Xcode, Fortunately, where necessity arises, open source (often) delivers. Jazzy Jazzy is a terrific open-source command-line utility that transforms your project’s documentation comments into a set of Apple-like HTML documentation (but that nice vintage style, before that whole redesign). Jazzy uses Xcode’s SourceKitService to read your beautifully written type and method descriptions. Install Jazzy as a gem, then run from the root of your project folder to generate documentation. $ gem install jazzy $ jazzy Running xcodebuild Parsing ... building site jam out ♪♫ to your fresh new docs in `docs` Take a peek at a Jazzy-generated documentation for the Bicycle class. Although the tooling and documentation around Swift is still developing, one would be wise to adopt good habits early, by using the new Markdown capabilities for documentation, as well as MARK: comments in Swift code going forward. Go ahead and add it to your TODO: list. Swift Property Observers 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, struct S { // Stored Property var stored: String = "stored" } // Computed Property var computed: 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. struct S { var stored: String { willSet { print("willSet was called") print("stored is now equal to \(self.stored)") print("stored will be set to \(newValue)") } } } didSet { print("didSet was called") 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: var s = 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 accessor person.name = @"Johnny"; // ...is equivalent to [person setName:@"Johnny"]; // ...which gets compiled to objc_msgSend(person, @selector(setName:), @"Johnny"); // ...whose synthesized implementation yields person->_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: var name: 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: struct NormalizedText { enum Error: Swift.Error { case empty case excessiveLength case unsupportedCharacters } static let maximumLength = 32 private(set) var value: String init(_ string: String) throws { if string.isEmpty { throw Error.empty } guard let value = string.applyingTransform(.stripDiacritics, reverse: false)? .uppercased(), value.canBeConverted(to: .ascii) else { throw Error.unsupportedCharacters } guard value.count < NormalizedText.maximumLength else { throw Error.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 Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch 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: struct Track { var title: String var audioURL: URL } class TrackViewController: UIViewController { var player: AVPlayer? var track: Track? { willSet { } self.player?.pause() didSet { guard let track = self.track else { return } self.title = track.title } } } let item = AVPlayerItem(url: 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. Cocoa TimeInterval, Date, and Date Interval Nestled between Madrid’s Centro and Salamanca districts, just a short walk from the sprawling Buen Retiro Park, The Prado Museum boasts an extensive collection of works from Europe’s most celebrated painters. But if, during your visit, you begin to tire of portraiture commissioned by 17th-century Spanish monarchs, consider visiting the northernmost room of the 1st floor — Sala 002. There you’ll find this Baroque era painting by the French artist Simon Vouet. You’d be forgiven for wondering why this pair of young women, brandishing a hook and spear, stand menacingly over a cowering old man while a mob of cherubim tears at his back. It is, of course, allegorical: reading the adjacent placard, you’ll learn that this piece is entitled Time defeated by Hope and Beauty. The old man? That’s Time. See the hourglass in his hand and scythe at his feet? Take a moment, standing in front of this painting, to reflect on the enigmatic nature of time. Think now about how our limited understanding of time is reflected in — or perhaps exacerbated by — the naming of the Foundation date and time APIs. It’s about time we got them straight. Seconds are the fundamental unit of time. They’re also the only unit that has a fixed duration. Months vary in length (“30 days hath September”), as do years (“53 weeks hath 71 years every cycle of 400”). Certain years pick up an extra day (leap years are misnamed if you think about it), and days gain and lose an hour from daylight saving time (thanks, Benjamin Franklin). And that’s to say nothing of leap seconds, which are responsible for such oddities as the 61 second minute, the 3601 second hour, and, of course, the 1209601 second fortnight. TimeInterval (née NSTimeInterval) is a typealias for Double that represents duration as a number of seconds. You’ll see it as a parameter or return type for APIs that deal with a duration of time. Being a double-precision floating-point number, TimeInterval can represent submultiples in its fraction, (though for anything beyond millisecond precision, you’ll want to use something else). Date and Time It’s unfortunate that the Foundation type representing time is named Date. Colloquially, one typically distinguishes “dates” from “times” by saying that the former has to do with calendar days and the latter has more to do with the time of day. But Date is entirely orthogonal from calendars, and contrary to its name represents an absolute point in time. Why NSDate and not NSTime? Our guess is that the originators of this API wanted to match its counterpart in java.util.date when EOF targeted both Java and Objective-C. Another source of confusion for Date is that, despite representing an absolute point in time, it’s defined by a time interval since a reference date: public struct Date : ReferenceConvertible, Comparable, Equatable { public typealias ReferenceType = NSDate fileprivate var _time: TimeInterval } … The reference date, in this case, is the first instant of January 1, 2001, Greenwich Mean Time (GMT). While we’re on the subject of conjectural sidebars, does anyone know why Apple created a new standard instead of using, say, the Unix Epoch (January 1, 1970)? 2001 was the year that Mac OS X was first released, but NSDate pre-NSDates that from its NeXT days. Was it perhaps a hedge against Y2K? Date Intervals and Time Intervals DateInterval is a recent addition to Foundation. Introduced in iOS 10 and macOS Sierra, this type represents a closed interval between two absolute points in time (again, in contrast to TimeInterval, which represents a duration in seconds). So what is this good for? Consider the following use cases: Getting the Date Interval of a Calendar Unit In order to know the time of day for a point in time — or what day it is in the first place — you need to consult a calendar. From there, you can determine the range of a particular calendar unit, like a day, month, or year. The Calendar method dateInterval(of:for:) makes this really easy to do: let calendar = Calendar.current let date = Date() let dateInterval = calendar.dateInterval(of: .month, for: date) Because we’re invoking Calendar, we can be confident in the result that we get back. Look how it handles daylight saving transition without breaking a sweat: let dstComponents = DateComponents(year: 2018, month: 11, day: 4) calendar.dateInterval(of: .day, for: calendar.date(from: dstComponents)!)?.duration // 90000 seconds It’s 2018. Don’t you think that it’s time you stopped hard-coding secondsInDay = 86400? Calculating Intersections of Date Intervals For this example, let’s return to The Prado Museum and admire its extensive collection of paintings by Rubens — particularly this apparent depiction of the god of Swift programming. Rubens, like Vouet, painted in the Baroque tradition. The two were contemporaries, and we can determine the full extent of how they overlap in the history of art with the help of DateInterval: import Foundation let calendar = Calendar.current // Simon Vouet // 9 January 1590 – 30 June 1649 let vouet = DateInterval(start: calendar.date(from: DateComponents(year: 1590, month: 1, day: 9))!, end: calendar.date(from: DateComponents(year: 1649, month: 6, day: 30))!) // Peter Paul Rubens // 28 June 1577 – 30 May 1640 let rubens = DateInterval(start: calendar.date(from: DateComponents(year: 1577, month: 6, day: 28))!, end: calendar.date(from: DateComponents(year: 1640, month: 5, day: 30))!) let overlap = rubens.intersection(with: vouet)! calendar.dateComponents([.year], from: overlap.start, to: overlap.end) // 50 years According to our calculations, there was a period of 50 years where both painters were living. We can even take things a step further and use DateIntervalFormatter to provide a nice representation of that time period: let formatter = DateIntervalFormatter() formatter.timeStyle = .none formatter.dateTemplate = "%Y" formatter.string(from: overlap) // "1590 – 1640" Beautiful. You might as well print this code out, frame it, and hang it next to The Judgement of Paris. The fact is, we still don’t really know what time is (or if it even actually exists). But I’m hopeful that we, as developers, will find the beauty in Foundation’s Date APIs, and in time, learn how to overcome our lack of understanding. That does it for this week’s article. See you all next time. CharacterSet In Japan, there’s a comedy tradition known as Manzai (漫才). It’s kind of a cross between stand up and vaudeville, with a straight man and a funny man delivering rapid-fire jokes that revolve around miscommunication and wordplay. As it were, we’ve been working on a new routine as a way to introduce the subject for this week’s article, CharacterSet, and wanted to see what you thought: Is CharacterSet a Set<Character>? キャラクターセットではないキャラクターセット? Of course not! もちろん違います! What about NSCharacterSet? 何エンエスキャラクタセットは? That's an old reference. それは古いリファレンスです。 Then what do you call a collection of characters? 何と呼ばれる文字の集合ですか? That would be a String! それは文字列でしょ! (╯° 益 °)╯ 彡 ┻━┻ 無駄無駄無駄無駄無駄無駄無駄 (Yeah, we might need to workshop this one a bit more.) All kidding aside, CharacterSet is indeed ripe for miscommunication and wordplay (so to speak): it doesn’t store Character values, and it’s not a Set in the literal sense. So what is CharacterSet and how can we use it? Let’s find out! (行きましょう!) CharacterSet (and its reference type counterpart, NSCharacterSet) is a Foundation type used to trim, filter, and search for characters in text. In Swift, a Character is an extended grapheme cluster (really just a String with a length of 1) that comprises one or more scalar values. CharacterSet stores those underlying Unicode.Scalar values, rather than Character values, as the name might imply. The “set” part of CharacterSet refers not to Set from the Swift standard library, but instead to the SetAlgebra protocol, which bestows the type with the same interface: contains(_:), insert(_:), union(_:), intersection(_:), and so on. Predefined Character Sets CharacterSet defines constants for sets of characters that you’re likely to work with, such as letters, numbers, punctuation, and whitespace. Most of them are self-explanatory and, with only a few exceptions, correspond to one or more Unicode General Categories. Type Property Unicode General Categories & Code Points alphanumerics L*, M*, N* letters L*, M* * capitalizedLetters Lt lowercaseLetters Ll uppercaseLetters Lu, Lt nonBaseCharacters M* decimalDigits Nd punctuationCharacters P* symbols S* whitespaces Zs, U+0009 newlines U+000A – U+000D, U+0085, U+2028, U+2029 whitespacesAndNewlines Z*, U+000A – U+000D, U+0085 controlCharacters Cc, Cf illegalCharacters Cn A common mistake is to use capitalizedLetters when what you actually want is uppercase Letters. Unicode actually defines three cases: lowercase, uppercase, and titlecase. You can see this in the Latin script used for Czech as well as Serbo-Croatian and other South Slavic languages, in which digraphs like “dž” are considered single letters, and have separate forms for lowercase (dž), uppercase (DŽ), and titlecase (Dž). The capitalizedLetters character set contains only a few dozen of those titlecase digraphs. The remaining predefined character set, decomposables, is derived from the decomposition type and mapping of characters. Trimming Leading and Trailing Whitespace Perhaps the most common use for CharacterSet is to remove leading and trailing whitespace from text. """ """.trimmingCharacters(in: .whitespacesAndNewlines) // " " You can use this, for example, when sanitizing user input or preprocessing text. Predefined URL Component Character Sets In addition to the aforementioned constants, CharacterSet provides predefined values that correspond to the characters allowed in various components of a URL: • urlUserAllowed • urlPasswordAllowed • urlHostAllowed • urlPathAllowed • urlQueryAllowed • urlFragmentAllowed Escaping Special Characters in URLs Only certain characters are allowed in certain parts of a URL without first being escaped. For example, spaces must be percent-encoded as %20 (or +) when part of a query string like https ://nshipster.com/search/?q=character%20set. URLComponents takes care of percent-encoding components automatically, but you can replicate this functionality yourself using the addingPercentEncoding(withAllowedCharacters:) method and passing the appropriate character set: let query = "character set" query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) // "character%20set" Internationalized domain names encode non-ASCII characters using Punycode instead of percentencoding (for example, NSHipster.中国 would be NSHipster.xn–fiqy6j) Punycode encoding / decoding isn’t currently provided by Apple SDKs. Building Your Own In addition to these predefined character sets, you can create your own. Build them up character by character, inserting multiple characters at a time by passing a string, or by mixing and matching any of the predefined sets. Validating User Input You might create a CharacterSet to validate some user input to, for example, allow only lowercase and uppercase letters, digits, and certain punctuation. var allowed = CharacterSet() allowed.formUnion(.lowercaseLetters) allowed.formUnion(.uppercaseLetters) allowed.formUnion(.decimalDigits) allowed.insert(charactersIn: "!@#$%&") func validate(_ input: String) -> Bool { return input.unicodeScalars.allSatisfy { allowed.contains($0) } } Depending on your use case, you might find it easier to think in terms of what shouldn’t be allowed, in which case you can compute the inverse character set using the inverted property: let disallowed = allowed.inverted func validate(_ input: String) -> Bool { return input.rangeOfCharacter(from: disallowed) == nil } Caching Character Sets If a CharacterSet is created as the result of an expensive operation, you may consider caching its bitmapRepresentation for later reuse. For example, if you wanted to create CharacterSet for Emoji, you might do so by enumerating over the Unicode code space (U+0000 – U+1F0000) and inserting the scalar values for any characters with Emoji properties using the properties property added in Swift 5 by SE-0221 “Character Properties”: import Foundation var emoji = CharacterSet() for codePoint in 0x0000...0x1F0000 { guard let scalarValue = Unicode.Scalar(codePoint) else { continue } // Implemented in Swift 5 (SE-0221) // https://github.com/apple/swift-evolution/blob/master/proposals/ 0221-character-properties.md if scalarValue.properties.isEmoji { emoji.insert(scalarValue) } } The resulting bitmapRepresentation is a 16KB Data object. emoji.bitmapRepresentation // 16385 bytes You could store that in a file somewhere in your app bundle, or embed its Base64 encoding as a string literal directly in the source code itself. extension CharacterSet { static var emoji: CharacterSet { let base64Encoded = """ AAAAAAgE/wMAAAAAAAAAAAAAAAAA... """ let data = Data(base64Encoded: base64Encoded)! } } return CharacterSet(bitmapRepresentation: data) CharacterSet.emoji.contains(" ") // true Because the Unicode code space is a closed range, CharacterSet can express the membership of a given scalar value using a single bit in a bit map, rather than using a universal hashing function like a conventional Set. On top of that, CharacterSet does some clever optimizations, like allocating on a per-plane basis and representing sets of contiguous scalar values as ranges, if possible. Much like our attempt at a Manzai routine at the top of the article, some of the meaning behind CharacterSet is lost in translation. NSCharacterSet was designed for NSString at a time when characters were equivalent to 16-bit UCS-2 code units and text rarely had occasion to leave the Basic Multilingual Plane. But with Swift’s modern, Unicode-compliant implementations of String and Character, the definition of terms has drifted slightly; along with its NS prefix, CharacterSet lost some essential understanding along the way. Nevertheless, CharacterSet remains a performant, specialized container type for working with collections of scalar values. FIN おしまい。 NLLanguageRecognizer 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 enumerate Substrings(in:options:using:) (neé CFStringTokenizer). NLLanguageRecognizer offers an extension of the functionality previously exposted through the dominantLanguage in NSLinguistic Tagger, 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: import NaturalLanguage let string = """ 私はガラスを食べられます。それは私を傷つけません。 """ let recognizer = 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! Language Translation Latin Sing, o Nero, the great Persian wars! Italian The 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: let string = """ 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. """ let languageRecognizer = 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) Language Confidence 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] Language Confidence (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 process String(_:) method: let string = """ Wenn ist das Nunstück git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput! """ let languageRecognizer = NLLanguageRecognizer() languageRecognizer.processString(string) let dominantLanguage = 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:): let textChecker = UITextChecker() let nsString = NSString(string: string) let stringRange = NSRange(location: 0, length: nsString.length) var offset = 0 repeat { let wordRange = textChecker.rangeOfMisspelledWord(in: string, range: stringRange, startingAt: offset, wrap: false, language: dominantLanguage.rawValue) guard wordRange.location != NSNotFound else { break } print(nsString.substring(with: wordRange)) offset = wordRange.upperBound } while true 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: let string = """ 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. """ let languageRecognizer = NLLanguageRecognizer() languageRecognizer.processString(string) let language = languageRecognizer.dominantLanguage!.rawValue // fr let speechSynthesizer = AVSpeechSynthesizer() let utterance = 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. Locale “You take delight not in a city’s seven or seventy wonders, but in the answer it gives to a question of yours.” ― Italo Calvino, Invisible Cities Localization (l10n) is the process of adapting your application for a specific market, or locale. Internationalization (i18n) is the process of preparing your app to be localized. Internationalization is a necessary, but not sufficient condition for localization. And whether or not you decide to localize your app for other markets, it’s always a good idea to do everything with internationalization in mind. The hardest thing about internationalization (aside from saying the word itself) is learning how to think outside of your cultural context. Unless you’ve had the privilege to travel or to meet people from other places, you may not even be aware that things could be any other way. But when you venture out into the world, everything from the price of bread to the letters in the alphabet to the numbers of hours in a day — all of that is up in the air. It can be absolutely overwhelming without a guide. Fortunately for us, we have Locale to show us the ways of the world. Foundation’s Locale type encapsulates the linguistic and cultural conventions of a user, which include: • Language and Orthography • Text Direction and Layout • Keyboards and Input Methods • Collation Ordering • Personal Name Formatting • Calendar and Date Formatting • Currency and Number Formatting • Units and Measurement Formatting • Use of Symbols, Colors, and Iconography Locale objects are often used as parameters for methods that perform localized operations like sorting a list of strings or formatting a date. Most of the time, you’ll access the Locale.current type property to use the user’s current locale. import Foundation let units = ["meter", "smoot", "agate", "ångström"] units.sorted { (lhs, rhs) in lhs.compare(rhs, locale: .current) == .orderedAscending } // => ["agate", "ångström", "meter", "smoot"] You can also construct a Locale that corresponds to a particular locale identifier. A locale identifier typically comprises an ISO 639-1 language code (such as en for English) and an ISO 3166-2 region code (such as US for the United States). let = Locale(identifier: "sv_SE") units.sorted { (lhs, rhs) in lhs.compare(rhs, locale: ) == .orderedAscending } // => ["agate", "meter", "smoot", "ångström"] Most methods and properties that take a Locale do so optionally or with a default value such that — if left unspecified — the current locale is used. Locale identifiers may also specify an explicit character encoding or other preferences like currency, calendar system, or number format with the following syntax: language[_region][.character-encoding][@modifier=value[, ...]] For example, the locale identifier de_DE.UTF8@collation=phonebook,currency=DDM specifies a German language locale in Germany that uses UTF-8 text encoding, phonebook collation, and the pre-Euro Deutsche Mark currency. Support for identifiers specifying other than language or region is inconsistent across system APIs and different platforms, so you shouldn’t rely on specific behavior. Users can change their locale settings in the “Language & Text” system preference on macOS and in “General > International” in the iOS Settings app. Terra Cognita Locale often takes a passive role, being passed into a property here and a method there. But if you give it a chance, it can tell you more about a place than you ever knew there was to know: • Language and Script (languageCode, scriptCode, exemplarCharacterSet) • Region (regionCode) • Collation (collationIdentifier, collatorIdentifier) • Calendar (calendar) • Currency (currencyCode, currencySymbol) • Numbers and Units (decimalSeparator, usesMetricSystem) • Quotation Delimiters (quotationBeginDelimiter / quotationEndDelimiter, alternateQuotationBeginDelimiter / alternateQuotationEndDelimiter) While this all may seem like fairly esoteric stuff, you’d be surprised by how many opportunities this kind of information unlocks for making a world-class app. It’s the small things, like knowing that quotation marks vary between locales: English “I can eat glass, it doesn’t harm me.” German „Ich kann Glas essen, das tut mir nicht weh.“ Japanese 「私はガラスを食べられます。それは私を傷つけません。」 So if you were building a component that added quotations around arbitrary text, you should use these properties rather than hard-coding English quotation marks. Si Fueris Romae… As if it weren’t enough to know, among other things, the preferred currency for every region on the planet, Locale can also tell you how you’d refer to it in a particular locale. For example, here in the USA, we call our country the United States. But that’s just an endonym; elsewhere, American has other names — exonyms. Like in France, it’s… import Foundation let let = Locale(identifier: "en_US") = Locale(identifier: "fr_FR") .localizedString(forIdentifier: // => "English (United States)" .identifier) .localizedString(forIdentifier: // => "anglais (États-Unis)" .identifier) Use this technique whenever you’re displaying locale, like in this screen from the Settings app, which shows language endonyms alongside exonyms: Behind the scenes, all of this locale information is provided by the Common Locale Data Repository CLDR, a companion project to the Unicode standard. Vox Populi The last stop in our tour of Locale is the preferredLanguages property. Contrary to what you might expect for a type (rather than an instance) property, the returned value depends on the current language preferences of the user. Each of the array elements is IETF BCP 47 language identifier and is returned in order of user preference. If your app communicates with a web app over HTTP, you might use this property to set the Accept-Language header field to give the server an opportunity to return localized resources: import Foundation let url = URL(string: "https://nshipster.com")! var request = URLRequest(url: url) let acceptLanguage = Locale.preferredLanguages.joined(separator: ", ") request.setValue(acceptLanguage, forHTTPHeaderField: "Accept-Language") Even if your server doesn’t yet localize its resources, putting this in place now allows you to flip the switch when the time comes — without having to push an update to the client. Neat! HTTP content negotiation is a complex topic, and apps interacting with localized, remote resources should use Locale.preferredLanguages as a starting point for generating a more precise Accept-Language field value. For example, you might specify an associated quality value like in en-SG, en;q=0.9 to tell the server “I prefer Singaporean English, but will accept other types of English”. For more information, see RFC 7231. Travel is like internationalization: it’s something that isn’t exactly fun at the time, but we do it because it makes us better people. Both activities expand our cultural horizons and allow us to better know people we’ll never actually meet. With Locale you can travel the world without going AFK. Formatter Conversion is a tireless errand in software development. Most programs boil down to some variation of transforming data into something more useful. In the case of user-facing software, making data human-readable is an essential task — and a complex one at that. A user’s preferred language, calendar, and currency can all factor into how information should be displayed, as can other constraints, such as a label’s dimensions. All of this is to say: calling description on an object just doesn’t cut it under most circumstances. Indeed, the real tool for this job is Formatter: an ancient, abstract class deep in the heart of the Foundation framework that’s responsible for transforming data into textual representations. Formatter’s origins trace back to NSCell, which is used to display information and accept user input in tables, form fields, and other views in AppKit. Much of the API design of (NS)Formatter reflects this. Back then, formatters came in two flavors: dates and numbers. But these days, there are formatters for everything from physical quantities and time intervals to personal names and postal addresses. And as if that weren’t enough to keep straight, a good portion of these have been soft-deprecated, or otherwise superseded by more capable APIs (that are also formatters). There are so many formatters in Apple SDKs that it’s impossible to keep them all in working memory. Apparently, this is as true of computers as it is for humans; at the time of writing, searching for “formatter” on developer.apple.com fails with a timeout! To make sense of everything, this week’s article groups each of the built-in formatters into one of four categories: Numbers and Quantities NumberFormatter MeasurementFormatter Dates, Times, and Durations DateFormatter ISO8601DateFormatter DateComponentsFormatter DateIntervalFormatter RelativeDateTimeFormatter People and Places PersonNameComponentsFormatter CNPostalAddressFormatter Lists and Items ListFormatter Formatting Numbers and Quantities Class Example Output Availability NumberFormatter “1,234.56” iOS 2.0 macOS 10.0+ MeasurementFormatter “-9.80665 m/s²” iOS 10.0+ macOS 10.12+ ByteCountFormatter “756 KB” iOS 6.0+ macOS 10.8+ EnergyFormatter “80 kcal” iOS 8.0+ macOS 10.10+ MassFormatter “175 lb” iOS 8.0+ macOS 10.10+ LengthFormatter “5 ft, 11 in” iOS 8.0+ macOS 10.10+ MKDistanceFormatter “500 miles” iOS 7.0+ macOS 10.9+ ByteCountFormatter, EnergyFormatter, MassFormatter, LengthFormatter, and MKDistanceFormatter are superseded by MeasurementFormatter. Legacy Formatter Measurement Formatter Unit ByteCountFormatter UnitInformationStorage EnergyFormatter UnitEnergy MassFormatter UnitMass LengthFormatter UnitLength MKDistanceFormatter UnitLength The only occasions in which you might still use EnergyFormatter, MassFormatter, or Length Formatter are when working with the HealthKit framework; these formatters provide conversion and interoperability with HKUnit quantities. NumberFormatter NumberFormatter covers every aspect of number formatting imaginable. For better or for worse (mostly for better), this all-in-one API handles ordinals and cardinals, mathematical and scientific notation, percentages, and monetary amounts in various flavors. It can even write out numbers in a few different languages! So whenever you reach for NumberFormatter, the first order of business is to establish what kind of number you’re working with and set the numberStyle property accordingly. Number Styles Number Style Example Output none 123 decimal 123.456 percent 12% scientific 1.23456789E4 spellOut one hundred twenty-three ordinal 3rd currency $1234.57 currencyAccounting ($1234.57) currencyISOCode USD1,234.57 currencyPlural 1,234.57 US dollars NumberFormatter also has a format property that takes a familiar SPRINTF(3)-style format string. As we’ve argued in a previous article, format strings are something to be avoided unless absolutely necessary. Rounding & Significant Digits To prevent numbers from getting annoyingly pedantic (“thirty-two point three three — repeating, of course…”), you’ll want to get a handle on NumberFormatter’s rounding behavior. Here, you have two options: • Set usesSignificantDigits to true to format according to the rules of significant figures var formatter = NumberFormatter() formatter.usesSignificantDigits = true formatter.minimumSignificantDigits = 1 // default formatter.maximumSignificantDigits = 6 // default formatter.string(from: formatter.string(from: formatter.string(from: formatter.string(from: formatter.string(from: 1234567) // 1234570 1234.567) // 1234.57 100.234567) // 100.235 1.23000) // 1.23 0.0000123) // 0.0000123 • Set usesSignificantDigits to false (or keep as-is, since that’s the default) to format according to specific limits on how many decimal and fraction digits to show (the number of digits leading or trailing the decimal point, respectively). var formatter = NumberFormatter() formatter.usesSignificantDigits = false formatter.minimumIntegerDigits = 0 // default formatter.maximumIntegerDigits = 42 // default (seriously) formatter.minimumFractionDigits = 0 // default formatter.maximumFractionDigits = 0 // default formatter.string(from: formatter.string(from: formatter.string(from: formatter.string(from: formatter.string(from: 1234567) // 1234567 1234.567) // 1235 100.234567) // 100 1.23000) // 1 0.0000123) // 0 If you need specific rounding behavior, such as “round to the nearest integer” or “round towards zero”, check out the roundingMode, roundingIncrement, and roundingBehavior properties. Locale Awareness Nearly everything about the formatter can be customized, including the grouping separator, decimal separator, negative symbol, percent symbol, infinity symbol, and how to represent zero values. Although these settings can be overridden on an individual basis, it’s typically best to defer to the defaults provided by the user’s locale. The advice to defer to user locale defaults has a critical exception: money Consider the following code that uses the default NumberFormatter settings for American and Japanese locales to format the same number: let number = 1234.5678 // let formatter = NumberFormatter() formatter.numberStyle = .currency let = Locale(identifier: "en_US") formatter.locale = formatter.string(for: number) // $1,234.57 let = Locale(identifier: "ja_JP") formatter.locale = formatter.string(for: number) // ¥ 1,235 NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; for (NSString *identifier in @[@"en_US", @"ja_JP"]) { numberFormatter.locale = [NSLocale localeWithLocaleIdentifier:identifier]; NSLog(@"%@: %@", identifier, [numberFormatter stringFromNumber:@(1234.5678)]); } // Prints "$1,234.57" and "¥ 1,235" At the time of writing, the difference between $1,234.57 and ¥ 1,235 is roughly equivalent to the price difference between a new MacBook Air and a Lightning - 3.5 mm Adapter. Make a mistake like that in your app, and someone — developer or user — is going to be pretty upset. Working with money in code is a deep topic, but the basic guidance is this: Use Decimal values (rather than Float or Double) and specify an explicit currency. For more information, check out the Flight School Guide to Swift Numbers and its companion Swift library, Money. MeasurementFormatter MeasurementFormatter was introduced in iOS 10 and macOS 10.12 as part of the full complement of APIs for performing type-safe dimensional calculations: • Unit subclasses represent units of measure, such as count and ratio • Dimension subclasses represent dimensional units of measure, such as mass and length, (which is the case for the overwhelming majority of the concrete subclasses provided, on account of them being dimensional in nature) • A Measurement is a quantity of a particular Unit • A UnitConverter converts quantities of one unit to a different, compatible unit MeasurementFormatter and its associated APIs are a intuitive — just a delight to work with, honestly. The only potential snag for newcomers to Swift (or Objective-C old-timers, perhaps) are the use of generics to constrain Measurement values to a particular Unit type. import Foundation // "The swift (Apus apus) can power itself to a speed of 111.6km/h" let speed = Measurement<UnitSpeed>(value: 111.6, unit: .kilometersPerHour) let formatter = MeasurementFormatter() formatter.string(from: speed) // 69.345 mph Configuring the Underlying Number Formatter By delegating much of its formatting responsibility to an underlying NumberFormatter property, MeasurementFormatter maintains a high degree of configurability while keeping a small API footprint. Readers with an engineering background may have noticed that the localized speed in the previous example gained an extra significant figure along the way. As discussed previously, we can enable uses SignificantDigits and set maximumSignificantDigits to prevent incidental changes in precision. formatter.numberFormatter.usesSignificantDigits = true formatter.numberFormatter.maximumSignificantDigits = 4 formatter.string(from: speed) // 69.35 mph Changing Which Unit is Displayed A MeasurementFormatter, by default, will use the preferred unit for the user’s current locale (if one exists) instead of the one provided by a Measurement value. Readers with a non-American background certainly noticed that the localized speed in the original example converted to a bizarre, archaic unit of measure known as “miles per hour”. You can override this default unit localization behavior by passing the providedUnit option. formatter.unitOptions = [.providedUnit] formatter.string(from: speed) // 111.6 km/h formatter.string(from: speed.converted(to: .milesPerHour)) // 69.35 mph Formatting Dates, Times, and Durations Class Example Output Availability DateFormatter “July 15, 2019” iOS 2.0 macOS 10.0+ ISO8601DateFormatter “2019-07-15” iOS 10.0+ macOS 10.12+ DateComponentsFormatter “10 minutes” iOS 8.0 macOS 10.10+ DateIntervalFormatter “6/3/19 - 6/7/19” iOS 8.0 macOS 10.10+ RelativeDateTimeFormatter “3 weeks ago” iOS 13.0+ macOS 10.15 DateFormatter DateFormatter is the OG class for representing dates and times. And it remains your best, first choice for the majority of date formatting tasks. For a while, there was a concern that it would become overburdened with responsibilities like its sibling NumberFormatter. But fortunately, recent SDK releases spawned new formatters for new functionality. We’ll talk about those in a little bit. Date and Time Styles The most important properties for a DateFormatter object are its dateStyle and timeStyle. As with NumberFormatter and its numberStyle, these date and time styles provide preset configurations for common formats. Style Date Time none “” “” short “11/16/37” “3:30 PM” medium “Nov 16, 1937” “3:30:32 PM” long “November 16, 1937” “3:30:32 PM” full “Tuesday, November 16, 1937 AD “3:30:42 PM EST” let date = Date() let formatter = DateFormatter() formatter.dateStyle = .long formatter.timeStyle = .long formatter.string(from: date) // July 15, 2019 at 9:41:00 AM PST formatter.dateStyle = .short formatter.timeStyle = .short formatter.string(from: date) // "7/16/19, 9:41:00 AM" NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateStyle:NSDateFormatterLongStyle]; [formatter setTimeStyle:NSDateFormatterLongStyle]; NSLog(@"%@", [formatter stringFromDate:[NSDate date]]); // July 15, 2019 at 9:41:00 AM PST [formatter setDateStyle:NSDateFormatterShortStyle]; [formatter setTimeStyle:NSDateFormatterShortStyle]; NSLog(@"%@", [formatter stringFromDate:[NSDate date]]); // 7/16/19, 9:41:00 AM dateStyle and timeStyle are set independently. So, to display just the time for a particular date, for example, you set dateStyle to none: let formatter = DateFormatter() formatter.dateStyle = .none formatter.timeStyle = .medium let string = formatter.string(from: Date()) // 9:41:00 AM NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateStyle:NSDateFormatterNoStyle]; [formatter setTimeStyle:NSDateFormatterMediumStyle]; NSLog(@"%@", [formatter stringFromDate:[NSDate date]]); // 9:41:00 AM As you might expect, each aspect of the date format can alternatively be configured individually, a la carte. For any aspiring time wizards NSDateFormatter has a bevy of different knobs and switches to play with. DateFormatter also has a dateFormat property that takes a familiar STRFTIME(3)-style format string. We’ve already called this out for NumberFormatter, but it’s a point that bears repeating: use presets wherever possible and only use custom format strings if absolutely necessary. ISO8601DateFormatter When we wrote our first article about NSFormatter back in 2013, we made a point to include discussion of Peter Hosey’s ISO8601DateFormatter’s as the essential open-source library for parsing timestamps from external data sources. Fortunately, we no longer need to proffer a third-party solution, because, as of iOS 10.0 and macOS 10.12, ISO8601DateFormatter is now built-in to Foundation. let formatter = ISO8601DateFormatter() formatter.date(from: "2019-07-15T09:41:00-07:00") // Jul 15, 2019 at 9:41 AM JSONDecoder provides built-in support for decoding ISO8601-formatted timestamps by way of the .iso8601 date decoding strategy. import Foundation let json = #""" [{ "body": "Hello, world!", "timestamp": "2019-07-15T09:41:00-07:00" }] """#.data(using: .utf8)! struct Comment: Decodable { let body: String let timestamp: Date } let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 let comments = try decoder.decode([Comment].self, from: json) comments.first?.timestamp // Jul 15, 2019 at 9:41 AM DateIntervalFormatter DateIntervalFormatter is like DateFormatter, but can handle two dates at once — specifically, a start and end date. let formatter = DateIntervalFormatter() formatter.dateStyle = .short formatter.timeStyle = .none let fromDate = Date() let toDate = Calendar.current.date(byAdding: .day, value: 7, to: fromDate)! formatter.string(from: fromDate, to: toDate) // "7/15/19 – 7/22/19" NSDateIntervalFormatter *formatter = [[NSDateIntervalFormatter alloc] init]; formatter.dateStyle = NSDateIntervalFormatterShortStyle; formatter.timeStyle = NSDateIntervalFormatterNoStyle; NSDate *fromDate = [NSDate date]; NSDate *toDate = [fromDate dateByAddingTimeInterval:86400 * 7]; NSLog(@"%@", [formatter stringFromDate:fromDate toDate:toDate]); // "7/15/19 – 7/22/19" Date Interval Styles Style Date Time none “” “” short “6/30/14 - 7/11/14” “5:51 AM - 7:37 PM” medium “Jun 30, 2014 - Jul 11, 2014” “5:51:49 AM - 7:38:29 PM” long “June 30, 2014 - July 11, 2014” “6:02:54 AM GMT-8 - 7:49:34 PM GMT-8” full “Monday, June 30, 2014 - Friday, July 11, 2014 “6:03:28 PM Pacific Standard Time - 7:50:08 PM Pacific Standard Time” When displaying business hours, such as “Mon – Fri: 8:00 AM – 10:00 PM”, use the shortWeekday Symbols of the current Calendar to get localized names for the days of the week. import Foundation var calendar = Calendar.current calendar.locale = Locale(identifier: "en_US") calendar.shortWeekdaySymbols // ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] calendar.locale = Locale(identifier: "ja_JP") calendar.shortWeekdaySymbols // ["日", "月", "火", "水", "木", "金", "土"] DateComponentsFormatter As the name implies, DateComponentsFormatter works with DateComponents values (previously), which contain a combination of discrete calendar quantities, such as “1 day and 2 hours”. DateComponentsFormatter provides localized representations of date components in several different, pre-set formats: let formatter = DateComponentsFormatter() formatter.unitsStyle = .full let components = DateComponents(day: 1, hour: 2) let string = formatter.string(from: components) // 1 day, 2 hours NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init]; formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull; NSDateComponents *components = [[NSDateComponents alloc] init]; components.day = 1; components.hour = 2; NSLog(@"%@", [formatter stringFromDateComponents:components]); // 1 day, 2 hours Date Components Unit Styles Style Example positional “1:10” abbreviated “1h 10m” short “1hr 10min” full “1 hour, 10 minutes” spellOut “One hour, ten minutes” Formatting Context Some years ago, formatters introduced the concept of formatting context, to handle situations where the capitalization and punctuation of a localized string may depend on whether it appears at the beginning or middle of a sentence. A context property is available for DateComponentsFormatter, as well as DateFormatter, NumberFormatter, and others. * Formatting Context Output standalone “About 2 hours” listItem “About 2 hours” beginningOfSentence “About 2 hours” middleOfSentence “about 2 hours” dynamic Depends* A Dynamic context changes capitalization automatically depending on where it appears in the text for locales that may position strings differently depending on the content. RelativeDateTimeFormatter RelativeDateTimeFormatter is a newcomer in iOS 13 — and at the time of writing, still undocumented, so consider this an NSHipster exclusive scoop! Longtime readers may recall that DateFormatter actually gave this a try circa iOS 4 by way of the doesRelativeDateFormatting property. But that hardly ever worked, and most of us forgot about it, probably. Fortunately, RelativeDateTimeFormatter succeeds where doesRelativeDateFormatting fell short, and offers some great new functionality to make your app more personable and accessible. (As far as we can tell,) RelativeDatetimeFormatter takes the most significant date component and displays it in terms of past or future tense (“1 day ago” / “in 1 day”). let formatter = RelativeDateTimeFormatter() formatter.localizedString(from: formatter.localizedString(from: formatter.localizedString(from: formatter.localizedString(from: DateComponents(day: 1, hour: 1)) // "in 1 day" DateComponents(day: -1)) // "1 day ago" DateComponents(hour: 3)) // "in 3 hours" DateComponents(minute: 60)) // "in 60 minutes" For the most part, this seems to work really well. However, its handling of nil, zero, and net-zero values leaves something to be desired… formatter.localizedString(from: DateComponents(hour: 0)) // "in 0 hours" formatter.localizedString(from: DateComponents(day: 1, hour: -24)) // "in 1 day" formatter.localizedString(from: DateComponents()) // "" Styles Style Example abbreviated “1 mo. ago” * short “1 mo. ago” full “1 month ago” spellOut “one month ago” *May produce output distinct from short for non-English locales. Using Named Relative Date Times By default, RelativeDateTimeFormatter adopts the formulaic convention we’ve seen so far. But you can set the dateTimeStyle property to .named to prefer localized deictic expressions — “tomorrow”, “yesterday”, “next week” — whenever one exists. import Foundation let formatter = RelativeDateTimeFormatter() formatter.localizedString(from: DateComponents(day: -1)) // "1 day ago" formatter.dateTimeStyle = .named formatter.localizedString(from: DateComponents(day: -1)) // "yesterday" This just goes to show that beyond calendrical and temporal relativity, RelativeDateTimeFormatter is a real whiz at linguistic relativity, too! For example, English doesn’t have a word to describe the day before yesterday, whereas other languages, like German, do. formatter.localizedString(from: DateComponents(day: -2)) // "2 days ago" formatter.locale = Locale(identifier: "de_DE") formatter.localizedString(from: DateComponents(day: -2)) // "vorgestern" Hervorragend! Formatting People and Places Class Example Output Availability PersonNameComponentsFormatter “J. Appleseed” iOS 9.0+ macOS 10.11+ CNContactFormatter “Appleseed, Johnny” iOS 9.0+ macOS 10.11+ CNPostalAddressFormatter “1 Infinite Loop\n Cupertino CA 95014” iOS 9.0+ macOS 10.11+ CNContactFormatter is superseded by PersonNameComponentsFormatter. Unless you’re working with existing CNContact objects, prefer the use of PersonNameComponents Formatter to format personal names. PersonNameComponentsFormatter PersonNameComponentsFormatter is a sort of high water mark for Foundation. It encapsulates one of the hardest, most personal problems in computer in such a way to make it accessible to anyone without requiring a degree in Ethnography. The documentation does a wonderful job illustrating the complexities of personal names (if I might say so myself), but if you had any doubt of the utility of such an API, consider the following example: let formatter = PersonNameComponentsFormatter() var nameComponents = PersonNameComponents() nameComponents.givenName = "Johnny" nameComponents.familyName = "Appleseed" formatter.string(from: nameComponents) // "Johnny Appleseed" Simple enough, right? We all know names are space delimited, first-last… right? nameComponents.givenName = "约翰尼" nameComponents.familyName = "苹果籽" formatter.string(from: nameComponents) // "苹果籽约翰尼" ‘nuf said. CNPostalAddressFormatter CNPostalAddressFormatter provides a convenient Formatter-based API to functionality dating back to the original AddressBook framework. The following example formats a constructed CNMutablePostalAddress, but you’ll most likely use existing CNPostalAddress values retrieved from the user’s address book. let address = CNMutablePostalAddress() address.street = "One Apple Park Way" address.city = "Cupertino" address.state = "CA" address.postalCode = "95014" let addressFormatter = CNPostalAddressFormatter() addressFormatter.string(from: address) /* "One Apple Park Way Cupertino CA 95014" */ Styling Formatted Attributed Strings When formatting compound values, it can be hard to figure out where each component went in the final, resulting string. This can be a problem when you want to, for example, call out certain parts in the UI. Rather than hacking together an ad-hoc, regex-based solution, CNPostalAddressFormatter provides a method that vends an NSAttributedString that lets you identify the ranges of each component (PersonNameComponentsFormatter does this too). The NSAttributedString API is… to put it politely, bad. It feels bad to use. So for the sake of anyone hoping to take advantage of this functionality, please copy-paste and appropriate the following code sample to your heart’s content: var attributedString = addressFormatter.attributedString( from: address, withDefaultAttributes: [:] ).mutableCopy() as! NSMutableAttributedString let stringRange = NSRange(location: 0, length: attributedString.length) attributedString.enumerateAttributes(in: stringRange, options: []) { (attributes, attributesRange, _) in let color: UIColor switch attributes[NSAttributedString.Key(CNPostalAddressPropertyAttribute)] as? String { case CNPostalAddressStreetKey: color = .red case CNPostalAddressCityKey: color = .orange case CNPostalAddressStateKey: color = .green case CNPostalAddressPostalCodeKey: color = .purple default: return } } attributedString.addAttribute(.foregroundColor, value: color, range: attributesRange) One Apple Park Way Cupertino CA 95014 Formatting Lists and Items Class Example Output Availability ListFormatter “macOS, iOS, iPadOS, watchOS, and tvOS” iOS 13.0+ macOS 10.15+ ListFormatter Rounding out our survey of formatters in the Apple SDK, it’s another new addition in iOS 13: List Formatter. To be completely honest, we didn’t know where to put this in the article, so we just kind of stuck it on the end here. (Though in hindsight, this is perhaps appropriate given the subject matter). Once again, we don’t have any official documentation to work from at the moment, but the comments in the header file give us enough to go on. NSListFormatter provides locale-correct formatting of a list of items using the appropriate separator and conjunction. Note that the list formatter is unaware of the context where the joined string will be used, e.g., in the beginning of the sentence or used as a standalone string in the UI, so it will not provide any sort of capitalization customization on the given items, but merely join them as-is. The string joined this way may not be grammatically correct when placed in a sentence, and it should only be used in a standalone manner. tl;dr: This is joined(by:) with locale-aware serial and penultimate delimiters. For simple lists of strings, you don’t even need to bother with instantiating ListFormatter — just call the localizedString(byJoining:) class method. import Foundation let operatingSystems = ["macOS", "iOS", "iPadOS", "watchOS", "tvOS"] ListFormatter.localizedString(byJoining: operatingSystems) // "macOS, iOS, iPadOS, watchOS, and tvOS" ListFormatter works as you’d expect for lists comprising zero, one, or two items. ListFormatter.localizedString(byJoining: []) // "" ListFormatter.localizedString(byJoining: ["Apple"]) // "Apple" ListFormatter.localizedString(byJoining: ["Jobs", "Woz"]) // "Jobs and Woz" Lists of Formatted Values ListFormatter exposes an underlying itemFormatter property, which effectively adds a map(_:) before calling joined(by:). You use itemFormatter whenever you’d formatting a list of non-String elements. For example, you can set a NumberFormatter as the itemFormatter for a ListFormatter to turn an array of cardinals (Int values) into a localized list of ordinals. let numberFormatter = NumberFormatter() numberFormatter.numberStyle = .ordinal let listFormatter = ListFormatter() listFormatter.itemFormatter = numberFormatter listFormatter.string(from: [1, 2, 3]) // "1st, 2nd, and 3rd" If you set a custom locale on your list formatter, be sure to set that locale for the underlying formatter. And be mindful of value semantics, too — without the re-assignment to itemFormatter in the example below, you’d get a French list of English ordinals instead. let = Locale(identifier: "fr_FR") listFormatter.locale = numberFormatter.locale = listFormatter.itemFormatter = numberFormatter listFormatter.string(from: [1, 2, 3]) // "1er, 2e et 3e" As some of the oldest members of the Foundation framework, NSNumberFormatter and NSDate Formatter are astonishingly well-suited to their respective domains, in that way only decade-old software can. This tradition of excellence is carried by the most recent incarnations as well. If your app deals in numbers or dates (or time intervals or names or lists measurements of any kind), then NSFormatter is indispensable. And if your app doesn’t… then the question is, what does it do, exactly? Invest in learning all of the secrets of Foundation formatters to get everything exactly how you want them. And if you find yourself with formatting logic scattered across your app, consider creating your own Formatter subclass to consolidate all of that business logic in one place. FileManager One of the most rewarding experiences you can have as a developer is to teach young people how to program. If you ever grow jaded by how fundamentally broken all software is, there’s nothing like watching a concept like recursion click for the first time to offset your world-weariness. My favorite way to introduce the concept of programming is to set out all the ingredients for a peanut butter and jelly sandwich and ask the class give me instructions for assembly as if I were a robot . The punchline is that the computer takes every instruction as literally as possible, often with unexpected results. Ask the robot to “put peanut butter on the bread”, and you may end up with an unopened jar of Jif flattening a sleeve of Wonder Bread. Forget to specify which part of the bread to put the jelly on? Don’t be surprised if it ends up on the outer crust. And so on. Kids love it. The lesson of breaking down a complex process into discrete steps is a great encapsulation of programming. And the malicious compliance from lack of specificity echoes the analogy of “programming as wish making” from our article about numericCast(_:). But let’s take the metaphor a step further, and imagine that instead of commanding a single robot to (sudo) make a sandwich, you’re writing instructions for a thousand different robots. Big and small, fast and slow; some have 4 arms instead of 2, others hover in the air, maybe a few read everything in reverse. Consider what would happen if multiple robots tried to make a sandwich at the same time. Or imagine that your instructions might be read by robots that won’t be built for another 40 years, by which time peanut butter is packaged in capsules and jelly comes exclusively as a gas. That’s kind of what it’s like to interact with a file system. The only chance we have at making something that works is to leverage the power of abstraction. On Apple platforms, this functionality is provided by the Foundation framework by way of FileManager. We can’t possibly cover everything there is to know about working with file systems in a single article, so this week, let’s take a look at the operations you’re most likely to perform when building an app. FileManager offers a convenient way to create, read, move, copy, and delete both files and directories, whether they’re on local or networked drives or iCloud ubiquitous containers. The common currency for all of these operations are paths and file URLs. Paths and File URLs Objects on the file system can be identified in a few different ways. For example, each of the following represents the location of the same text document: • Path: /Users/NSHipster/Documents/article.md • File URL: file:///Users/NSHipster/Documents/article.md • File Reference URL: file:///.file/id=1234567.7654321/ Paths are slash-delimited (/) strings that designate a location in the directory hierarchy. File URLs are URLs with a file:// scheme in addition to a file path. File Reference URLs identify the location of a file using a unique identifier separate from any directory structure. Of those, you’ll mostly deal with the first two, which identify files and directories using a relational path. That path may be absolute and provide the full location of a resource from the root directory, or it may be relative and show how to get to a resource from a given starting point. Absolute URLs begin with /, whereas relative URLs begin with ./ (the current directory), ../ (the parent directory), or ~ (the current user’s home directory). FileManager has methods that accept both paths and URLs — often with variations of the same method for both. In general, the use of URLs is preferred to paths, as they’re more flexible to work with. (it’s also easier to convert from a URL to a path than vice versa). Locating Files and Directories The first step to working with a file or directory is locating it on the file system. Standard locations vary across different platforms, so rather than manually constructing paths like /System or ~/Documents, you use the FileManager method url(for:in:appropriateFor:create:) to locate the appropriate location for what you want. The first parameter takes one of the values specified by FileManager.SearchPathDirectory. These determine what kind of standard directory you’re looking for, like “Documents” or “Caches”. The second parameter passes a FileManager.SearchPathDomainMask value, which determines the scope of where you’re looking for. For example, .applicationDirectory might refer to /Applications in the local domain and ~/Applications in the user domain. let documentsDirectoryURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *filePath = [documentsPath stringByAppendingPathComponent:@"file.txt"]; Files and directories can have alternate names from what’s encoded by the path. For example, most macOS system directories are localized; although a user’s photos located at ~/Photos, the ~/Photos/.localized file can change how the folder is named in Finder and Open / Save panels. An app can also provide locale-specific names for itself and directories it creates. Another example of this is when files are configured to hide their file extension. (You’ve probably encountered this at some point, with some degree of bewilderment). Long story short, when displaying the name of a file or directory to the user, don’t simply take the last path component. Instead, call the method displayName(atPath:): let directoryURL: URL = /path/to/directory // Bad let filename = directoryURL.pathComponents.last // Good let filename = FileManager.default.displayName(atPath: url.path) Determining Whether a File Exists You might check to see if a file exists at a location before trying to read it, or want to avoid overwriting an existing one. To do this, call the fileExists(atPath:) method: let fileURL: URL = /path/to/file let fileExists = FileManager.default.fileExists(atPath: fileURL.path) NSURL *fileURL = <#/path/to/file#>; NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL fileExists = [fileManager fileExistsAtPath:[fileURL path]]; Getting Information About a File The file system stores various pieces of metadata about each file and directory in the system. You can access them using the FileManager method attributesOfItem(atPath:). The resulting dictionary contains attributes keyed by FileAttributeKey values, including .creationDate: let fileURL: URL = /path/to/file let attributes = FileManager.default.attributesOfItem(atPath: fileURL.path) let creationDate = attributes[.creationDate] NSURL *fileURL = <#/path/to/file#>; NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; NSDictionary *attributes = [fileManager attributesOfItemAtPath:[fileURL path] error:&error]; NSDate *creationDate = attributes[NSFileCreationDate]; File attributes used to be keyed by string constants, which made them hard to discover through autocompletion or documentation. Fortunately, it’s now easy to see everything that’s available. Listing Files in a Directory To list the contents of a directory, call the FileManager method contentsOfDirectory(at:including PropertiesForKeys:options:). If you intend to access any metadata properties, as described in the previous section (for example, get the modification date of each file in a directory), specify those here to ensure that those attributes are cached. The options parameter of this method allows you to skip hidden files and/or descendants. let directoryURL: URL = /path/to/directory let contents = try FileManager.default.contentsOfDirectory(at: directoryURL, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) for file in contents { … } NSFileManager *fileManager = [NSFileManager defaultManager]; NSURL *bundleURL = [[NSBundle mainBundle] bundleURL]; NSArray *contents = [fileManager contentsOfDirectoryAtURL:bundleURL includingPropertiesForKeys:@[] options:NSDirectoryEnumerationSkipsHiddenFiles error:nil]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"pathExtension == 'png'"]; for (NSURL *fileURL in [contents filteredArrayUsingPredicate:predicate]) { // Enumerate each .png file in directory } Recursively Enumerating Files In A Directory If you want to go through each subdirectory at a particular location recursively, you can do so by creating a FileManager.DirectoryEnumerator object with the enumerator(atPath:) method: let directoryURL: URL = /path/to/directory if let enumerator = FileManager.default.enumerator(atPath: directoryURL.path) { for case let path as String in enumerator { // Skip entries with '_' prefix, for example if path.hasPrefix("_") { enumerator.skipDescendants() } } } NSFileManager *fileManager = [NSFileManager defaultManager]; NSURL *bundleURL = [[NSBundle mainBundle] bundleURL]; NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:bundleURL includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey] options:NSDirectoryEnumerationSkipsHiddenFiles *url, NSError *error) { if (error) { NSLog(@"[Error] %@ (%@)", error, url); return NO; } }]; errorHandler:^BOOL(NSURL return YES; NSMutableArray *mutableFileURLs = [NSMutableArray array]; for (NSURL *fileURL in enumerator) { NSString *filename; [fileURL getResourceValue:&filename forKey:NSURLNameKey error:nil]; NSNumber *isDirectory; [fileURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil]; // Skip directories with '_' prefix, for example if ([filename hasPrefix:@"_"] && [isDirectory boolValue]) { } } [enumerator skipDescendants]; continue; if (![isDirectory boolValue]) { [mutableFileURLs addObject:fileURL]; } Creating a Directory To create a directory, call the method createDirectory(at:withIntermediateDirectories :attributes:). In Unix parlance, setting the withIntermediateDirectories parameter to true is equivalent to passing the -p option to mkdir. try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *imagesPath = [documentsPath stringByAppendingPathComponent:@"images"]; if (![fileManager fileExistsAtPath:imagesPath]) { NSError *error = nil; [fileManager createDirectoryAtPath:imagesPath withIntermediateDirectories:NO attributes:nil error:&error]; } Deleting a File or Directory If you want to delete a file or directory, call removeItem(at:): let fileURL: URL = /path/to/file try FileManager.default.removeItem(at: fileURL) NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *filePath = [documentsPath stringByAppendingPathComponent:@"image.png"]; NSError *error = nil; if (![fileManager removeItemAtPath:filePath error:&error]) { NSLog(@"[Error] %@ (%@)", error, filePath); } FileManagerDelegate FileManager may optionally set a delegate to verify that it should perform a particular file operation. This is a convenient way to audit all file operations in your app, and a good place to factor out and centralize business logic, such as which files to protect from deletion. There are four operations covered by the FileManagerDelegate protocol: moving, copying, removing, and linking items — each with variations for working with paths and URLs, as well as how to proceed after an error occurs: If you were wondering when you might create your own FileManager rather than using this shared instance, this is it. From the documentation: You should associate your delegate with a unique instance of the FileManager class, as opposed to the shared instance. class CustomFileManagerDelegate: NSObject, FileManagerDelegate { func fileManager(_ fileManager: FileManager, shouldRemoveItemAt URL: URL) -> Bool { // Don't delete PDF files return URL.pathExtension != "pdf" } } // Maintain strong references to fileManager and delegate let fileManager = FileManager() let delegate = CustomFileManagerDelegate() fileManager.delegate = delegate NSFileManager *fileManager = [[NSFileManager alloc] init]; fileManager.delegate = delegate; NSURL *bundleURL = [[NSBundle mainBundle] bundleURL]; NSArray *contents = [fileManager contentsOfDirectoryAtURL:bundleURL includingPropertiesForKeys:@[] options:NSDirectoryEnumerationSkipsHiddenFiles error:nil]; for (NSString *filePath in contents) { [fileManager removeItemAtPath:filePath error:nil]; } // CustomFileManagerDelegate.m #pragma mark - NSFileManagerDelegate - (BOOL)fileManager:(NSFileManager *)fileManager shouldRemoveItemAtURL:(NSURL *)URL { return ![[[URL lastPathComponent] pathExtension] isEqualToString:@"pdf"]; } When you write an app that interacts with a file system, you don’t know if it’s an HDD or SSD or if it’s formatted with APFS or HFS+ or something else entirely. You don’t even know where the disk is: it could be internal or in a mounted peripheral, it could be network-attached, or maybe floating around somewhere in the cloud. The best strategy for ensuring that things work across each of the various permutations is to work through FileManager and its related Foundation APIs. Temporary Files Volumes have been written about persisting data, but when it comes to short-lived, temporary files, there is very little to go on for Cocoa. (Or if there has, perhaps it was poetically ephemeral itself). Temporary files are used to write data to disk before either moving it to a permanent location or discarding it. For example, when a movie editor app exports a project, it may write each frame to a temporary file until it reaches the end and moves the completed file to the ~/Movies directory. Using a temporary file for these kinds of situations ensures that tasks are completed atomically (either you get a finished product or nothing at all; nothing half-way), and without creating excessive memory pressure on the system (on most computers, disk space is plentiful whereas memory is limited). There are four distinct steps to working with a temporary file: 1. Creating a temporary directory in the filesystem 2. Creating a temporary file in that directory with a unique filename 3. Writing data to the temporary file 4. Moving or deleting the temporary file once you’re finished with it Creating a Temporary Directory The first step to creating a temporary file is to find a reasonable, out-of-the-way location to which you can write — somewhere inconspicuous that doesn’t get in the way of the user or get picked up by a system process like Spotlight indexing, Time Machine backups, or iCloud sync. On Unix systems, the /tmp directory is the de facto scratch space. However, today’s macOS and iOS apps run in a container and don’t have access to system directories; a hard-coded path like that isn’t going to cut it. If you don’t intend to keep the temporary file around, you can use the NSTemporaryDirectory() function to get a path to a temporary directory for the current user. let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) NSURL *temporaryDirectoryURL = [NSURL fileURLWithPath: NSTemporaryDirectory() isDirectory: YES]; Alternatively, if you intend to move your temporary file to a destination URL, the preferred (albeit more complicated) approach is to call the FileManager method uri(for:in:appropriateFor:create:). let destinationURL: URL = /path/to/destination let temporaryDirectoryURL = try FileManager.default.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: destinationURL, create: true) NSURL *destinationURL = <#/path/to/destination#>; NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; NSURL *temporaryDirectoryURL = [fileManager URLForDirectory:NSItemReplacementDirectory inDomain:NSUserDomainMask appropriateForURL:destinationURL create:YES error:&error]; The parameters of this method are frequently misunderstood, so let’s go through each to understand what this method actually does: • We pass the item replacement search path (.itemReplacementDirectory) to say that we’re interested in a temporary directory. • We pass the user domain mask (.userDomainMask) to get a directory that’s accessible to the user. • For the appropriateForURL parameter, we specify our destinationURL, so that the system returns a temporary directory from which a file can be quickly moved to the destination (and not, say across different volumes). • Finally, we pass true to the create parameter to save us the additional step of creating it ourselves. The resulting directory will have a path that looks something like this: file:///var/folders/l3/kyksr35977d8nfl1mhw6l_c00000gn/T/ TemporaryItems/(A%20Document%20Being%20Saved%20By%20NSHipster%208)/ Creating a Temporary File With a place to call home (at least temporarily), the next step is to figure out what to call our temporary file. We’re not picky about what to it’s named — just so long as it’s unique, and doesn’t interfere with any other temporary files in the directory. The best way to generate a unique identifier is the ProcessInfo property globallyUniqueString: ProcessInfo().globallyUniqueString [[NSProcessInfo processInfo] globallyUniqueString]; The resulting filename will look B72B049E9254-25121-000144AB9F08C9C1 something like this: 42BC63F7-E79E-4E41-8E0D- Alternatively, UUID also produces workably unique identifiers: UUID().uuidString [[NSUUID UUID] UUIDString] A generated UUID string has the following format: B49C292E-573D-4F5B-A362-3F2291A786E7 Now that we have an appropriate directory and a unique filename, let’s put them together to create our temporary file: let destinationURL: URL = /path/to/destination let temporaryDirectoryURL = try FileManager.default.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: destinationURL, create: true) let temporaryFilename = ProcessInfo().globallyUniqueString let temporaryFileURL = temporaryDirectoryURL.appendingPathComponent(temporaryFilename) NSURL *destinationURL = <#/path/to/destination#>; NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; NSURL *temporaryDirectoryURL = [fileManager URLForDirectory:NSItemReplacementDirectory inDomain:NSUserDomainMask appropriateForURL:destinationURL create:YES error:&error]; NSString *temporaryFilename = [[NSProcessInfo processInfo] globallyUniqueString]; NSURL *temporaryFileURL = [temporaryDirectoryURL URLByAppendingPathComponent:temporaryFilename]; Writing to a Temporary File The sole act of creating a file URL is of no consequence to the file system; a file is created only when the file path is written to. So let’s talk about our options for doing that: Writing Data to a URL The simplest way to write data to a file is to call the Data method write(to:options): let data: Data = some data try data.write(to: temporaryFileURL, options: .atomic) NSData *data = <#some data#>; NSError *error = nil; [data writeToURL:temporaryFileURL options:NSDataWritingAtomic error:&error]; By passing the atomic option, we ensure that either all of the data is written or the method returns an error. Writing Data to a File Handle If you’re doing anything more complicated than writing a single Data object to a file, consider creating a FileHandle instead, like so: let fileHandle = try FileHandle(forWritingTo: temporaryFileURL) defer { fileHandle.closeFile() } fileHandle.write(data) NSError *error = nil; NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingToURL:temporaryFileURL error:&error]; [fileHandle writeData:data]; [fileHandle closeFile]; Writing Data to an Output Stream For more advanced APIs, it’s not uncommon to use OutputStream to direct the flow of data. Creating an output stream to a temporary file is no different than any other kind of file: let outputStream = OutputStream(url: temporaryFileURL, append: true)! defer { outputStream.close() } data.withUnsafeBytes { bytes in outputStream.write(bytes, maxLength: bytes.count) } NSOutputStream *outputStream = [NSOutputStream outputStreamWithURL:temporaryFileURL append:YES]; [outputStream write:data.bytes maxLength:data.length]; [outputStream close]; In Swift, calling fileHandle.closeFile() or outputStream.close() within a defer statement is a convenient way to fulfill the API contract of closing a file when we’re done with it. (Of course, don’t do this if you want to keep the file handle open longer than the enclosing scope). Moving or Deleting the Temporary File Files in system-designated temporary directories are periodically deleted by the operating system. So if you intend to hold onto the file that you’ve been writing to, you need to move it somewhere outside the line of fire. If you already know where the file’s going to live, you can use FileManager to move it to its permanent home: let fileURL: URL = /path/to/file try FileManager.default.moveItem(at: temporaryFileURL, to: fileURL) NSFileManager *fileManager = [NSFileManager defaultManager]; NSURL *fileURL = <#/path/to/file#>; NSError *error = nil; [fileManager moveItemAtURL:temporaryFileURL toURL:fileURL error:&error]; Or, if you’re not entirely settled on that, you can use the same approach to locate a cache directory where the file can lie low for a while: let cacheDirectoryURL = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false) NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; NSURL *cacheDirectoryURL = [fileManager URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:&error]; Although the system eventually takes care of files in temporary directories, it’s not a bad idea to be a responsible citizen and follow the guidance of “take only pictures; leave only footprints.” FileManager can help us out here as well, with the removeItem(at:) method: try FileManager.default.removeItem(at: temporaryFileURL) NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error = nil; [fileManager removeItemAtURL:temporaryFileURL error:&error]; “This too shall pass” is a mantra that acknowledges that all things are indeed temporary. Within the context of the application lifecycle, some things are more temporary than others, and it’s with that knowledge that we choose to act appropriately: seeking to find the right place, make a unique impact, and leave without a trace. Perhaps we can learn something from this cycle in our own, brief and glorious lifecycle. CoreGraphics Geometry Primitives Unless you were a Math Geek or an Ancient Greek, Geometry probably wasn’t your favorite subject in school. More likely, you were that kid in class who dutifully programmed all of those necessary formulæ into your TI-8X calculator to avoid rote memorization. So for those of you who spent more time learning TI-BASIC than Euclid, here’s the cheat-sheet for how geometry works in Quartz 2D, the drawing system used by Apple platforms: 1.0 (x: 1.0, y: 2.0) CGFloat CGPoint (dx: 4.0, dy: 3.0) CGVector (width: 4.0, height: 3.0) (origin: (x: 1.0, y: 2.0), size: (width: 4.0, height: 3.0)) CGSize CGRect • A CGFloat represents a scalar quantity. • A CGPoint represents a location in a two-dimensional coordinate system and is defined by x and y scalar components. • A CGVector represents a change in position in 2D space and is defined by dx and dy scalar components. • A CGSize represents the extent of a figure in 2D space and is defined by width and height scalar components. • A CGRect represents a rectangle and is defined by an origin point (CGPoint) and a size (CGSize). import CoreGraphics let let let let var float: CGFloat = 1.0 point = CGPoint(x: 1.0, y: 2.0) vector = CGVector(dx: 4.0, dy: 3.0) size = CGSize(width: 4.0, height: 3.0) rectangle = CGRect(origin: point, size: size) CGVector isn’t widely used in view programming; instead, CGSize values are typically used to express positional vectors. Unfortunately, this can result in awkward semantics, because sizes may have negative width and / or height components (in which case a rectangle is extended in the opposite direction along that dimension). (x, y) macOS (x, y) iOS On iOS, the origin is located at the top-left corner of a window, so x and y values increase as they move down and to the right. macOS, by default, orients (0, 0) at the bottom left corner of a window, such that y values increase as they move up. You can configure views on macOS to use the same coordinate system as iOS by overriding the is Flipped property on subclasses of NSView. Every view in an iOS or macOS app has a frame represented by a CGRect value, so one would do well to learn the fundamentals of these geometric primitives. In this week’s article, we’ll do a quick run through the APIs with which every app developer should be familiar. Introspection “First, know thyself.” So goes the philosophical aphorism. And it remains practical guidance as we begin our survey of CoreGraphics API. As structures, you can access the member values of geometric types directly through their stored properties: point.x // 1.0 point.y // 2.0 size.width // 4.0 size.height // 3.0 rectangle.origin // {x 1 y 2} rectangle.size // {w 4 h 3} You can mutate variables by reassignment or by using mutating operators like *= and +=: var mutableRectangle = rectangle // {x 1 y 2 w 4 h 3} mutableRectangle.origin.x = 7.0 mutableRectangle.size.width *= 2.0 mutableRectangle.size.height += 3.0 mutableRectangle // {x 7 y 2 w 8 h 6} For convenience, rectangles also expose width and height as top-level, computed properties; (x and y coordinates must be accessed through the intermediary origin): rectangle.origin.x rectangle.origin.y rectangle.width rectangle.height However, you can’t use these convenience accessors to change the underlying rectangle like in the preceding example: mutableRectangle.width *= 2.0 // {x 1 y 2 w 8 h 3} // ️ Left side of mutating operator isn't mutable: 'width' is a get-only property Accessing Minimum, Median, and Maximum Values Although a rectangle can be fully described by a location (CGPoint) and an extent (CGSize), that’s just one side of the story. For the other 3 sides, use the built-in convenience properties to get the minimum (min), median (mid), and maximum (max) values in the x and y dimensions: minX midX maxX minY midY maxY rectangle.minX // 1.0 rectangle.midX // 3.0 rectangle.maxX // 5.0 rectangle.minY // 2.0 rectangle.midY // 3.5 rectangle.maxY // 5.0 Computing the Center of a Rectangle It’s often useful to compute the center point of a rectangle. Although this isn’t provided by the framework SDK, you can easily extend CGRect to implement it using the midX and midY properties: extension CGRect { var center: CGPoint { return CGPoint(x: midX, y: midY) } } Normalization Things can get a bit strange when you use non-integral or negative values in geometric calculations. Fortunately, CoreGraphics has just the APIs you need to keep everything in order. Standardizing Rectangles We expect that a rectangle’s origin is situated at its top-left corner. However, if its size has a negative width or height, the origin could become any of the other corners instead. For example, consider the following bizarro rectangle that extends leftwards and upwards from its origin. let ǝןƃuɐʇɔǝɹ = CGRect(origin: point, size: CGSize(width: -4.0, height: -3.0)) ǝןƃuɐʇɔǝɹ // {x 1 y 2 w -4 h -3} We can use the standardized property to get the equivalent rectangle with non-negative width and height. In the case of the previous example, the standardized rectangle has a width of 4 and height of 3 and is situated at the point (-3, -1): ǝןƃuɐʇɔǝɹ.standardized // {x -3 y -1 w 4 h 3} Integrating Rectangles It’s generally a good idea for all CGRect values to be rounded to the nearest whole point. Fractional values can cause the frame to be drawn on a pixel boundary. Because pixels are atomic units, a fractional value causes drawing to be averaged over the neighboring pixels. The result: blurry lines that don’t look great. The integral property takes the floor each origin value and the ceil each size value. This ensures that your drawing code aligns on pixel boundaries crisply. let blurry = CGRect(x: 0.1, y: 0.5, width: 3.3, height: 2.7) blurry // {x 0.1 y 0.5 w 3.3 h 2.7} blurry.integral // {x 0 y 0 w 4 h 4} Though keep in mind that CoreGraphics coordinates operate in terms of points not pixels. So, for example, a Retina screen with pixel density of 2 represents each point with 4 pixels and can draw ± 0.5f point values on odd pixels without blurriness. Transformations While it’s possible to mutate a rectangle by performing member-wise operations on its origin and size, the CoreGraphics framework offers better solutions by way of the APIs discussed below. Translating Rectangles Translation describes the geometric operation of moving a shape from one location to another. Use the offsetBy method (or CGRectOffset function in Objective-C) to translate a rectangle’s origin by a specified x and y distance. rectangle.offsetBy(dx: 2.0, dy: 2.0) // {x 3 y 4 w 4 h 3} Consider using this method whenever you shift a rectangle’s position. Not only does it save a line of code, but it more semantically represents intended operation than manipulating the origin values individually. Contracting and Expanding Rectangles Other common transformations for rectangles include contraction and expansion around a center point. The insetBy(dx:dy:) method can accomplish both. When passed a positive value for either component, this method returns a rectangle that shrinks by the specified amount from each side as computed from the center point. For example, when inset by 1.0 horizontally (dy = 0.0), a rectangle originating at (1, 2) with a width of 4 and height equal to 3, produces a new rectangle originating at (2, 2) with width equal to 2 and height equal to 3. Which is to say: the result of insetting a rectangle by 1 point horizontally is a rectangle whose width is 2 points smaller than the original. rectangle // {x 1 y 2 w 4 h 3} rectangle.insetBy(dx: 1.0, dy: 0.0) // {x 2 y 2 w 2 h 3} When passed a negative value for either component, the rectangle grows by that amount from each side. When passed a non-integral value, this method may produce a rectangle with non-integral components. rectangle.insetBy(dx: -1.0, dy: 0.0) // {x 0 y 2 w 6 h 3} rectangle.insetBy(dx: 0.5, dy: 0.0) // {x 1.5 y 2 w 3 h 3} For more complex transformations, another option is CGAffineTransform, which allows you to translate, scale, and rotate geometries — all at the same time! (We’ll cover affine transforms in a future article) Identities and Special Values Points, sizes, and rectangles each have a zero property, which defines the identity value for each respective type: CGPoint.zero // {x 0 y 0} CGSize.zero // {w 0 h 0} CGRect.zero // {x 0 y 0 w 0 h 0} Swift shorthand syntax allows you to pass .zero directly as an argument for methods and initializers, such as CGRect.init(origin:size:): let square = CGRect(origin: .zero, size: CGSize(width: 4.0, height: 4.0)) To determine whether a rectangle is empty (has zero size), use the isEmpty property rather than comparing its size to CGSize.zero. CGRect.zero.isEmpty // true CGRect has two additional special values: infinite and null: CGRect.infinite // {x -∞ y -∞ w +∞ h +∞} CGRect.null // {x +∞ y +∞ w 0 h 0} CGRect.null is conceptually similar to NSNotFound, in that it represents the absence of an expected value, and does so using the largest representable number to exclude all other values. CGRect.infinite has even more interesting properties, as it intersects with all points and rectangles, contains all rectangles, and its union with any rectangle is itself. CGRect.infinite.contains( any point ) // true CGRect.infinite.intersects( any other rectangle ) // true CGRect.infinite.union( any other rectangle ) // CGRect.infinite Use isInfinite to determine whether a rectangle is, indeed, infinite. CGRect.infinite.isInfinite // true But to fully appreciate why these values exist and how they’re used, let’s talk about geometric relationships: Relationships Up until this point, we’ve been dealing with geometries in isolation. To round out our discussion, let’s consider what’s possible when evaluating two or more rectangles. Intersection Two rectangles intersect if they overlap. Their intersection is the smallest rectangle that encompasses all points contained by both rectangles. In Swift, you can use the intersects(_:) and intersection(_:) methods to efficiently compute the intersection of two CGRect values: let square = CGRect(origin: .zero, size: CGSize(width: 4.0, height: 4.0)) square // {x 0 y 0 w 4 h 4} rectangle.intersects(square) // true rectangle.intersection(square) // {x 1 y 2 w 3 h 2} If two rectangles don’t intersect, the intersection(_:) method produces CGRect.null: rectangle.intersects(.zero) // false rectangle.intersection(.zero) // CGRect.null Union The union of two rectangles is the smallest rectangle that encompasses all of the points contained by either rectangle. In Swift, the aptly-named union(_:) method does just this for two CGRect values: rectangle.union(square) // {x 0 y 0 w 5 h 5} So what if you didn’t pay attention in Geometry class — this is the real world. And in the real world, you have CGGeometry.h and all of the types and functions it provides. Know it well, and you’ll be on your way to discovering great new user interfaces in your apps. Do a good enough job with that, and you may encounter the best arithmetic problem of all: adding up all the money you’ve made with your awesome new app. Mathematical! Bundles and Packages In this season of giving, let’s stop to consider one of the greatest gifts given to us by modern computer systems: the gift of abstraction. Consider those billions of people around the world who use computers and mobile devices on a daily basis. They do this without having to know anything about the millions of CPU transistors and SSD sectors and LCD pixels that come together to make that happen. All of this is thanks to abstractions like files and directories and apps and documents. This week on NSHipster, we’ll be talking about two important abstractions on Apple platforms: bundles and packages. Despite being distinct concepts, the terms “bundle” and “package” are frequently used interchangeably. Part of this is undoubtedly due to their similar names, but perhaps the main source of confusion is that many bundles just so happen to be packages (and vice versa). So before we go any further, let’s define our terminology: • A bundle is a directory with a known structure that contains executable code and the resources that code uses. • A package is a directory that looks like a file when viewed in Finder. The following diagram illustrates the relationship between bundles and packages, as well as things like apps, frameworks, plugins, and documents that fall into either or both categories: If you’re still fuzzy on these distinctions, here’s an analogy that might help you keep things straight: Think of a package as a box ( ) whose contents are sealed away and are considered to exist as a single entity. Contrast that with bundles, which are more like backpacks ( ) — each with special pockets and compartments for carrying whatever you need, and coming in different configurations depending on whether it’s for taking to school, work, or the gym. If something’s both a bundle and a package, it’s like a piece of luggage ( ): sealed like a box and organized into compartments like a backpack. Bundles Bundles are primarily for improving developer experience by providing structure for organizing code and resources. This structure not only allows for predictable loading of code and resources but allows for system-wide features like localization. Bundles fall into one of the following three categories, each with their own particular structure and requirements: • App Bundles, which contain an executable that can be launched, an Info.plist file describing the executable, app icons, launch images, and other assets and resources used by the executable, including interface files, strings files, and data files. • Framework Bundles, which contain code and resources used by the dynamic shared library. • Loadable Bundles like plug-ins, which contain executable code and resources that extend the functionality of an app. Accessing Bundle Contents In apps, playgrounds, and most other contexts the bundle you’re interested in is accessible through the type property Bundle.main. And most of the time, you’ll use url(forResource:withExtension:) (or one of its variants) to get the location of a particular resource. For example, if your app bundle includes a file named Photo.jpg, you can get a URL to access it like so: Bundle.main.url(forResource: "Photo", withExtension: "jpg") Or if you’re using the Asset Catalog, you can simply drag & drop from the Media Library ( ⇧ ⌘ M ) to your editor to create an image literal. For everything else, Bundle provides several instance methods and properties that give the location of standard bundle items, with variants returning either a URL or a String paths: URL Path Description executableURL executablePath The executable url(forAuxiliaryExecutable:) path(forAuxiliaryExecutable:) The auxiliary executables resourceURL resourcePath The subdirectory containing resources sharedFrameworksURL sharedFrameworksPath The subdirectory containing shared frameworks privateFrameworksURL privateFrameworksPath The subdirectory containing private frameworks builtInPlugInsURL builtInPlugInsPath The subdirectory containing plug-ins sharedSupportURL sharedSupportPath The subdirectory containing shared support files appStoreReceiptURL The App Store receipt Getting App Information All app bundles are required to have an Info.plist file that contains information about the app. Some metadata is accessible directly through instance properties on bundles, including bundleURL and bundleIdentifier. import Foundation let bundle = Bundle.main bundle.bundleURL // "/path/to/Example.app" bundle.bundleIdentifier // "com.nshipster.example" You can get any other information by subscript access to the infoDictionary property. (Or if that information is presented to the user, use the localizedInfoDictionary property instead). bundle.infoDictionary["CFBundleName"] // "Example" bundle.localizedInfoDictionary["CFBundleName"] // "Esempio" (`it_IT` locale) Getting Localized Strings One of the most important features that bundles facilitate is localization. By enforcing a convention for where localized assets are located, the system can abstract the logic for determining which version of a file to load away from the developer. For example, bundles are responsible for loading the localized strings used by your app. You can access them using the localizedString(forKey:value:table:) method. import Foundation let bundle = Bundle.main bundle.localizedString(forKey: "Hello, %@", value: "Hello, ${username}", table: nil) However, it’s almost always a better idea to use NSLocalizedString so that utilities like genstrings can automatically extract keys and comments to .strings files for translation. NSLocalizedString("Hello, %@", comment: "Hello, ${username}") $ find . \( -name "*.swift" ! ! -path "./Carthage/*" ! -path "./Pods/*" \) | tr '\n' '\0' | xargs -0 genstrings -o . \ \ \ \ \ \ # find all Swift files # ignoring dependencies # from Carthage and CocoaPods # change delimiter to NUL # to handle paths with spaces Packages Packages are primarily for improving user experience by encapsulating and consolidating related resources into a single unit. A directory is considered to be a package by the Finder if any of the following criteria are met: • The directory has a special extension like .app, .playground, or .plugin • The directory has an extension that an app has registered as a document type • The directory has an extended attribute designating it as a package * Accessing the Contents of a Package In Finder, you can control-click to show a contextual menu with actions to perform on a selected item. If an item is a package, “Show Package Contents” will appear at the top, under “Open”. Selecting this menu item will open a new Finder window from the package directory. You can, of course, access the contents of a package programmatically, too. The best option depends on the kind of package: • If a package has bundle structure, it’s usually easiest to use Bundle as described in the previous section. • If a package is a document, you can use NSDocument on macOS and UIDocument on iOS. • Otherwise, you can use FileWrapper to navigate directories, files, and symbolic links, and FileHandler to read and write to file descriptors. Determining if a Directory is a Package Although it’s up to the Finder how it wants to represent files and directories, most of that is delegated to the operating system and the services responsible for managing Uniform Type Identifiers (UTIs). To determine whether a file extension is one of the built-in system package types or used by an installed app as a registered document type, access the URL resource isPackageKey: let url: URL = … let directoryIsPackage = (try? url.resourceValues(forKeys: [.isPackageKey]).isPackage) ?? false Or, if you don’t have a URL handy and wanted to check a particular filename extension, you could instead use the Core Services framework to make this determination: import Foundation import CoreServices func filenameExtensionIsPackage(_ filenameExtension: String) -> Bool { guard let uti = UTTypeCreatePreferredIdentifierForTag( kUTTagClassFilenameExtension, filenameExtension as NSString, nil )?.takeRetainedValue() else { return false } } return UTTypeConformsTo(uti, kUTTypePackage) let xcode = URL(fileURLWithPath: "/Applications/Xcode.app") directoryIsPackage(xcode) // true If you want to set the so-called “package bit” for a file the hard way (rather than calling URL.set ResourceValues(_:)), spelunking through CarbonCore/Finder.h indicates that you can do this by setting the kHasBundle (0x2000) flag in the com.apple.FinderInfo extended attribute: $ xattr -wx com.apple.FinderInfo /path/to/package \ 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 \ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 As we’ve seen, it’s not just end-users that benefit from abstractions — whether it’s the safety and expressiveness of a high-level programming language like Swift or the convenience of APIs like Foundation, we as developers leverage abstraction to make great software. For all that we may (rightfully) complain about abstractions that are leaky or inverted, it’s important to take a step back and realize how many useful abstractions we deal with every day, and how much they allow us to do. Miscellaneous Empathy Great software is created to scratch one’s own itch. Being close to a problem provides not only insight for how to solve it, but the motivation to actually follow through. It’s the better angels of our nature that compel us to share these solutions with one other. And in the open source world, we do so freely, with only a karmic expectation of paying the favor forward. We naturally want to help one another, to explain ideas, to be generous and patient. However, on the Internet, human nature seems to drop a few packets. Practicing empathy online becomes a feat of moral athleticism. Lacking many of the faculties to humanize and understand one another (facial expressions, voice tonality, non-verbal cues) we can lose sight of who we’re talking to, and become less human ourselves. Before engaging with someone, take a moment to visualize how that encounter would play out in real life. Would you be proud of how you conducted yourself? Rather than responding defensively to snark or aggression, stop to consider what could have motivated that reaction. Is there something you could be doing better as a programmer or community member? Or are they just having a bad day? (We’ve all had our bad days). And let it never be that someone is marginalized for their ability to communicate in English. Be patient and ask questions. Respond simply and clearly. Everything you need to succeed as a software developer extends from a conscious practice of empathy.