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

UISplitViewController

$
0
0

In the beginning, there was the iPhone. And it was good.

Some years later, the iPad was introduced. And with some adaptations, an iOS app could be made Universal to accommodate both the iPhone and iPad in a single bundle.

For a while, the split between the two was the split itself — namely UISplitViewController. Given a classic master-detail view controller paradigm, an iPhone would display each on separate screens, whereas an iPad would display both side-by-side.

But over time, the iPhone grew in size and the distinction between phone and tablet began to blur. Starting with the iPhone 6+, apps running in landscape mode on the phone had enough screen real estate to act like they were on a tablet.

Although user interface idioms have made way for the broader concept of size classes, UISplitViewController remains a workhorse API for writing Universal apps. This week, let’s take a closer look at how we can use it to adapt our UI to a variety of screen sizes.


Let’s start with an example of UISplitViewController working its magic on a large iPhone:

However, the view doesn’t split when the iPhone is in Zoomed Display mode.

This is one instance of how split views automatically determine when to show split views.

Split View Controller, from Start to Finish

The best way to understand how to use UISplitViewController works is to show a complete example. The source code for the example project in this post can be found here.

The Storyboard Layout

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

UISplitViewController Storyboard Layout

In order to master this concept, let’s dive into more detail.

Master / Detail

The first step to using a UISplitViewController is dragging it onto the storyboard. The next step is to specify which view controller is the master and which one is the detail.

UISplitViewController Master-Detail Storyboard

You can do this by selecting the appropriate Relationship Segue:

UISplitViewController Relationship Segue

The master view controller is typically the navigation controller that contains the list view (a UITableView in most cases); the detail view controller is the navigation controller that contains the view that shows up when the user taps on the list item.

Show Detail

There’s one last part to making the split view controller work: specifying the “Show Detail” segue.

UISplitViewController Show Detail Segue

In the example below, when the user taps on a cell in the ColorsViewController, they’re shown a navigation controller with the ColorViewController at its root.

Double Navigation Controllers‽

At this point, you might be wondering: Why do the master and detail view controllers have to be navigation controllers — especially when there’s already a “Show Detail” segue?.

Well, let’s see what happens when the detail view controller doesn’t have a navigation controller at its root:

UISplitViewController No Detail Navigation Controller

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

UISplitViewController No Navigation Bar)

It’s not a big deal unless want your navigation bar to show a title. But this is a deal-breaker on an iPad:

Notice that when the iPad app first launches, there’s no indication that there’s a split view controller at all! To trigger the master view controller, the user has to magically know to swipe left-to-right.

Adding a Display Mode Button

To resolve this issue, we’re looking for some way to indicate that there’s more to the app than what’s currently on-screen. Luckily, UISplitViewController has a displayModeButtonItem navigation item, which can be added to the navigation bar to give us the visual indicator we seek:

overridefuncviewDidLoad(){super.viewDidLoad()// ...navigationItem.leftBarButtonItem=splitViewController?.displayModeButtonItemnavigationItem.leftItemsSupplementBackButton=true}
-(void)viewDidLoad{[superviewDidLoad];// ...self.navigationItem.leftBarButtonItem=self.splitViewController.displayModeButtonItem;self.navigationItem.leftItemsSupplementBackButton=YES;}

Build and Run on the iPad again, and now you get a nice indication of how access the rest of the app:

The displayModeButtonItem property lends some nice usability to apps running on large iPhones in landscape mode, too:

By using displayModeButtonItem, you let iOS figure out what’s appropriate for the current screen size and orientation. Instead of sweating the small (and big) stuff yourself, you can sit back and relax. 🍹

Collapse Detail View Controller

There’s one more optimization we can do for the iPhone. When the user first launches the app, let’s make the master view controller display fully until the user selects a list item. We can do that using UISplitViewControllerDelegate:

classColorsViewController:UITableViewController{varcollapseDetailViewController:Bool=true// ...// MARK: - UITableViewDelegateoverridefunctableView(_tableView:UITableView,didSelectRowAtindexPath:IndexPath){self.collapseDetailViewController=false}}classSplitViewDelegate:NSObject,UISplitViewControllerDelegate{// ...funcsplitViewController(_splitViewController:UISplitViewController,collapseSecondarysecondaryViewController:UIViewController,ontoprimaryViewController:UIViewController)->Bool{guardletnavigationController=primaryViewControlleras?UINavigationController,letcontroller=navigationController.topViewControlleras?ColorsViewControllerelse{returntrue}returncontroller.collapseDetailViewController}}
// SelectColorTableViewController.h@interfaceSelectColorTableViewController:UITableViewController<UISplitViewControllerDelegate>@end// SelectColorTableViewController.m@interfaceSelectColorTableViewController()@property(nonatomic)BOOLshouldCollapseDetailViewController;@end@implementationSelectColorTableViewController-(void)viewDidLoad{[superviewDidLoad];self.shouldCollapseDetailViewController=YES;self.splitViewController.delegate=self;}#pragma mark - UITableViewDelegate
        -(void)tableView:(UITableView*)tableViewdidSelectRowAtIndexPath:(NSIndexPath*)indexPath{self.shouldCollapseDetailViewController=NO;}#pragma mark - UISplitViewControllerDelegate
        -(BOOL)splitViewController:(UISplitViewController*)splitViewControllercollapseSecondaryViewController:(UIViewController*)secondaryViewControllerontoPrimaryViewController:(UIViewController*)primaryViewController{returnself.shouldCollapseDetailViewController;}@end

Now when the app launches on an iPhone in portrait orientation, ColorsViewController is in full view. Once the user selects a color (or the app goes into the background), ColorsViewController is collapsed again, and ColorViewController is displayed:


iOS is always adapting to new capabilities from new hardware. When retina screens were introduced, developers could no longer assume that 1pt = 1px. When larger iPhones were introduced, developers could no longer assume a single screen size.

Today, we’re responsible for accommodating several generations or iPhones and iPads, as well as external displays and various accessibility features. This would be a nightmare if it weren’t for the powerful and thoughtful APIs provided in iOS.

UISplitViewController may not be the newest API on the block when it comes to adapting to various interface conditions, but it remains a useful tool for quickly creating robust apps.


Swift Operators

$
0
0

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 operandoperatorright operand

Expressions are expressed in a single, flat line of code, from which the compiler constructs an AST, or abstract syntax tree:

1 + 2 AST

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 operandoperatorright operand

1 + 2 * 3 AST

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 operandoperatorright operand

5 - 2 + 3 AST

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
/Divide
%Remainder
&*Multiply, ignoring overflow
&/Divide, ignoring overflow
&%Remainder, ignoring overflow
&Bitwise AND
AdditionPrecedence
+Add
-Subtract
&+Add with overflow
&-Subtract with overflow
|Bitwise OR
^Bitwise XOR
RangeFormationPrecedence
..<Half-open range
...Closed range
CastingPrecedence
isType check
asType cast
NilCoalescingPrecedence
??nil Coalescing
ComparisonPrecedence
<Less than
<=Less than or equal
>Greater than
>=Greater than or equal
==Equal
!=Not equal
===Identical
!==Not identical
~=Pattern match
LogicalConjunctionPrecedence
&&Logical AND
LogicalDisjunctionPrecedence
||Logical OR
DefaultPrecedence
AssignmentPrecedence
=Assign
*=Multiply and assign
/=Divide and assign
%=Remainder and assign
+=Add and assign
-=Subtract and assign
<<=Left bit shift and assign
>>=Right bit shift and assign
&=Bitwise AND and assign
^=Bitwise XOR and assign
|=Bitwise OR and assign
&&=Logical AND and assign
||=Logical OR and assign

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

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

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, ....

letfruits=["🍎","🍌","🍐","🍊","🍋"]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 AssignmentPrecedence. 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{guardrhs>0else{return""}returnString(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]{returnzip(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.

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:

precedencegroupExponentiationPrecedence{associativity:righthigherThan:MultiplicationPrecedence}

Now we can declare the operator itself:

infixoperator**:ExponentiationPrecedence

Finally, we implement a top-level function using our new operator:

importDarwinfunc**(lhs:Double,rhs:Double)->Double{returnpow(lhs,rhs)}2**3// 8

When you create a custom operator, consider providing a mutating variant as well:

infixoperator**=:AssignmentPrecedencefunc**=(lhs:inoutDouble,rhs:Double){lhs=pow(lhs,rhs)}varn:Double=10n**=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:

importDarwinprefixoperatorprefixfunc(_value:Double)->Double{returnsqrt(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:

infixoperator±:AdditionPrecedencefunc±<T:Numeric>(lhs:T,rhs:T)->(T,T){return(lhs+rhs,lhs-rhs)}prefixoperator±prefixfunc±<T:Numeric>(_value:T)->(T,T){return0±value}2±3// (5, -1)±4// (4, -4)

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 +).

TimeInterval, Date, and DateInterval

$
0
0

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 1209600 second fortnight.

TimeInterval (neé 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.

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:

publicstructDate:ReferenceConvertible,Comparable,Equatable{publictypealiasReferenceType=NSDatefileprivatevar_time:TimeInterval// ...}

The reference date, in this case, is the first instant of January 1, 2001, Greenwich Mean Time (GMT).

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:

letcalendar=Calendar.currentletdate=Date()letdateInterval=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:

letdstComponents=DateComponents(year:2018,month:11,day:4)calendar.dateInterval(of:.day,for:calendar.date(from:dstComponents)!)?.duration// 90000 seconds

It’s 2018-10-08 11:56:58 -0700. 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:

importFoundationletcalendar=Calendar.current// Simon Vouet// 9 January 1590 – 30 June 1649letvouet=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 1640letrubens=DateInterval(start:calendar.date(from:DateComponents(year:1577,month:6,day:28))!,end:calendar.date(from:DateComponents(year:1640,month:5,day:30))!)letoverlap=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:

letformatter=DateIntervalFormatter()formatter.timeStyle=.noneformatter.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.

DateComponents

$
0
0

There are as many mnemonic devices for making sense of time as the day is long. “Spring ahead, Fall back”. That knuckle trick for remembering the lengths of months. Musical theater aficionados can tell you in quick measure the length of a year in minutes. Mathematicians, though, have the best ones of all: Did you know that the fifth hyperfactorial (5⁵ × 4⁴ × 3³ × 2² × 1¹) is equal to 86400000, or exactly 1 (civil) day in milliseconds? Or that ten factorial (10! = 10 × 9 × 8… = 3628800) seconds is equal to 6 weeks?

Amazing, right? But I want you to forget all of those, at least for the purposes of programming.

As we discussed in our article about Date, et al., the only unit of time with a constant duration is the second (and its subdivisions). When you want to express the duration of, 1 day, don’t write 60 * 60 * 24. Instead, write DateComponents(day: 1).

“What is DateComponents”, you ask? It’s a relatively recent addition to Foundation for representing a date or duration of time, and it’s the subject of this article.


DateComponents is a useful, but ambiguous type.

Taken in one context, date components can be used to represent a specific calendar date. But in another context, the same object might instead be used as a duration of time. For example, a date components object with year set to 2018, month set to 10, and day set to 10 could represent a period of 2018 years, 10 months, and 3 days or the third day of the tenth month in the year 2018:

importFoundationletcalendar=Calendar.currentletdateComponents=DateComponents(calendar:calendar,year:2018,month:10,day:10)// DateComponents as a date specifierletdate=calendar.date(from:dateComponents)!// 2018-10-10// DateComponents as a duration of timecalendar.date(byAdding:dateComponents,to:date)// 4037-08-20

Let’s explore both of these contexts individually, starting with date components as a representation of a calendar date:


Date Components as a Representation of a Calendar Date

Extracting Components from a Date

DateComponents objects can be created for a particular date using the Calendar method components(_:from:):

letdate=Date()// 2018-10-10T10:00:00+00:00letcalendar=Calendar.currentcalendar.dateComponents([.year,.month,.day],from:date)// (year: 2018, month: 0, day: 10)

Each property in DateComponents has a corresponding entry in the Calendar.Component enumeration.

For reference, here’s what the dateComponents(_:from:) method produces when you specify all of the available calendar units:

importFoundationletdate=Date()// 2018-10-10T10:00:00+00:00letcalendar=Calendar.currentletdateComponents=calendar.dateComponents([.calendar,.timeZone,.era,.quarter,.year,.month,.day,.hour,.minute,.second,.nanosecond,.weekday,.weekdayOrdinal,.weekOfMonth,.weekOfYear,.yearForWeekOfYear],from:date)
ComponentValue
calendargregorian
timeZoneAmerica/Los_Angeles
era1
quarter0
year2018
month10
day3
hour10
minute0
second0
nanosecond123456789
weekday4
weekdayOrdinal2
weekOfMonth2
weekOfYear41
yearForWeekOfYear2018
isLeapMonthfalse

One of the advantages of learning Foundation APIs is that you gain a deeper understanding of the domains that it models. Unless you’re a horologist or ISO 8601 enthusiast, there are probably a few of these components that you’re less familiar with, so let’s take a look at some of the more obscure ones:

Era and Year

The Gregorian calendar has two eras: BC and AD (alternatively, C.E. and B.C.E). Their respective integer date component values are 0 and 1. No matter what the era is, the year component is always a positive number.

Quarter

In academia and business, calendar years are often divided up into quarter (Q1, Q2, Q3, Q4).

Weekday, Weekday Ordinal, and Week of Month

Weekdays are given integer values starting with 1 for Sunday and ending with 6 for Saturday.

But the first weekday varies across different locales. The first weekday in the calendar depends on your current locale. The United States, China, and other countries begin their weeks on Sunday. Most countries in Europe, as well as India, Australia, and elsewhere typically designate Monday as their first weekday. Certain locales in the Middle East and North Africa use Saturday as the start of their week.

The locale also affects the values returned for the weekdayOrdinal and weekOfMonth components. In the en-US locale, the date components returned for October 7th, 2018 would have weekdayOrdinal equal to 1 (meaning “the first Sunday of the month”) and a weekOfMonth value of 2 (meaning “the second week of the month”).

Week of Year and Year for Week of Year

These two are probably the most confusing of all the date components. Part of that has to do with the ridiculous API name yearForWeekOfYear, but it mostly comes down to the lack of general awareness for ISO week dates.

The weekOfYear component returns the ISO week number for the date in question. For example, October 10th, 2018 occurs on the 41st ISO week.

The yearForWeekOfYear component is helpful for weeks that span two calendar years. For example, New Years Eve this year — December 31st, 2018 — falls on a Monday. Because occurs in the first week of 2019, its weekOfYear value is 1, its yearForWeekOfYear value is 2019, and its year value is 2018

Creating a Date from Date Components

In addition to extracting components from a date, we can go the opposite direction to create a date from components using the Calendar method date(from:).

Use it the next time you need to initialize a static date as a more performant and reliable way than parsing a timestamp with a date formatter.

vardate:Date?// Badlettimestamp="2018-10-03"letformatter=ISO8601DateFormatter()formatter.formatOptions=[.withFullDate,.withDashSeparatorInDate]date=formatter.date(from:timestamp)// Goodletcalendar=Calendar.currentletdateComponents=DateComponents(calendar:calendar,year:2018,month:10,day:3)date=calendar.date(from:dateComponents)

When date components are used to represent a date, there’s still some ambiguity. Date components can be (and often are) under-specified, such that the values of components like era or hour are inferred from additional context. When you use the date(from:) method, what you’re really doing is telling Calendar to search for the next date that satisfies the criteria you specified.

Sometimes this isn’t possible, like if date components have contradictory values (such as weekOfYear = 1 and weekOfMonth = 3), or a value in excess of what a calendar allows (such as an hour = 127). In these cases, date(from:) returns nil.

Getting the Range of a Calendar Unit

A common task when working with dates is to get the start of day, week, month, or year. Although it’s possible to do this with DateComponents creating a new date with a subset of date component values, a better way would be to use the Calendar method dateInterval(of:for:):

letdate=Date()// 2018-10-10T10:00:00+00:00letcalendar=Calendar.currentvarbeginningOfMonth:Date?// OKletdateComponents=calendar.dateComponents([.year,.month],from:date)beginningOfMonth=calendar.date(from:dateComponents)// BetterbeginningOfMonth=calendar.dateInterval(of:.month,for:date)?.start

Date Components as a Representation of a Duration of Time

Calculating the Distance Between Two Dates

Picking up from the previous example — you can use the Calendar method dateComponents(_:from:to:) to calculate the time between two dates in terms of your desired units.

How long is the month of October in hours?

letdate=Date()// 2018-10-10T10:00:00+00:00letcalendar=Calendar.currentletmonthInterval=calendar.dateInterval(of:.month,for:date)!calendar.dateComponents([.hour],from:monthInterval.start,to:monthInterval.end).hour// 744

Adding Components to Dates

Another frequent programming task is to calculate a date from an offset like “tomorrow” or “next week”.

If you’re adding a single calendar component value, you can use the Calendar method date(byAdding:value:to:):

letdate=Date()// 2018-10-10T10:00:00+00:00letcalendar=Calendar.currentvartomorrow:Date?// Badtomorrow=date.addingTimeInterval(60*60*24)// Goodtomorrow=calendar.date(byAdding:.day,value:1,to:date)

For more than one calendar component value, use the date(byAdding:to:) method instead, passing a DateComponents object.

letdate=Date()letcalendar=Calendar.current// Adding a yearcalendar.date(byAdding:.year,value:1,to:date)// Adding a year and a dayletdateComponents=DateComponents(year:1,day:1)calendar.date(byAdding:dateComponents,to:date)

If you really want to be pedantic when time traveling, though, the method you’re looking for is nextDate(after:matching:matchingPolicy:repeatedTimePolicy:direction:). For example, if you wanted to find the date corresponding to the next time with the same time components (hour, minute, second, nanosecond) and wanted to be specific about how to handle phenomena like 2:59AM occurring twice on November 4th, 2018, here’s how you might do that:

letdateComponents=calendar.dateComponents([.hour,.minute,.second,.nanosecond],from:date)yesterday=calendar.nextDate(after:date,matching:dateComponents,matchingPolicy:.nextTime,repeatedTimePolicy:.first,direction:.forward)

So there you have it! Now you know how to do calendar arithmetic correctly using Calendar and DateComponents.

To help you remember, we humbly offer the following mnemonic:

Are you multiplying seconds? Don’t! /
Instead, use (NS)DateComponents*

* NS prefix added to make the meter work. Thanks, Swift 3.

numericCast(_:)

$
0
0

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:

publicfuncnumericCast<T:BinaryInteger,U:BinaryInteger>(_x:T)->U{returnU(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-of-range 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).

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:

letarrayOfInt:[Int]=[1,2,3]letarrayOfUInt:[UInt]=[1,2,3]

Despite their seeming equivalence, we can’t, for example, do this:

arrayOfIntas[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:

letarrayOfNegativeInt:[Int]=[-1,-2,-3]arrayOfNegativeInt.map(numericCast)as[UInt]// 🧞‍ Fatal error: Negative value is not representable

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

letarrayOfNegativeInt:[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 number, 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_tarc4random_uniform(uint32_t__upper_bound)

arc4random requires not one but two separate type conversions in Swift: first for the upper bound parameter (IntUInt32) and second for the return value (UInt32Int):

importDarwinfuncrandom(inrange:Range<Int>)->Int{returnInt(arc4random_uniform(UInt32(range.count)))+range.lowerBound}

Gross.

By using numericCast(_:), we can make things a little more readable, albeit longer:

importDarwinfuncrandom(inrange:Range<Int>)->Int{returnnumericCast(arc4random_uniform(numericCast(range.count)))+range.lowerBound}

numericCast(_:) isn’t doing anything here that couldn’t otherwise be accomplished with type-appropriate 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)// 🧞‍ Fatal error: Not enough bits to represent the passed value

If we look at the Standard Library implementation that now lets 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?

ValueTransformer

$
0
0

Of all the Foundation classes, ValueTransformer is perhaps the one that fared the worst in the shift from macOS to iOS.

Why? Here are two reasons:

First, ValueTransformer was used primarily in AppKit with Cocoa bindings. There, they could automatically transform values from one property to another, like for negating a boolean or checking whether a value was nil, without the need of intermediary glue code. iOS, of course, doesn’t have bindings.

The second reason has less to do with iOS than the Objective-C runtime itself. With the introduction of blocks, it got a whole lot easier to pass behavior between objects — significantly easier than, say ValueTransformer or NSInvocation. So even if iOS were to get bindings tomorrow, it’s unclear whether ValueTransformer would play a significant role this time around.

But you know what? ValueTransformer might just be ripe for a comeback. With a little bit of re-tooling and some recontextualization, this blast from the past could be the next big thing in your application.


ValueTransformer is an abstract class that transforms one value into another. A transformation specifies what kinds of input values can be handled and whether it supports reversible transformations.

A typical implementation looks something like this:

classClassNameTransformer:ValueTransformer{overrideclassfunctransformedValueClass()->AnyClass{returnNSString.self}overrideclassfuncallowsReverseTransformation()->Bool{returnfalse}overridefunctransformedValue(_value:Any?)->Any?{guardlettype=valueas?AnyClasselse{returnnil}returnNSStringFromClass(type)}}
@interfaceClassNameTransformer:NSValueTransformer{}@end#pragma mark -
        @implementationClassNameTransformer+(Class)transformedValueClass{return[NSStringclass];}+(BOOL)allowsReverseTransformation{returnNO;}-(id)transformedValue:(id)value{return(value==nil)?nil:NSStringFromClass([valueclass]);}@end

ValueTransformer is rarely initialized directly. Instead, it follows a pattern familiar to fans of NSPersistentStore or NSURLProtocol, where a class is registered and instances are created from a manager — except in this case, you register a named instance to act as a singleton:

extensionClassNameTransformer{staticletname=NSValueTransformerName(rawValue:"ClassNameTransformer")}// Set the value transformerValueTransformer.setValueTransformer(ClassNameTransformer(),forName:ClassNameTransformer.name)// Get the value transformerletvalueTransformer=ValueTransformer(forName:ClassNameTransformer.name)
NSValueTransformerNameconstClassNameTransformerName=@"ClassNameTransformer";// Set the value transformer[NSValueTransformersetValueTransformer:[[ClassNameTransformeralloc]init]forName:ClassNameTransformerName];// Get the value transformerNSValueTransformer*valueTransformer=[NSValueTransformervalueTransformerForName:ClassNameTransformerName];

A common pattern is to register the singleton instance in the +initialize method of the value transformer subclass so it can be used without additional setup.

Now at this point you probably realize ValueTransformer’s fatal flaw: it’s super annoying to set up! Create a class, implement a handful of simple methods, define a constant, and register it in an +initialize method? No thanks.

In this age of blocks, we want — nay, demand— a way to declare functionality in one (albeit gigantic) line of code.

Nothing a little metaprogramming can’t fix. Behold:

letTKCapitalizedStringTransformerName=NSValueTransformerName(rawValue:"TKCapitalizedStringTransformerName")ValueTransformer.registerValueTransformerWithName(TKCapitalizedStringTransformerName,transformedValueClass:NSString.self){objectinguardletstring=objectas?Stringelse{returnnil}returnstring.capitalized}
NSValueTransformerNameconstTKCapitalizedStringTransformerName=@"TKCapitalizedStringTransformerName";[NSValueTransformerregisterValueTransformerWithName:TKCapitalizedStringTransformerNametransformedValueClass:[NSStringclass]returningTransformedValueWithBlock:^id(idvalue){return[valuecapitalizedString];}];

Now with a fresh new look, we can start to get a better understanding of how we might take advantage of ValueTransformer:

Making Business Logic More Functional

ValueTransformer objects are a great way to represent an ordered chain of fixed transformations. For instance, an app interfacing with a legacy system might transform user input through a succession of string transformations (trim whitespace, remove diacritics, and then capitalize letters) before sending it off to the mainframe.

Thinking Forwards and Backwards

Unlike blocks, value transformers have the concept of reversibility, which enables some interesting use cases.

Say you were wanted to map keys from a REST API representation into a model. You could create a reversible transformation that converted snake_case to llamaCase when initializing, and llamaCase to snake_case when serializing back to the server.

Configuring Functionality

Another advantage over blocks is that ValueTransformer subclasses can expose new properties that can be used to configure behavior in a particular way. Access to properties also provides a clean way to cache or memoize results and do any necessary book-keeping along the way.

Transforming Your Core Data Stack

Lest we forget, ValueTransformer can be used alongside Core Data to encode and decode compound data types from blob fields. It seems to have fallen out of fashion over the years, but serializing simple collections in this way can be a winning strategy for difficult-to-model data. (Just don’t use this approach to serialize images or other binary data; use external storage instead)


ValueTransformer, far from a vestige of AppKit, remains Foundation’s purest connection to functional programming: input goes in, output comes out.

While it’s true that Objective-C blocks and all of the advanced language features in Swift are superior examples of the functional programming paradigm. ValueTransformer has a special place in Cocoa’s history and Xcode’s tooling. For that reason, object orientation is transformed from an embarrassing liability to its greatest asset.

And though it hasn’t aged very well on its own, a little modernization restores ValueTransformer to that highest esteem of NSHipsterdom: a solution that we didn’t know we needed but was there all along.

SwiftSyntax

$
0
0

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, SwiftSynax 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 code-completion 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:

funcone()->Int{return1}

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"},...

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 (FunctionDecl), 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:

importBasicimportFoundationletcode:Stringlettempfile=tryTemporaryFile(deleteOnClose:true)defer{tempfile.fileHandle.closeFile()}tempfile.fileHandle.write(code.data(using:.utf8)!)leturl=URL(fileURLWithPath:tempfile.path.asString)letsourceFile=trySyntaxTreeParser.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:

importSwiftSyntaxletstructKeyword=SyntaxFactory.makeStructKeyword(trailingTrivia:.spaces(1))letidentifier=SyntaxFactory.makeIdentifier("Example",trailingTrivia:.spaces(1))letleftBrace=SyntaxFactory.makeLeftBraceToken()letrightBrace=SyntaxFactory.makeRightBraceToken(leadingTrivia:.newlines(1))letmembers=MemberDeclBlockSyntax{builderinbuilder.useLeftBrace(leftBrace)builder.useRightBrace(rightBrace)}letstructureDeclaration=StructDeclSyntax{builderinbuilder.useStructKeyword(structKeyword)builder.useIdentifier(identifier)builder.useMembers(members)}print(structureDeclaration)

Whew. So what did all of that effort get us?

structExample{}

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-but-ostensibly-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:

importSwiftSyntaxpublicclassZalgoRewriter:SyntaxRewriter{publicoverridefuncvisit(_token:TokenSyntax)->Syntax{guardcaselet.stringLiteral(text)=token.tokenKindelse{returntoken}returntoken.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 SyntaxRewriter subclass:

importSwiftSyntaxclassSwiftSyntaxHighlighter:SyntaxRewriter{varhtml:String=""overridefuncvisit(_token:TokenSyntax)->Syntax{switchtoken.tokenKind{// ...case.floatingLiteral(letstring):html+="<span class=\"mf\">\(string)</span>"case.integerLiteral(letstring):ifstring.hasPrefix("0b"){html+="<span class=\"mb\">\(string)</span>"}elseifstring.hasPrefix("0o"){html+="<span class=\"mo\">\(string)</span>"}elseifstring.hasPrefix("0x"){html+="<span class=\"mh\">\(string)</span>"}else{html+="<span class=\"mi\">\(string)</span>"}// ...default:break}returntoken}}

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!

Temporary Files

$
0
0

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:

  • Creating a temporary directory in the filesystem
  • Creating a temporary file in that directory with a unique filename
  • Writing data to the temporary file
  • 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 get in the way of the user or accidentally 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.

Instead, let’s ask FileManager to point us in the right direction using the uri(for:in:appropriateFor:create:) method:

lettemporaryDirectoryURL=tryFileManager.default.url(for:.itemReplacementDirectory,in:.userDomainMask,appropriateFor:URL(fileURLWithPath:"/"),create:true)
NSFileManager*fileManager=[NSFileManagerdefaultManager];NSError*error=nil;NSURL*temporaryDirectoryURL=[fileManagerURLForDirectory:NSItemReplacementDirectoryinDomain:NSUserDomainMaskappropriateForURL:[NSURLfileURLWithPath:@"/"]create:YESerror:&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, the only part of the file URL that’s considered is the volume; therefore, we can pass URL(fileURLWithPath: "/") to specify the current volume.
  • 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
[[NSProcessInfoprocessInfo]globallyUniqueString];

The resulting filename will look something like this: 42BC63F7-E79E-4E41-8E0D-B72B049E9254-25121-000144AB9F08C9C1

Alternatively, UUID also produces workably unique identifiers:

UUID().uuidString
[[NSUUIDUUID]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:

lettemporaryDirectoryURL=tryFileManager.default.url(for:.itemReplacementDirectory,in:.userDomainMask,appropriateFor:URL(fileURLWithPath:"/"),create:true)lettemporaryFilename=ProcessInfo().globallyUniqueStringlettemporaryFileURL=temporaryDirectoryURL.appendingPathComponent(temporaryFilename)
NSFileManager*fileManager=[NSFileManagerdefaultManager];NSError*error=nil;NSURL*temporaryDirectoryURL=[fileManagerURLForDirectory:NSItemReplacementDirectoryinDomain:NSUserDomainMaskappropriateForURL:[NSURLfileURLWithPath:@"/"]create:YESerror:&error];NSString*temporaryFilename=[[NSProcessInfoprocessInfo]globallyUniqueString];NSURL*temporaryFileURL=[temporaryDirectoryURLURLByAppendingPathComponent: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):

letdata:Datatrydata.write(to:temporaryFileURL,options:.atomicWrite)
NSData*data;NSError*error=nil;[datawriteToURL:temporaryFileURLoptions:NSDataWritingAtomicerror:&error];

By passing the atomicWrite 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:

letfileHandle=tryFileHandle(forWritingTo:temporaryFileURL)defer{fileHandle.closeFile()}fileHandle.write(data)
NSError*error=nil;NSFileHandle*fileHandle=[NSFileHandlefileHandleForWritingToURL:temporaryFileURLerror:&error];[fileHandlewriteData:data];[fileHandlecloseFile];

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:

letoutputStream=OutputStream(url:temporaryFileURL,append:true)!defer{outputStream.close()}data.withUnsafeBytes{bytesinoutputStream.write(bytes,maxLength:bytes.count)}
NSOutputStream*outputStream=[NSOutputStreamoutputStreamWithURL:temporaryFileURLappend:YES];[outputStreamwrite:data.bytesmaxLength:data.length];[outputStreamclose];

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:

letfileURL:URLtryFileManager.default.moveItem(at:temporaryFileURL,to:fileURL)
NSFileManager*fileManager=[NSFileManagerdefaultManager];NSURL*fileURL;NSError*error=nil;[fileManagermoveItemAtURL:temporaryFileURLtoURL:fileURLerror:&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:

tryFileManager.default.removeItem(at:temporaryFileURL)
NSFileManager*fileManager=[NSFileManagerdefaultManager];NSError*error=nil;[fileManagerremoveItemAtURL:temporaryFileURLerror:&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.


FileManager

$
0
0

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.

letdocumentsDirectoryURL=tryFileManager.default.url(for:.documentDirectory,in:.userDomainMask,appropriateFor:nil,create:false)
NSFileManager*fileManager=[NSFileManagerdefaultManager];NSString*documentsPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)firstObject];NSString*filePath=[documentsPathstringByAppendingPathComponent:@"file.txt"];

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:

letfileURL:URLletfileExists=FileManager.default.fileExists(atPath:fileURL.path)
NSURL*fileURL;NSFileManager*fileManager=[NSFileManagerdefaultManager];BOOLfileExists=[fileManagerfileExistsAtPath:[fileURLpath]];

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:

letfileURL:URLletattributes=FileManager.default.attributesOfItem(atPath:fileURL.path)letcreationDate=attributes[.creationDate]
NSURL*fileURL;NSFileManager*fileManager=[NSFileManagerdefaultManager];NSError*error=nil;NSDictionary*attributes=[fileManagerattributesOfItemAtPath:[fileURLpath]error:&error];NSDate*creationDate=attributes[NSFileCreationDate];

Listing Files in a Directory

To list the contents of a directory, call the FileManager method contentsOfDirectory(at:includingPropertiesForKeys: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.

letdirectoryURL:URLletcontents=tryFileManager.default.contentsOfDirectory(at:directoryURL,includingPropertiesForKeys:nil,options:[.skipsHiddenFiles])forfileincontents{// ...}
NSFileManager*fileManager=[NSFileManagerdefaultManager];NSURL*bundleURL=[[NSBundlemainBundle]bundleURL];NSArray*contents=[fileManagercontentsOfDirectoryAtURL:bundleURLincludingPropertiesForKeys:@[]options:NSDirectoryEnumerationSkipsHiddenFileserror:nil];NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"pathExtension == 'png'"];for(NSURL*fileURLin[contentsfilteredArrayUsingPredicate: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:

letdirectoryURL:URLifletenumerator=FileManager.default.enumerator(atPath:directoryURL.path){forcaseletpathasStringinenumerator{// Skip entries with '_' prefix, for exampleifpath.hasPrefix("_"){enumerator.skipDescendants()}}}
NSFileManager*fileManager=[NSFileManagerdefaultManager];NSURL*bundleURL=[[NSBundlemainBundle]bundleURL];NSDirectoryEnumerator*enumerator=[fileManagerenumeratorAtURL:bundleURLincludingPropertiesForKeys:@[NSURLNameKey,NSURLIsDirectoryKey]options:NSDirectoryEnumerationSkipsHiddenFileserrorHandler:^BOOL(NSURL*url,NSError*error){if(error){NSLog(@"[Error] %@ (%@)",error,url);returnNO;}returnYES;}];NSMutableArray*mutableFileURLs=[NSMutableArrayarray];for(NSURL*fileURLinenumerator){NSString*filename;[fileURLgetResourceValue:&filenameforKey:NSURLNameKeyerror:nil];NSNumber*isDirectory;[fileURLgetResourceValue:&isDirectoryforKey:NSURLIsDirectoryKeyerror:nil];// Skip directories with '_' prefix, for exampleif([filenamehasPrefix:@"_"]&&[isDirectoryboolValue]){[enumeratorskipDescendants];continue;}if(![isDirectoryboolValue]){[mutableFileURLsaddObject: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.

tryFileManager.default.createDirectory(at:directoryURL,withIntermediateDirectories:true,attributes:nil)
NSFileManager*fileManager=[NSFileManagerdefaultManager];NSString*documentsPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)firstObject];NSString*imagesPath=[documentsPathstringByAppendingPathComponent:@"images"];if(![fileManagerfileExistsAtPath:imagesPath]){NSError*error=nil;[fileManagercreateDirectoryAtPath:imagesPathwithIntermediateDirectories:NOattributes:nilerror:&error];}

Deleting a File or Directory

If you want to delete a file or directory, call removeItem(at:):

letfileURL:URLtryFileManager.default.removeItem(at:fileURL)
NSFileManager*fileManager=[NSFileManagerdefaultManager];NSString*documentsPath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)firstObject];NSString*filePath=[documentsPathstringByAppendingPathComponent:@"image.png"];NSError*error=nil;if(![fileManagerremoveItemAtPath:filePatherror:&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.

classCustomFileManagerDelegate:NSObject,FileManagerDelegate{funcfileManager(_fileManager:FileManager,shouldRemoveItemAtURL:URL)->Bool{// Don't delete PDF filesreturnURL.pathExtension!="pdf"}}// Maintain strong references to fileManager and delegateletfileManager=FileManager()letdelegate=CustomFileManagerDelegate()fileManager.delegate=delegate
NSFileManager*fileManager=[[NSFileManageralloc]init];fileManager.delegate=delegate;NSURL*bundleURL=[[NSBundlemainBundle]bundleURL];NSArray*contents=[fileManagercontentsOfDirectoryAtURL:bundleURLincludingPropertiesForKeys:@[]options:NSDirectoryEnumerationSkipsHiddenFileserror:nil];for(NSString*filePathincontents){[fileManagerremoveItemAtPath:filePatherror:nil];}// CustomFileManagerDelegate.m#pragma mark - NSFileManagerDelegate
        -(BOOL)fileManager:(NSFileManager*)fileManagershouldRemoveItemAtURL:(NSURL*)URL{return![[[URLlastPathComponent]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.

Void

$
0
0

Fear is an automatic response.

It starts with sensory input from our eyes and ears and skin, which moves from the thalamus to the sensory cortex. This information is processed and combined with conscious memories from the hippocampus to provide context for the stimuli. If the amygdala determines there to be a threat, the hypothalamus activates a “fight or flight” response, causing the release of epinephrine (adrenaline) and dozens of other hormones that prepare our bodies to respond to the threat.

Over countless generations, the shape of fear was more palpable — a predator, a fire, a sound in the night. But for lack of existential threats, fear becomes increasingly abstract.

We know not to fear what we know, so we fear the unknown. And nothing is more unknown than nothing.

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.

typealiasVoid=()

We become aware of the Void as we fill it.

letvoid: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.

protocolExpressibleByNilLiteral{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.

extensionVoid{}// 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{returntrue}

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{returnfalse}

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:

structWrapper<Value>{letvalue: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.

extensionWrapper:EquatablewhereValue:Equatable{staticfunc==(lhs:Wrapper<Value>,rhs:Wrapper<Value>)->Bool{returnlhs.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{returntrue}

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.

letwrapperOfVoid=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

funcdoSomething(){...}// Is equivalent tofuncdoSomething()->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 warningletresult=doSomething()// ⚠️ Constant 'result' inferred to have type 'Void', which may be unexpected

You can silence this warning by explicitly specifying the Void type.

letresult:Void=doSomething()// ()

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:

(trueasVoid?)// error

But hard-pressed, Void? can act in the same way as Bool. Consider the following function that randomly throws an error:

structFailure:Error{}funcfailsRandomly()throws{ifBool.random(){throwFailure()}}

The correct way to use this method is to call it within a do / catch block using a try expression.

do{tryfailsRandomly()// 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.

letsuccess:Void?=try?failsRandomly()ifsuccess!=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:

staticvaroneTimeSideEffect:Void?={returntry?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:

extensionOptional:ExpressibleByBooleanLiteralwhereWrapped==Void{publictypealiasBooleanLiteralType=Boolpublicinit(booleanLiteralvalue:Bool){ifvalue{self.init(())!}else{self.init(nilLiteral:())!}}}letpseudoBool: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.

enumResult<Value,Error>{casesuccess(Value)casefailure(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.

funcping(_url:URL,completion:(Result<Void,Error>)->Void){// ...}

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.

extensionResultwhereValue==Void{staticvarsuccess:Result{return.success(())}}

Nothing lost; nothing gained.

completion(.success)

Though 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.

Language Server Protocol

$
0
0

Last month, Apple announced on the Swift.org forums that it was starting work to adopt the Language Server Protocol (LSP) for Swift and C languages.

At Apple we are making it a priority to support high-quality tooling for all Swift developers, including those working on non-Apple platforms. We want to collaborate with the open-source community and focus our efforts on building common infrastructure that can be shared by Xcode and other editors and platforms. To that end, [ … ] we’ve chosen to adopt LSP.

Argyrios Kyrtzidis, October 15th, 2018

This is arguably the most important decision Apple has made for Swift since releasing the language as open source in 2014. It’s a big deal for app developers, and it’s an even bigger deal for Swift developers on other platforms.

To understand why, this week’s article will take a look at what problem the Language Server Protocol solves, how it works, and what it’s long-term impacts may be.


Imagine a grid with each row representing a different programming language (Swift, JavaScript, Ruby, Python, etc.) and each column representing a different code editor (Xcode, Visual Studio, Vim, Atom, etc.), such that each cell represents the level of support that a particular editor has for a language.

Up until recently, what you’d find was a patchwork of compatibility across the various combinations. Some editors offered deep integration with a few languages and little to no support for anything else, whereas other editors aimed to be general-purpose with at least a modicum of support for many languages. (The term IDE is often used to describe the former).

Case in point: You’d be stubborn not to use Xcode for app development and foolish to use it for anything else.

For an editor to have better support for a particular language, it needs to write integration code — either directly in the code base or via a plugin system. Due to implementation differences across languages and editors, improvements to, say, Ruby support in Vim wouldn’t translate into better support for Python, nor could they be applied to make Ruby work better in Atom. The end result: inconsistent support across technologies and a lot of wasted effort.

The situation we described is often referred to as an M × N problem, where the number of integrations is the product of M editors and N languages. What the Language Server Protocol does is change this M × N problem into a M + N problem.

Rather than an editor having to implement support for each language, it only needs to support the LSP. And in doing so, it gets the same level of functionality for all languages that support the LSP.

Language Server Protocol provides a common set of functionality for supported languages, including:

  • Syntax Highlighting
  • Automatic Formatting
  • Autocomplete
  • Syntax
  • Tooltips
  • Inline Diagnostics
  • Jump to Definition
  • Find References in Project
  • Advanced Text and Symbol Search

Rather than reinventing the wheel for each new technology, tools and editors can invest in better usability and more advanced functionality.

How Language Server Protocol Works

If you’re an iOS developer, you may be most familiar with the terms server and protocol in the sense of communicating with web applications in JSON format via HTTP. This actually isn’t too far off from how the Language Server Protocol works.

In the case of LSP, the client refers to the editor — or more generally, the tool — and the server refers to an external program run locally in a separate process.

As for the protocol itself, LSP resembles a simplified version of HTTP:

  • Each message consists of a header part and a content part.
  • The header part has a required Content-Length field containing the size of the content part in bytes, and an optional Content-Type field (application/vscode-jsonrpc; charset=utf-8 by default)
  • The content part uses JSON-RPC to describe the structure of requests, responses, and notifications.

Whenever something happens in the tool, such as the user jumping to the definition of a symbol, the tool sends a request to the server. The server receives that request and then returns an appropriate response.

For example, imagine that a user opens the following Swift code in an Xcode-like editor that supported the Language Server Protocol:

classParent{}classChild:Parent{}

When the user -clicks the symbol Parent in the inheritance clause on line 2, the editor jumps to the definition of the Parent class on line 1.

Here’s how LSP enables this interaction behind the scenes:

First, when the user opens the Swift code, the editor launches its Swift language server in a separate process, if it isn’t running already, and performs any additional setup.

When the user executes the “jump to definition” command, the editor sends the following request to its Swift language server:

{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///Users/NSHipster/Example.swift"},"position":{"line":1,"character":13}}}

Upon receiving this request, the Swift language server uses a compiler tool like SourceKit to identify the corresponding code entity and find the location of its declaration on the preceding line. The language server then responds with the following message:

{"jsonrpc":"2.0","id":1,"result":{"uri":"file:///Users/NSHipster/Example.swift","range":{"start":{"line":0,"character":6},"end":{"line":0,"character":12}}}}

Finally, the editor navigates to the file (which, in this case, is already open), moves the cursor to that range, and highlights the token.

The beauty of this approach is that the editor did all of this without knowing anything about the Swift programming language other than that .swift files are associated with Swift code. All the editor needs to do is talk to the language server and update the UI. And knowing how to do that, the editor can follow the same procedure to facilitate this interaction for code written in any language with a language server implementation.

Language Server Protocol Support in Clang / LLVM

If the M + N diagram from before looks familiar, it might be because it’s the same approach taken by LLVM.

At the core of LLVM is an intermediate representation (IR). Supported languages generate IR using a compiler frontend, and that IR can generate machine code for any platform supported by a compiler backend.

The LLVM compiler frontend used by Swift and C languages is called Clang. And in its recent 5.0.0 release, Clang added a new tool called Clangd, LLVM’s implementation for the Language Server Protocol.

In April 2018, Apple announced to the LLVM mailing list that it was switching the focus of its development efforts from libclang to Clangd as the primary way to create interactive tooling.

Now you might think, “So what?” Apple is among the most prominent supporters of the LLVM project — having, among other things, employed the project’s founder, Chris Lattner, for over a decade. Apple’s decision to switch from one obscure Clang tool to another would seem to an implementation detail (so to speak).

What makes this announcement quite interesting is that Clangd appears to have been created entirely outside of Apple, with significant contributions from Google and other companies. This announcement signals a significant shift in the direction of tooling development going forward — something that would be confirmed 6 months later on the Swift.org forums.

Potential Consequences of Apple’s Support of Language Server Protocol

According to Apple’s LSP announcement from October, we should expect to see the first bits of code for the project within the coming weeks (as of the time of writing; early to mid-November).

It’ll take a bit longer to feel the full impact of these developments, but believe me: your patience will be rewarded. Here are just a few of what I believe will happen as a result of LSP in the coming months and years.

Swift Becomes More Appealing as a General-Purpose Programming Language

Although Swift is used primarily for app development, it was designed from the start to be a capable general-purpose programming language. Between Swift for TensorFlow, SwiftNIO, and other projects, we’re just starting to see the promise of what Swift can do beyond the App Store.

Among the biggest factors holding Swift back from mainstream adoption up to this point has been its reliance on Xcode.

It’s a lot to ask, say, a web developer or machine learning engineer to download Xcode just to try Swift when there are so many great alternatives with a much lower barrier to entry. Support for the Language Server Protocol should make it significantly easier for folks outside the Apple ecosystem to evaluate Swift with the same, familiar tools they use for everything else.

Xcode Gets Better

Adopting LSP isn’t just about making Swift work better in other editors; Xcode stands to benefit immensely, as well.

Consider this forum post from Project Lead for Swift at Apple, Ted Kremenek:

The LSP service [Argyrios] is describing will be functionally more powerful than SourceKit is today.

LSP is an opportunity for the Xcode team to take a fresh approach to Swift integration, and to capitalize on all of the improvements to the language and tooling in the four years since its 1.0 release.

Xcode (Eventually) Becomes More Capable

The benefit of LSP isn’t limited to Swift and Objective-C; from another post by Argyrios in that thread:

Getting Xcode to use our new LSP service should make it viable to use other LSP services as well, and it’s something that we are interested in, but we don’t have specific plans to announce at this moment.

The main focus for the current efforts are to improve the story for Swift. But once that’s done, it should be relatively straightforward to have those benefits cascade down to other languages with LSP support.


The architecture of software reflects the structure and values of the organizations that create it. The converse is true as well, to some extent.

By adopting the open Language Server Protocol standard for Xcode, Apple is making good on its commitment to the success of Swift on platforms outside the Apple ecosystem. And I think it’ll work: tooling (or lack thereof) is often the key decider in which technologies gain mindshare. But perhaps more importantly, I believe this decision demonstrates an increased willingness within (at least some small part of) the company for collaboration and transparency.

OptionSet

$
0
0

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:

typedefNS_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.

extensionUIView{structAutoresizingMask:OptionSet{init(rawValue:UInt)staticvarflexibleLeftMargin:UIView.AutoresizingMaskstaticvarflexibleWidth:UIView.AutoresizingMaskstaticvarflexibleRightMargin:UIView.AutoresizingMaskstaticvarflexibleTopMargin:UIView.AutoresizingMaskstaticvarflexibleHeight:UIView.AutoresizingMaskstaticvarflexibleBottomMargin:UIView.AutoresizingMask}}

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:

structToppings:OptionSet{letrawValue:Intstaticletpepperoni=Toppings(rawValue:1<<0)staticletonions=Toppings(rawValue:1<<1)staticletbacon=Toppings(rawValue:1<<2)staticletextraCheese=Toppings(rawValue:1<<3)staticletgreenPeppers=Toppings(rawValue:1<<4)staticletpineapple=Toppings(rawValue:1<<5)staticletmeatLovers:Toppings=[.pepperoni,.bacon]staticlethawaiian:Toppings=[.pineapple,.bacon]staticletall:Toppings=[.pepperoni,.onions,.bacon,.extraCheese,.greenPeppers,.pineapple]}

Taken into a larger example for context:

structPizza{enumStyle{caseneapolitan,sicilian,newHaven,deepDish}structToppings:OptionSet{...}letdiameter:Intletstyle:Stylelettoppings:Toppingsinit(inchesInDiameterdiameter:Int,style:Style,toppings:Toppings=[]){self.diameter=diameterself.style=styleself.toppings=toppings}}letdinner=Pizza(inchesInDiameter:12,style:.neapolitan,toppings:[.greenPepper,.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:

extensionPizza{varisVegetarian:Bool{returntoppings.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 cakepizza 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:

enumTopping:String,Option{casepepperoni,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”:

extensionSetwhereElement==Topping{staticvarmeatLovers:Set<Topping>{return[.pepperoni,.bacon]}staticvarhawaiian:Set<Topping>{return[.pineapple,.bacon]}staticvarall:Set<Topping>{returnSet(Element.allCases)}}typealiasToppings=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:

extensionSetwhereElement:Option{varrawValue:Int{varrawValue=0for(index,element)inElement.allCases.enumerated(){ifself.contains(element){rawValue|=(1<<index)}}returnrawValue}}

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.


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.

CustomPlaygroundDisplayConvertible

$
0
0

Playgrounds allow you to see what your Swift code is doing every step along the way. Each time a statement is executed, its result is logged to the sidebar along the right-hand side. From there, you can open a Quick Look preview of the result in a popover or display the result inline, directly in the code editor.

The code responsible for providing this feedback is provided by the PlaygroundLogger framework, which is part of the open source swift-xcode-playground-support project.

Reading through the code, we learn that the Playground logger distinguishes between structured values, whose state is disclosed by inspecting its internal members, and opaque values, which provide a specialized representation of itself. Beyond those two, the logger recognizes entry and exit points for scopes (control flow statements, functions, et cetera) as well as runtime errors (caused by implicitly unwrapping nil values, fatalError(), and the like) Anything else — imports, assignments, blank lines — are considered gaps

Built-In Opaque Representations

The Playground logger provides built-in opaque representations for many of the types you’re likely to interact with in Foundation, UIKit, AppKit, SpriteKit, CoreGraphics, CoreImage, and the Swift standard library:

CategoryTypesResult
Strings
  • String
  • NSString
"Hello, world!"
Attributed Strings
  • NSAttributedString
"Hello, world!"
Numbers
  • Int, UInt, …
  • Double, Float, …
  • CGFloat
  • NSNumber
42
Ranges
  • NSRange
{0, 10}
Boolean Values
  • Bool
true
Pointers
  • UnsafePointer
  • UnsafeMutablePointer
  • UnsafeRawPointer
  • UnsafeMutableRawPointer
0x0123456789ABCDEF
Dates
  • Date
  • NSDate
Nov 12, 2018 at 10:00
URLs
  • URL
  • NSURL
https://nshipster.com
Colors
  • CGColor
  • NSColor
  • UIColor
  • CIColor
🔴 r 1.0 g 0.0 b 0.0 a 1.0
Geometry
  • CGPoint
  • CGSize
  • CGRect
{x 0 y 0 w 100 h 100}
Bezier Paths
  • NSBezierPath
  • UIBezierPath
11 path elements
Images
  • CGImage
  • NSCursor
  • NSBitmapImageRep
  • NSImage
  • UIImage
w 50 h 50
SpriteKit Nodes
  • SKShapeNode
  • SKSpriteNode
  • SKTexture
  • SKTextureAtlas
SKShapeNode
Views
  • NSView
  • UIView
NSView

Structured Values

Alternatively, the Playground logger provides for values to be represented structurally — without requiring an implementation of the CustomReflectable protocol(* update forthcoming).

This works if the value is a tuple, an enumeration case, or an instance of a class or structure. It handles aggregates, or values bridged from an Objective-C class, as well as containers, like arrays and dictionaries. If the value is an optional, the logger will implicitly unwrap its value, if present.

Customizing How Results Are Logged In Playgrounds

Developers can customize how the Playground logger displays results by extending types to adopt the CustomPlaygroundDisplayConvertible protocol and implement the required playgroundDescription computed property.

For example, let’s say you’re using Playgrounds to familiarize yourself with the Contacts framework. You create a new CNMutableContact, set the givenName and familyName properties, and provide an array of CNLabeledValue values to the emailAddresses property:

importContactsletcontact=CNMutableContact()contact.givenName="Johnny"contact.familyName="Appleseed"contact.emailAddresses=[CNLabeledValue(label:CNLabelWork,value:"johnny@apple.com")]

If you were hoping for feedback to validate your API usage, you’d be disappointed by what shows up in the results sidebar:

`<CNMutableContact: 0x7ff727e38bb0: ... />`

To improve on this, we can extend the superclass of CNMutableContact, CNContact, and have it conform to CustomPlaygroundDisplayConvertible. The Contacts framework includes CNContactFormatter, which offers a convenient way to summarize a contact:

extensionCNContact:CustomPlaygroundDisplayConvertible{publicvarplaygroundDescription:Any{returnCNContactFormatter.string(from:self,style:.fullName)??""}}

By putting this at the top of our Playground (or in a separate file in the Playground’s auxilliary sources), our contact from before now provides a much nicer Quick Look representation:

"Johnny Appleseed"

To provide a specialized Playground representation, delegate to one of the value types listed in the table above. In this case, the ContactsUI framework provides a CNContactViewController class whose view property we can use here:

importContactsUIextensionCNContact:CustomPlaygroundDisplayConvertible{publicvarplaygroundDescription:Any{letviewController=CNContactViewController()viewController.contact=selfreturnviewController.view}}

After replacing our original playgroundDescription implementation, our contact displays with the following UI:


Playgrounds occupy an interesting space in the Xcode tooling ecosystem. It’s neither a primary debugging interface, nor a mechanism for communicating with the end user. Rather, it draws upon both low-level and user-facing functionality to provide a richer development experience. Because of this, it can be difficult to understand how Playgrounds fit in with everything else.

Here’s a run-down of some related functionality:

Relationship to CustomStringConvertible and CustomDebugStringConvertible

The Playground logger uses the following criteria when determining how to represent a value in the results sidebar:

  • If the value is a String, return that value
  • If the value is CustomStringConvertible or CustomDebugStringConvertible, return String(reflecting:)
  • If the value is an enumeration (as determined by Mirror), return String(describing:)
  • Otherwise, return the type name, normalizing to remove the module from the fully-qualified name

Therefore, you can customize the Playground description for types by providing conformance to CustomStringConvertible or CustomDebugStringConvertible.

So the question becomes, “How do I decide which of these protocols to adopt?”

Here are some general guidelines:

  • Use CustomStringConvertible (description) to represent values in a way that’s appropriate for users.
  • Use CustomDebugStringConvertible (debugDescription) to represent values in a way that’s appropriate for developers.
  • Use CustomPlaygroundDisplayConvertible (playgroundDescription) to represent values in a way that’s appropriate for developers in the context of a Playground.

Within a Playground, expressiveness is prioritized over raw execution. So we have some leeway on how much work is required to generate descriptions.

For example, the default representation of most sequences is the type name (often with cryptic generic constraints):

letevens=sequence(first:0,next:{$0+2})

UnfoldSequence<Int, (Optional, Bool)>

Iterating a sequence has unknown performance characteristics, so it would be inappropriate to include that within a description or debugDescription. But in a Playground? Sure, go nuts— by associating it in the Playground itself, there’s little risk in that code making it into production.

So back to our original example, let’s see how CustomPlaygroundDisplayConvertible can help us decipher our sequence:

extensionUnfoldSequence:CustomPlaygroundDisplayConvertiblewhereElement:CustomStringConvertible{publicvarplaygroundDescription:Any{returnprefix(10).map{$0.description}.joined(separator:", ")+"…"}}

0, 2, 4, 6, 8, 10, 12, 14, 16, 18…

Relationship to Debug Quick Look

When a Playground logs structured values, it provides an interface similar to what you find when running an Xcode project in debug mode.

What this means in practice is that Playgrounds can approximate a debugger interface when working with structured types.

For example, the description of a Data value doesn’t tell us much:

letdata="Hello, world!".data(using:.utf8)

coming from description

13 bytes

And for good reason! As we described in the previous section, we want to keep the implementation of description nice and snappy.

By contrast, the structured representation of the same data object when viewed from a Playground tells us the size and memory address — it even shows an inline byte array for up to length 64:

count 13 pointer "UnsafePointer(7FFCFB609470)" [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]


Playgrounds use a combination of language features and tooling to provide a real-time, interactive development environment. With the CustomPlaygroundDisplayConvertible protocol, you can leverage this introspection for your own types.

Mirror / CustomReflectable / CustomLeafReflectable

$
0
0

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.

structChessBoard{typealiasRank=UInt8typealiasFile=UInt8structCoordinate:Equatable,Hashable{privateletvalue:UInt8varrank:Rank{return(value>>3)+1}varfile: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:

letb8=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:

extensionChessBoard.Coordinate:CustomReflectable{varcustomMirror:Mirror{returnMirror(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.Typeb8.customMirror.displayStyle// structforchildinb8.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

extensionChessBoard.Coordinate:CustomReflectable{varcustomMirror:Mirror{returnMirror(self,children:["rank":rank,"file":file])displayStyle:<#displayStyle#>)}}
StyleOutput
classChessBoard.Coordinate
structCoordinate(rank: 8, file: 2)
enumCoordinate(8)
optional8
tuple(rank: 8, file: 2)
collection[8, 2]
set{8, 2}
dictionary[rank: 8, file: 2]

Unlabeled Children

extensionChessBoard.Coordinate:CustomReflectable{varcustomMirror:Mirror{returnMirror(self,unlabeledChildren:[rank,file],displayStyle:<#displayStyle#>)}}
StyleOutput
classChessBoard.Coordinate
structCoordinate()
enumCoordinate(8)
optional8
tuple(8, 2)
collection[8, 2]
set{8, 2}
dictionary[ 8, 2]

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).

classPiece{enumColor:String{caseblack,white}letcolor:Colorletlocation:ChessBoard.Coordinate?init(color:Color,location:ChessBoard.Coordinate?=nil){self.color=colorself.location=locationprecondition(type(of:self)!=Piece.self,"Piece is abstract")}}classPawn:Piece{staticletvalue=1}classKnight:Piece{staticletvalue=3}classBishop:Piece{staticletvalue=3}classRook:Piece{staticletvalue=5}classQueen:Piece{staticletvalue=9}classKing:Piece{}

So far, so good. Now let’s use the same approach as before to define a custom mirror (setting the displayStyle to struct so that we get more reflected information than we would otherwise for a class):

extensionPiece:CustomReflectable{varcustomMirror:Mirror{returnMirror(self,children:["color":color,"location":location??"N/A"],displayStyle:.struct))}}

Let’s see what happens if we try to reflect a new Knight object:

letknight=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?

extensionKnight{overridevarcustomMirror:Mirror{returnMirror(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 CustomReflectable 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:

classPiece:CustomLeafReflectable{// ...varcustomMirror:Mirror{returnMirror(reflecting:self)}}classKnight:Piece{// ...overridevarcustomMirror:Mirror{returnMirror(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.

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.

extensionPiece:CustomReflectable{varcustomMirror:Mirror{returnMirror(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 CustomLeafReflectable. 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)? Yikes. Those kinds of heterogeneous interfaces give OOP a bad name and lead to unforeseen issues down the line.

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:

protocolPiece{varcolor:Color{get}varlocation:ChessBoard.Coordinate?{get}}protocolValuable:Piece{staticvarvalue:Int{get}}

Next, define value types — an enum for Color and structures for each piece:

enumColor:String{caseblack,white}// ...structKnight:Piece,Valuable{staticletvalue:Int=3letcolor:Colorletlocation: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:

extensionPiece{varcustomMirror:Mirror{returnMirror(self,children:["color":color,"location":location??"N/A"],displayStyle:.struct)}}extensionValuable{varcustomMirror:Mirror{returnMirror(self,children:["color":color,"location":location??"N/A","value":Self.value],displayStyle:.struct)}}// ...extensionKnight:CustomReflectable{}//...

Putting that all together, we get exactly the behavior we want with the advantage of value semantics.

letb8=ChessBoard.Coordinate(rank:8,file:2)letknight=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.

Swift Development with Visual Studio Code

$
0
0

Visual Studio Code (VSCode) is a cross-platform text and source code editor from Microsoft. It’s one of the most exciting open source projects today, with regular updates from hundreds of contributors. VSCode was among the first tools to support Language Server Protocol (LSP), which has played a large part in providing a great developer experience, in a variety of languages and technologies.

With the previously announced support for LSP for Swift now available in early development, it’s a great time to see how this integration works for yourself.

So this week, we’ll walk through the process of how to get started with Swift’s new Language Server Protocol support in Visual Studio Code on macOS. If you haven’t tried writing Swift outside Xcode, or are already a VSCode user and new to the language entirely, this article will tell you everything you need to know.


Step 0: Install Xcode

If you don’t already have Xcode installed on your machine, open the Terminal app and run the following command:

$ xcode-select --install

Running this command presents a system prompt.

Click the “Get Xcode” button and continue installation on the App Store.

Step 1: Install Visual Studio Code

Download Visual Studio Code and install it to your system Applications folder. Open the app and follow the instructions for launching from the command line. You’ll need to have the code command accessible from $PATH in order to install the SourceKit-LSP extension later on.

Step 2: Install the Latest Swift Toolchain

Go to Swift.org and download the latest trunk development snapshot (at the time of writing, this was from November 16th, 2018). Once it’s finished downloading, run the package to install the Xcode toolchain. To enable it, open Xcode, select the “Xcode > Preferences…” menu item (,), navigate to Components and choose Swift Development Snapshot.

Step 3: Install Node and NPM

VSCode extensions are written in JavaScript / TypeScript. If you’re not already set up for JS development, you can download Node (a JavaScript run-time for outside the browser)
and npm (a package manager for Node) with Homebrew using the following commands or manually by following these instructions:

$ brew install node
        

To verify that you have a working installation, run the following command:

$ npm --version6.4.1
        

Step 4: Build and Install SourceKit-LSP

With all of the dependencies taken care of, we’re now ready for the main attraction. From the command line, clone the sourcekit-lsp repository, navigate to the resulting directory, and build the Swift project.

$ git clone https://github.com/apple/sourcekit-lsp.git
        $cd sourcekit-lsp
        $ swift build
        

If successful, the completed binary will be available from of the hidden .build/debug directory. Move that binary to a standard directory in your $PATH, like /usr/local/bin or /usr/bin.

$mv .build/debug/sourcekit-lsp /usr/local/bin
        

You can verify that everything is working as expected by running the sourcekit-lsp command:

$ sourcekit-lsp
        

This command launches a new language server process, but don’t worry if it doesn’t provide any feedback to STDOUT— that means it’s working as intended. Exit the process with an ETX signal (^C).

Step 5: Build and Install SourceKit-LSP Extension for Visual Studio Code

Now that you have the Swift language server available, the final step is to build and install the extension that allows Visual Studio Code to communicate with it.

From the sourcekit-lsp directory in the previous step, navigate to the Editors/vscode directory, use npm to build the extension and then use the code command to install it:

$cd Editors/vscode/
        $ npm run createDevPackage
        $ code --install-extension out/sourcekit-lsp-vscode-dev.vsix
        

Now launch (or relaunch) VSCode and open a Swift project, such as this one, and enjoy an early preview of the functionality provided by Language Server Protocol support for Swift.


So there you have it — the makings of a first-class Swift development experience outside of Xcode. For now, Swift support for Language Server Protocol is limited to code completion, quick help, diagnostics, jumping to symbol definitions, and finding references. But we couldn’t be more excited for the future of this project and what it means for the prospects of the Swift language outside the Apple ecosystem.


simctl

$
0
0

At the heart of Apple’s creation myth is the story of Steve Jobs’ visit to Xerox PARC in 1979. So inspired by the prototype for the Alto computer was he, that — like Prometheus— Steve Jobs would steal fire from the gods and bring it to the masses by way of the Lisa and Macintosh computers a few years later.

Like so many creation myths, this one is based on dubious historical authenticity. But this is the story we like to tell ourselves because the mouse and graphical user interfaces were indeed revolutionary. With a mouse, anyone can use a computer, even if they aren’t a “computer person”.

…though, if you are a “computer person”, you probably use keyboard shortcuts for your most frequent operations, like saving with S instead of File > Save. More adventurous folks may even venture into the Utilities folder and bravely copy-paste commands into Terminal for one-off tasks like bulk renaming. This is how many programmers get their start; once you see the power of automation, it’s hard not to extend that to do more and more.

Which brings us to today’s subject: If you make apps, you spend a good chunk of your time in Xcode with Simulator running in the background. After exploring the basic functionality of Simulator, you might feel compelled to automate the more time-consuming tasks. The simctl command-line tool provides you the interface you need to use and configure Simulator in a programmatic way.


If you have Xcode installed, simctl is accessible through the xcrun command (which routes binary executables to the active copy of Xcode on your system). You can get a description of the utility and a complete list of subcommands by running simctl without any arguments:

$ xcrun simctl
        

Many of these subcommands are self-explanatory and offer a command-line interface to functionality accessible through the Simulator app itself. However, the best way to understand how they all work together is to start with the list subcommand.

Managing Simulator Devices

Run the list subcommand to get a list of the available runtimes, device types, devices, and device pairs:

$ xcrun simctl list
        -- iOS 12.1 --
        iPhone 5s (CC96B643-067E-41D5-B497-1AFD7B3D0A13) (Shutdown)
        iPhone 6 (7A27C0B9-411A-4BCD-8A67-68F00DF39C1A) (Shutdown)
        iPhone 6 Plus (A5918644-8D46-4C67-B21E-68EA25997F91) (Shutdown)
        iPhone 6s (1AB5A4EB-2434-42E4-9D2C-42E479CE8BDC) (Shutdown)
        …
        

Each device has an associated UUID. You pass this to any of the simctl subcommands that take a device parameter.

One such subcommand is boot, which starts up the specified device, making it available for interaction:

$ xcrun simctl boot $UUID

To see the booted simulator in action, open the Simulator app with the open command, like so:

$ open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/
        

You can see which devices are booted by redirecting the output of list to a grep search for the term “Booted”:

$ xcrun simctl list | grep Booted
        iPhone X (9FED67A2-3D0A-4C9C-88AC-28A9CCA44C60) (Booted)
        

To isolate the device identifier, you can redirect to grep again to search for a UUID pattern:

$ xcrun simctl list devices | \grep"(Booted)"         | \grep-E-o-i"([0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{8})"9FED67A2-3D0A-4C9C-88AC-28A9CCA44C60
        

Pass the --json or -j option to list and other subcommands to format output as JSON. Having a structured representation of resources like this is convenient when you’re writing scripts or programs for managing simulators.

$ xcrun simctl list --json
{"devicetypes":[{"name":"iPhone X","bundlePath":"\/Applications\/Xcode-beta.app\/Contents\/Developer\/Platforms\/iPhoneOS.platform\/Developer\/Library\/CoreSimulator\/Profiles\/DeviceTypes\/iPhone X.simdevicetype","identifier":"com.apple.CoreSimulator.SimDeviceType.iPhone-X"},]}

When you’re finished with a device, you can shut down and erase its contents using the shutdown and erase subcommands:

$ xcrun simctl shutdown $UUID$ xcrun simctl erase $UUID

Copying & Pasting Between Desktop and Simulator

It’s easy to feel “trapped” when using Simulator, unable to interact directly with the OS as you would any other app. Recent versions of Simulator have improved this by allowing you to automatically synchronize pasteboards between desktop and simulated device. But sometimes it’s more convenient to do this programmatically, which is when you’ll want to break out the pbcopy and pbpaste subcommands.

Be aware that the semantics of these subcommands might be opposite of what you’d expect:

  • pbpaste takes the pasteboard contents of the Simulator and copies them to the desktop’s main pasteboard
  • pbcopy takes the main pasteboard contents from your desktop and copies them to the pasteboard of the device in the Simulator

For example, if you wanted to copy the contents of a file on my Desktop to the pasteboard of a simulated device, you’d use the pbpaste subcommand like so:

$ pbcopy ~/Desktop/file.txt
        $ xcrun simctl pbpaste booted
        

After running these commands, the contents of file.txt will be used the next time you paste in Simulator.

Opening URLs in Simulator

You can use the openurl subcommand to have Simulator open up the specified URL on a particular device.

This URL may be a webpage, like this one:

$ xcrun simctl openurl booted "https://nshipster.com/simctl"

…or it can be a custom scheme associated with an app, such as maps:// for Apple Maps:

$ xcrun simctl openurl booted maps://?s=Apple+Park
        

Add to Photo Stream

We’ve all been there before: developing a profile photo uploader and becoming all-too-familiar with the handful of photos of flora from San Francisco and Iceland that come pre-loaded on Simulator.

Fortunately, you can mix things up by using the addmedia subcommand to add a photo or movie to the Photos library of the specified simulator:

$ xcrun simctl addmedia booted ~/Desktop/mindblown.gif
        

Alternatively, you can drag and drop files from Finder into the Photos app in Simulator to achieve the same effect.

Capture Video Recording from Simulator

Sure, you can use the global macOS shortcut to capture a screenshot of a simulator (4), but if you intend to use those for your App Store listing, you’re much better off using the io subcommand:

$ xcrun simctl io booted screenshot app-screenshot.png
        

You can also use this subcommand to capture a video as you interact with your app from the simulator (or don’t, in the case of automated UI tests):

$ xcrun simctl io booted recordVideo app-preview.mp4
        

Setting Simulator Locale

One of the most arduous tasks for developers localizing their app is switching back and forth between different locales. This process typically involves manually tapping into the Settings app, navigating to General > Language & Region selecting a region from a long modal list and then waiting for the device to restart. Each time takes about a minute — or maybe longer if you’re switching back from an unfamiliar locale (你懂中文吗?)

But knowing how simulators work in Xcode lets you automate this process in ways that don’t directly involve simctl.

For example, now that you know that each simulator device has a UUID, you can now find the data directory for a device in ~/Library/Developer/CoreSimulator/Devices/. The simulator preferences file data/Library/Preferences/.GlobalPreferences.plist contains a number of global settings including current locale and languages.

You can see the contents of this using the plutil command:

$ plutil -p ~/Library/Developer/CoreSimulator/Devices/$UUID/data/Library/Preferences/.GlobalPreferences.plist
        

Here’s what you might expect, in JSON format:

{"AddingEmojiKeybordHandled":true,"AppleLanguages":["en"],"AppleLocale":"en_US","AppleITunesStoreItemKinds":[],"AppleKeyboards":["en_US@sw=QWERTY;hw=Automatic","emoji@sw=Emoji","en_US@sw=QWERTY;hw=Automatic"],"AppleKeyboardsExpanded":1,"ApplePasscodeKeyboards":["en_US","emoji"],"AKLastIDMSEnvironment":0,"PKKeychainVersionKey":4}

You can use the plutil command again to modify the preferences programmatically. For example, the following shell script changes the current locale and language to Japanese:

PLIST="~/Library/Developer/CoreSimulator/Devices/$UUID/data/Library/Preferences/.GlobalPreferences.plist"LANGUAGE="ja"LOCALE="ja_JP"
        plutil -replace AppleLocale -string$LOCALE$PLIST
        plutil -replace AppleLanguages -json"[ \"$LOCALE\" ]"$PLIST

You can use this same technique to adjust Accessibility settings (com.apple.Accessibility.plist), such as enabling or disabling Voice Over and other assistive technologies.

To get the full scope of all the available settings, run plutil -p on all property lists in the preferences directory:

$ plutil -p ~/Library/Developer/CoreSimulator/Devices/$UUID/data/Library/Preferences/*.plist
        

At least some share of the credit for the popularity and success of iOS as a development platform can be given to the power and convenience provided by Simulator.

To be able to test your app on dozens of different devices and operating systems is something that we might take for granted, but is an order of magnitude better than the experience on other platforms. And the further ability to script and potentially automate these interactions makes for an even better developer experience.

Locale

$
0
0

“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.

importFoundationletunits=["meter","smoot","agate","ångström"]units.sorted{(lhs,rhs)inlhs.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)inlhs.compare(rhs,locale:🇸🇪)==.orderedAscending}// => ["agate", "meter", "smoot", "ångström"]

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[,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.

Users can change their locale settings in the “Language & Text” system preference on macOS and in “General > International” in the iOS Settings app.

Locale Preferences

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…

importFoundationlet🇺🇸=Locale(identifier:"en_US")let🇫🇷=Locale(identifier:"fr_FR")🇺🇸.localizedString(forIdentifier:🇺🇸.identifier)// => "English (United States)"🇫🇷.localizedString(forIdentifier:🇺🇸.identifier)// => "anglais (États-Unis)"

Use this technique whenever you’re displaying locale, like in this screen from the Settings app, which shows language endonyms alongside exonyms:

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:

importFoundationleturl=URL(string:"https://nshipster.com")!varrequest=URLRequest(url:url)letacceptLanguage=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!


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.

Swift Program Distribution with Homebrew

$
0
0

It’s not enough to make software; you also have to make it easy to install.

Apple’s had this figured out for almost a decade. Anyone can go to the App Store and — with a single tap — start using any one of a million apps in just a few moments.

Compare that to the all-too-common scenario when you go to install any other random piece of software:

Download this gzip’d tarball of source code — oh, and all of its dependencies, too. And make sure you have the latest version of Xcode (but if you’re running latest beta, use the development branch instead). You might hit this one bug, but don’t worry: there’s a workaround posted on StackOverflow… wait, where are you going?

Of course, we know there’s a better way.

For iOS and macOS frameworks, we use Carthage or CocoaPods. For Swift packages, we use Swift Package Manager. And when it comes time to distribute a command-line tool built in Swift, we use Homebrew.

Not sure how? Go ahead and pour a glass of your beverage of choice and read on — you’ll learn everything you need to know before you’re due for a refill. 🍺


Homebrew is the de facto system package manager for macOS. It’s the best way to install and manage programs that run on the command-line (and with Homebrew Cask, it’s the best way to install apps, too).

Simply put: If you want your software to reach the largest audience of developers on macOS, write and publish a Homebrew formula for it.

Even if you’re a long-time user of Homebrew, you may find the prospect of contributing to it daunting. But fear not — the process is straightforward and well-documented. For relatively simple projects, newcomers can expect to have a working formula finished within an hour; projects with a complex build process or dependency graph may take a while longer, but Homebrew is flexible enough to handle anything you throw at it.

For our article about the SwiftSyntax library, we wrote a syntax highlighter for Swift code. We’ll be using that project again for this article as an example for how to write and publish a Homebrew formula. (If you’d like to follow along at home, make sure you have Homebrew installed and can run the brew command from the Terminal)

Creating a Makefile

Although we could write out all of our build instructions directly to Homebrew, a better approach would be to delegate that process to proper build automation system.

There are lots of build automation tools out there — notably Bazel and Buck, which have both gained traction within the iOS community recently. For this example, though, we’re going to use Make.

Now, we could dedicate an entire articlebook to Make. But here’s a quick intro:

Make is a declarative build automation tool, meaning that it can infer everything that needs to happen when you ask it to build something. Build instructions are declared in a file named Makefile, which contains one or more rules. Each rule has a target, the target’s dependencies, and the commands to run.

Here’s a simplified version of the Makefile used to build the swift-syntax-highlight command-line executable:

prefix?= /usr/local
        bindir=$(prefix)/bin
        libdir=$(prefix)/lib
        build:
        swift build -c release --disable-sandboxinstall:buildinstall".build/release/swift-syntax-highlight""$(bindir)"install".build/release/libSwiftSyntax.dylib""$(libdir)"
        install_name_tool -change\".build/x86_64-apple-macosx10.10/release/libSwiftSyntax.dylib"\"$(libdir)/libSwiftSyntax.dylib"\"$(bindir)/swift-syntax-highlight"uninstall:rm-rf"$(bindir)/swift-syntax-highlight"rm-rf"$(libdir)/libSwiftSyntax.dylib"clean:rm-rf .build
        .PHONY:build install uninstall clean

This Makefile declares four targets: build, install, uninstall, and clean.

build calls swift build with release configuration and the option to disable App Sandboxing (which otherwise causes problems when installing via Homebrew).

install depends on build— which makes sense because you can’t install something that hasn’t been built yet. The install command copies the executable to /usr/local/bin, but because swift-syntax-highlighter links to libSwiftSyntax.dylib, we need to install that to /usr/local/lib and use the install_name_tool command to modify the executable and update its path to the dynamic library. If your project only links to system dynamic libraries (like @rpath/libswiftCore.dylib) then you won’t have to do any of this.

The uninstall and clean targets are the inverse to install and build, and are particularly useful when you’re writing or debugging your Makefile.

Before proceeding to the next step, you should be able to do the following with your Makefile:

  • Perform a clean install. If you have any build failures, address those first and foremost.
  • Run the program after cleaning. Cleaning the project after installation reveals any linker errors.
  • Uninstall successfully. After running make uninstall, your executable should no longer be accessible from your $PATH.

Writing a Homebrew Formula

A Homebrew formula is a Ruby file that contains instructions for installing a library or executable on your system.

Run the brew create subcommand to generate a formula, passing a URL to your project. Homebrew will automatically fill in a few details, including the formula name and Git repository.

$ brew create https://github.com/NSHipster/SwiftSyntaxHighlighter
        

After filling in the metadata fields and specifying installation and test instructions, here’s what the formula for swift-syntax-highlight looks like:

classSwiftSyntaxHighlight<Formuladesc"Syntax highlighter for Swift code"homepage"https://github.com/NSHipster/SwiftSyntaxHighlighter"url"https://github.com/NSHipster/SwiftSyntaxHighlighter.git",:tag=>"0.1.0",:revision=>"6c3e2dca81965f902694cff83d361986ad86f443"head"https://github.com/NSHipster/SwiftSyntaxHighlighter.git"depends_on:xcode=>["10.0",:build]definstallsystem"make","install","prefix=#{prefix}"endtestdosystem"#{bin}/swift-syntax-highlight""import Foundation\n"endend

Testing a Homebrew Formula Locally

Once you’ve put the finishing touches on your formula, it’s a good idea to give it a quick taste test before you share it with the rest of the world.

You can do that by running brew install and passing the --build-from-source option with a path to your formula. Homebrew will run through the entire process as if it were just fetched from the internet.

$ brew install--build-from-source Formula/swift-syntax-highlight.rb
        ==> Cloning https://github.com/NSHipster/SwiftSyntaxHighlighter.git
        Updating ~/Library/Caches/Homebrew/swift-syntax-highlight--git
        ==> Checking out tag 0.1.0
        HEAD is now at 6c3e2dc
        ==> make install prefix=/usr/local/Cellar/swift-syntax-highlight/0.1.0
        

Assuming that works as expected, go ahead and brew uninstall and get ready to publish.

Publishing a Tap

In Homebrew parlance, a tap is a collection of formulae contained within a Git repository. Creating a tap is as simple as creating a directory, copying your formula from the previous step, and setting up your repo:

$mkdir-p homebrew-formulae/Formula
        $cd homebrew-formulae
        $cp path/to/formula.rb Formula/
        $ git init
        $ git add .$ git commit -m"Initial commit"

Installing a Formula from a Tap

By convention, taps named "homebrew-formulae" and published on GitHub are accessible from the command line at <# organization #>/formulae. You can either add the tap to Homebrew’s search list or install a formula by fully-qualified name:

# Option 1:
        $ brew tap nshipster/formulae
        $ brew install swift-syntax-highlight
        # Option 2:
        $ brew install nshipster/formulae/swift-syntax-highlight
        

With your formula installed, you can now run any of its packaged executables from the command-line:

$ swift-syntax-highlight 'print("Hello, world!")'<pre class="highlight"><code><span class="n">print</span><span class="p">(</span><span class="s2">"Hello, world!"</span><span class="p">)</span></code></pre>
        

Neat!


Now looking at all of this, you might think that this is a lot of work — and you’d be right, to some extent. It’s not nothing.

You might say,

I’m doing this for free, and I don’t owe nothing to nobody. Read the license: This software is provided “as-is”. If you don’t like that, go ahead and fork it yourself.

If you do, that’s fine. You’re completely entitled to feel this way, and we sincerely appreciate your contributions.

But please consider this: if you spend, say, an hour making it easier to install your software, that’s at least that much saved for everyone else who wants to use it. For a popular piece of software, that can literally add up to years of time spent doing something more important.

You’ve already put so much effort into your work, why not share it with someone else who will appreciate it. 🍻

UIActivityViewController

$
0
0

On iOS, UIActivityViewController provides a unified interface for users to share and perform actions on strings, images, URLs, and other items within an app.

You create a UIActivityViewController by passing in the items you want to share and any custom activities you want to support (we’ll show how to do that later on). You then present that view controller as you would any other modal or popover.

letstring="Hello, world!"leturl=URL(string:"https://nshipster.com")!letimage=UIImage(named:"mustache.jpg")letpdf=Bundle.main.url(forResource:"Q4 Projections",withExtension:"pdf")letactivityViewController=UIActivityViewController(activityItems:[string,url,image,pdf],applicationActivities:nil)present(activityViewController,animated:true){// ...}

When you run this code the following is presented on the screen:

UIActivityViewController

By default, UIActivityViewController shows all the activities available for the items provided, but you can exclude certain activity types via the excludedActivityTypes property.

activityViewController.excludedActivityTypes=[.postToFacebook]

Activity types are divided up into “action” and “share” types:

  • Action (UIActivityCategoryAction) activity items take an action on selected content, such as copying text to the pasteboard or printing an image.
  • Share (UIActivityCategoryShare) activity items share the selected content, such as composing a message containing a URL or posting an image to Twitter.

Each activity type supports certain kinds of items. For example, you can post a String, URL, and / or image to Twitter, but you can’t assign a string to be the photo for a contact.

The following tables show the available activity types for each category and their supported items:

UIActivityCategoryAction

StringURLImageFiles
airDrop
addToReadingList
assignToContact
copyToPasteboard
print
saveToCameraRoll

UIActivityCategoryShare

StringURLImageFiles
mail
message
postToFacebook
postToFlickr
postToTencentWeibo
postToTwitter
postToVimeo
postToWeibo

Creating a Custom UIActivity

In addition to the system-provided activities, you can create your own activities.

As an example, let’s create a custom activity that takes an image and applies a mustache to it via a web application.

Jony Ive BeforeJony Ive After
BeforeAfter

Defining a Custom Activity Type

First, define a new activity type constant in an extension to UIActivity.ActivityType, initialized with a reverse-DNS identifier.

extensionUIActivity.ActivityType{staticletmustachify=UIActivity.ActivityType("com.nshipster.mustachify")}

Creating a UIActivity Subclass

Next, create a subclass of UIActivity and override the default implementations of the activityCategory type property and activityType, activityTitle, and activityImage instance properties.

classMustachifyActivity:UIActivity{overrideclassvaractivityCategory:UIActivity.Category{return.action}overridevaractivityType:UIActivity.ActivityType?{return.mustachify}overridevaractivityTitle:String?{returnNSLocalizedString("Mustachify",comment:"activity title")}overridevaractivityImage:UIImage?{returnUIImage(named:"mustachify-icon")}// ...}

Determining Which Items are Actionable

Activities are responsible for determining whether they can act on a given array of items by overriding the canPerform(withActivityItems:) method.

Our custom activity can work if any of the items is an image, which we identify with some fancy pattern matching on a for-in loop:

overridefunccanPerform(withActivityItemsactivityItems:[Any])->Bool{forcaseisUIImageinactivityItems{returntrue}returnfalse}

Preparing for Action

Once an activity has determined that it can work with the specified items, it uses the prepare(withActivityItems:) to get ready to perform the activity.

In the case of our custom activity, we take the PNG representation of the first image in the array of items and stores that in an instance variable:

varsourceImageData:Data?overridefuncprepare(withActivityItemsactivityItems:[Any]){forcaseletimageasUIImageinactivityItems{self.sourceImageData=image.pngData()return}}

Performing the Activity

The perform() method is the most important part of your activity. Because processing can take some time, this is an asynchronous method. However, for lack of a completion handler, you signal that work is done by calling the activityDidFinish(_:) method.

Our custom activity delegates the mustachification process to a web app using a data task sent from the shared URLSession. If all goes well, the mustachioedImage property is set and activityDidFinish(_:) is called with true to indicate that the activity finished successfully. If an error occurred in the request or we can’t create an image from the provided data, we call activityDidFinish(_:) with false to indicate failure.

varmustachioedImage:UIImage?overridefuncperform(){leturl=URL(string:"https://mustachify.app/")!varrequest=URLRequest(url:url)request.httpMethod="POST"request.httpBody=self.sourceImageDataURLSession.shared.dataTask(with:request){(data,_,error)inguarderror!=nilelse{self.activityDidFinish(false)return}ifletdata=data,letimage=UIImage(data:data){self.mustachioedImage=imageself.activityDidFinish(true)}else{self.activityDidFinish(false)}}}

Showing the Results

The final step is to provide a view controller to be presented with the result of our activity.

The QuickLook framework provides a simple, built-in way to display images. We’ll extend our activity to adopt QLPreviewControllerDataSource and return an instance of QLPreviewController, with self set as the dataSource for our override of theactivityViewController method.

importQuickLookextensionMustachifyActivity:QLPreviewControllerDataSource{overridevaractivityViewController:UIViewController?{guardletimage=self.mustachioedImageelse{returnnil}letviewController=QLPreviewController()viewController.dataSource=selfreturnviewController}// MARK: QLPreviewControllerDataSourcefuncnumberOfPreviewItems(incontroller:QLPreviewController)->Int{returnself.mustachioedImage!=nil?1:0}funcpreviewController(_controller:QLPreviewController,previewItemAtindex:Int)->QLPreviewItem{returnself.mustachioedImage!}}

Providing a Custom Activity to Users

We can use our brand new mustache activity by passing it to the applicationActivities parameter in the UIActivityViewController initializer:

letactivityViewController=UIActivityViewController(activityItems:[image],applicationActivities:[Mustachify()])present(activityViewController,animated:true){// ...}


There is a strong argument to be made that the long-term viability of iOS as a platform depends on sharing mechanisms like UIActivityViewController.

As the saying goes, “Information wants to be free.” Anything that stands in the way of federation is doomed to fail.

macOS Character Viewer

$
0
0

Emoji is a conspiracy by the Unicode® Consortium to make Americans care about internationalization.

For a time, many developers operated under the assumption that user-input would be primarily Latin-1-compatible; or if not, then Extended Latin, certainly. Or at least they could feel reasonably assured that everything would fall within the Basic Multilingual Plane — fitting comfortably into a single UTF-16 code unit.

But nowadays, with Emoji emerging as the lingua franca of these troubled times, text is presumed international until proven otherwise. Everyone should be ready for when the U+1F4A9 hits the fan.

So whatever you think about those colorful harbingers of societal collapse, at least they managed to break us of our ASCII-only information diets.

This week on NSHipster, we’ll be looking at a relatively obscure part of macOS that will prove essential for developers in today’s linguistic landscape: Character Viewer


From any macOS app, you can select the Edit menu and find an item at the very bottom called “Emoji & Symbols” (tellingly renamed from “Special Characters” in OS X Mavericks).

By default, this opens a panel that looks something like this:

You may have discovered this on your own and found it to be a convenient alternative to searching for Emoji online.

But this isn’t even its final form! Click the icon on the top right to see its true power:

Go ahead and memorize the global shortcut if you haven’t already: Space. If you do any serious work with text, you’ll be using Character Viewer frequently.

Let’s take a quick tour of Character Viewer and see what it can do for us:

A Quick Tour of Character Viewer

The sidebar on the left provides quick access to your favorite and frequently-used characters, as well as a customizable list of named categories like Emoji, Latin, Punctuation, and Bullets/Stars.

The center column displays a grid of characters. Some categories, including Emoji, provide special views that make it easier to browse through the characters in that collection.

Selecting a character populates the inspector pane on the right with a larger, isolated rendering of the character, the character name, code point, and UTF-8 encoding. The inspector may also show alternate glyph renderings provided by other fonts as well as related characters, as applicable.

Copying Character Information

You can control-click a character and choose “Copy Character Info” from the shortcut menu to copy the information found in the inspector. For example:

😂
face with tears of joy
Unicode: U+1F602, UTF-8: F0 9F 98 82

Let’s take a look at what all of this means and how to use it in Swift code:

Character Literal

The first line of the copied character information is the character itself.

Swift source code fully supports Unicode, so you can copy-paste any character into a string literal and have it work as expected:

"😂"// 😂

All characters found in Character Viewer are valid string and character literals. However, not all entries are valid Unicode scalar literals. For example, the character 👩🏻‍🦰 is a named sequence consisting of four individual code points:

  • 👩‍ WOMAN (U+1F469)
  • 🏻 EMOJI MODIFIER FITZPATRICK TYPE-1-2 (U+1F3FB)
  • ␣ ZERO WIDTH JOINER (U+200D)
  • 🦰 EMOJI COMPONENT RED HAIR (U+1F9B0)

Attempting to initialize a Unicode.Scalar value from a string literal with this character results in an error.

("👩🏻‍🦰"asUnicode.Scalar)// error

Unicode Code Point

Each Unicode character is assigned a unique name and number, known as a code point. By convention, Unicode code points are formatted as 4 – 6 hexadecimal digits (0–9, A–F) with the prefix “U+”.

In Swift, string literals have the \u{n} escape sequence which takes a 1 – 6 hexadecimal number corresponding to a Unicode scalar value (essentially, the numerical value of any code point that isn’t a surrogate).

The character 😂 has a scalar value equal to 1F602₁₆ (128514 in decimal). You can plug that number into a \u{} escape sequence in a string literal to have it replaced by the character in the resulting string.

"\u{1F602}"// "😂"

Unicode scalar value escape sequences are especially useful when working with nonprinting control characters like directional formatting characters.

UTF-8 Code Units

The pairs of hexadecimal digits labeled “UTF8” correspond to the code points for the UTF-8 encoded form of the character.

The UTF-8 code unit is a byte (8 bits), which is represented by two hexadecimal digits.

In Swift, you can use the String(decoding:as:) initializer to create a string from an array of UInt8 values corresponding to the values copied from Character Viewer.

String(decoding:[0xF0,0x9F,0x98,0x82],as:UTF8.self)// 😂

Unicode Character Name

The last piece of information provided by Character Viewer is the name of the character “face with tears of joy”.

The Swift standard library doesn’t currently provide a way to initialize Unicode scalar values or named sequences. However, you can use the String method applyingTransform(_:reverse:) provided by the Foundation framework to get a character by name:

importFoundation"\\N{FACE WITH TEARS OF JOY}".applyingTransform(.toUnicodeName,reverse:true)// "😂"

Perhaps more usefully, you can apply the .toUnicodeName transform in the non-reverse direction to get the Unicode names for each character in a string:

"🥳✨".applyingTransform(.toUnicodeName,reverse:false)// \\N{FACE WITH PARTY HORN AND PARTY HAT}\\N{SPARKLES}

Things to Do with Character Viewer

Now that you’re more familiar with Character Viewer, here are some ideas for what to do with it:

Add Keyboard Shortcut Characters to Favorites

All developers should take responsibility for writing documentation about the software they work on and the processes they use in their organization.

When providing instructions for using a Mac app, it’s often helpful to include the keyboard shortcuts corresponding to menu items. The symbols for modifier keys like Shift () are difficult to type, so it’s often more convenient to pick them from Character Viewer. You can make it even easier for yourself by adding them to your Favorites list.

Click on the Action button at the top left corner of the Character Viewer panel and select the Customize List… menu item. In the displayed sheet, scroll to the bottom of the categories listed under Symbols and check the box next to Technical Symbols.

ControlUP ARROWHEAD (U+2303)
Alt / OptionOPTION KEY (U+2325)
ShiftUPWARDS WHITE ARROW (U+21E7)
CommandPLACE OF INTEREST SIGN (U+2318)

Dismiss the sheet and select Technical Symbols in the sidebar, and you’ll notice some familiar keyboard shortcut characters. Add them to your Favorites list by selecting each individually and clicking the Add to Favorites button in the inspector.

Demystify Unknown Characters

Ever see a character and wonder what it was? Simply copy-paste it into the search field of Character Viewer to get its name and number.

For example, have you ever wondered about the  character you get by typing K? Like, how did Apple get its logo into the Unicode Standard when that goes against their criteria for encoding symbols?

By copy-pasting into the Character Viewer, you can learn that, in fact, the Apple logo isn’t an encoded Unicode character. Rather, it’s a glyph associated with the code point U+F8FF from the Private-Use Area block.

The next time you have a question about what’s in your pasteboard, consider asking Character Viewer instead of Safari.

Divest Your Cryptocurrency

Given the current economic outlook for Bitcoin (₿) and other cryptocurrencies, you may be looking to divest your holdings in favor of something more stable and valuable (sorry, too soon?).

Look no further than the Currency Symbols category for some exciting investment opportunities, including French francs (₣) and Italian lira (₤).

Explore the Unicode Code Table

At the bottom of the Customize List sheet, you’ll find a section titled Code Tables.

Go ahead and check the box next to Unicode.

This is arguably the best interface available to you for browsing the Unicode Standard. No web page comes close to matching the speed and convenience of what’s available here in the macOS Character Viewer.

The top panel shows a sortable table of Unicode blocks, with their code point offset, name, and category. Clicking on any of these entries navigates to the corresponding offset in the bottom panel, where characters are displayed in a 16-column grid.

Brilliant.


Character Viewer is an indispensable tool for working with text on computers — a hidden gem in macOS if ever there was one.

But even more than that, Character Viewer offers a look into our collective linguistic and cultural heritage as encoded into the Unicode Standard. Etchings made thousands of years ago by Phoenician merchants and Qin dynasty bureaucrats and Ancient Egyptian priests and Lycian school children — they’re all preserved here digitally, just waiting to be discovered.

Seriously, how amazing is that?

So if ever you grow weary of the awfulness of software… take a scroll through the multitude of scripts and symbols in the Unicode code table, and take solace that we managed to get a few things right along the way.

Viewing all 382 articles
Browse latest View live